ultimatepp/uppsrc/TCtrlLib/OldTreeCtrl.cpp
cxl 597466fea8 uppsrc: NAMESPACE_UPP / END_UPP_NAMESPACE removed
git-svn-id: svn://ultimatepp.org/upp/trunk@10186 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2016-08-26 17:15:30 +00:00

1469 lines
31 KiB
C++

//////////////////////////////////////////////////////////////////////
// OldTreeCtrl: tree control.
#include "TCtrlLib.h"
#pragma hdrstop
#include "OldTreeCtrl.h"
namespace Upp {
#define IMAGECLASS OldTreeCtrlImg
#define IMAGEFILE <TCtrlLib/OldTreeCtrl.iml>
#include <Draw/iml_header.h>
enum { SCREEN_ZOOM = 140 };
#ifdef _DEBUG
//#define DBG_PAINT(co, dbgco) dbgco // debug version
#define DBG_PAINT(co, dbgco) co // release version
#else
#define DBG_PAINT(co, dbgco) co // release version
#endif
#define IMAGECLASS OldTreeCtrlImg
#define IMAGEFILE <TCtrlLib/OldTreeCtrl.iml>
#include <Draw/iml_source.h>
enum
{
ICONGAP = 6,
TEXTGAP = 5,
TSGAP = 3,
BUTTON_SIZE = 9,
BUTTON_HALF = BUTTON_SIZE >> 1,
};
//////////////////////////////////////////////////////////////////////
// OldTreeCursor::
OldTreeCursor::OldTreeCursor()
: root(0)
{
}
OldTreeCursor::OldTreeCursor(const OldTreeItem& item)
: root(0)
{
Find(item);
}
OldTreeCursor::OldTreeCursor(const OldTreeCursor& another, int)
: root(another.root)
, cursor(another.cursor, 0)
{
}
OldTreeCursor& OldTreeCursor::Adjust()
{
if(cursor.IsEmpty() || !root)
return *this;
const OldTreeItem *ptr = root;
int *b = cursor.Begin(), *e = cursor.End(), *p;
for(p = b; p < e; p++)
if(ptr->IsEmpty())
break;
else {
*p = minmax(*p, 0, ptr->GetCount() - 1);
ptr = &ptr->children[*p];
}
cursor.SetCount(p - b);
return *this;
}
OldTreeCursor& OldTreeCursor::WeakAdjust()
{
if(cursor.IsEmpty() || !root)
return *this;
const OldTreeItem *ptr = root;
int *b = cursor.Begin(), *e = cursor.End(), *p;
for(p = b; p < e; p++) {
*p = minmax(*p, 0, ptr->GetCount());
if(*p == ptr->GetCount()) {
p++;
break;
}
ptr = &ptr->children[*p];
}
cursor.SetCount(p - b);
return *this;
}
OldTreeCursor& OldTreeCursor::AddAfter(const OldTreeCursor& c, int count)
{
int top = c.cursor.GetCount() - 1;
if(!root || root != c.root || top < 0 || cursor.GetCount() <= top
|| memcmp(cursor.Begin(), c.cursor.Begin(), sizeof(int) * top))
return *this;
int d = cursor[top] - c.cursor[top];
if(d >= 0)
cursor[top] += max(count, -d);
return *this;
}
OldTreeCursor& OldTreeCursor::AdjustAfter(const OldTreeCursor& c, int count)
{
return AddAfter(c, count).Adjust();
}
OldTreeCursor& OldTreeCursor::WeakAdjustAfter(const OldTreeCursor& c, int count)
{
return AddAfter(c, count).WeakAdjust();
}
OldTreeItem *OldTreeCursor::Get() const
{
OldTreeItem *ptr = root;
for(const int *p = cursor.Begin(), *e = cursor.End(); ptr && p < e; p++)
ptr = (*p >= 0 && *p < ptr->children.GetCount() ? &ptr->children[*p] : NULL);
return ptr;
}
OldTreeItem *OldTreeCursor::GetParent() const
{
if(!root || cursor.IsEmpty())
return NULL;
OldTreeItem *ptr = root;
for(const int *p = cursor.Begin(), *e = cursor.End() - 1; ptr && p < e; p++)
ptr = (*p >= 0 && *p < ptr->children.GetCount() ? &ptr->children[*p] : NULL);
return ptr;
}
const OldTreeCursor& OldTreeCursor::Open() const
{
if(!root)
return *this;
OldTreeItem *i = root;
for(const int *p = cursor.Begin(), *e = cursor.End(); p < e; p++) {
if(!i->IsOpenItem())
i->Open().TouchLayout();
if(*p < 0 || *p >= i->GetCount())
break;
i = &(*i)[*p];
}
return *this;
}
OldTreeCursor& OldTreeCursor::BeginAll()
{
cursor.Clear();
return *this;
}
OldTreeCursor& OldTreeCursor::EndAll()
{
cursor.Clear();
ASSERT(root);
cursor.Add(root->GetCount());
return *this;
}
bool OldTreeCursor::IsBeginAll() const
{
return cursor.IsEmpty();
}
bool OldTreeCursor::IsEndAll() const
{
ASSERT(root);
return !cursor.IsEmpty() && cursor[0] >= root->GetCount();
}
bool OldTreeCursor::IsBegin() const
{
return cursor.IsEmpty() || cursor.Top() == 0;
}
const OldTreeCursor& OldTreeCursor::Select(bool select, bool subtree) const
{
OldTreeItem *ptr = Get();
if(ptr)
if(subtree)
ptr->SelectTree(select);
else
ptr->Select(select);
return *this;
}
OldTreeCursor& OldTreeCursor::Find(const OldTreeItem& item)
{
cursor.Clear();
if(root = item.owner) {
for(OldTreeItem *i = const_cast<OldTreeItem *>(&item); i->parent; i = i->parent) {
int pos = i->parent->children.GetIndex(*i);
ASSERT(pos >= 0);
cursor.Insert(0, pos);
}
}
return *this;
}
OldTreeItem *OldTreeCursor::Find(int clss, int flags)
{
for(OldTreeItem *i; i = Get(); Down(flags))
if(i->GetClass() == clss)
return i;
return NULL;
}
bool OldTreeCursor::Up(int flags)
{
OldTreeItem *parent = GetParent();
if(!parent)
return false;
int pos = min(cursor.Top(), parent->GetCount());
if(pos <= 0) { // drop out of child into parent
if(flags & LEFT)
cursor.Drop();
return true;
}
cursor.Top() = (pos -= 1);
if(flags & (RIGHT | RIGHT_CLOSED))
for(OldTreeItem *temp; temp = &parent->children[pos],
!temp->IsEmpty() && (temp->IsOpenItem() || flags & RIGHT_CLOSED);
cursor.Add(pos = temp->GetCount() - 1), parent = temp)
;
return true;
}
bool OldTreeCursor::Down(int flags)
{
if(cursor.IsEmpty()) { // step into 1st item
if(flags & (RIGHT | RIGHT_CLOSED) && root && !root->IsEmpty()
&& (root->IsOpenItem() || flags & RIGHT_CLOSED)) {
cursor.Add(0);
return true;
}
return false;
}
OldTreeItem *parent = GetParent();
if(!parent)
return false;
int pos = cursor.Top();
if(pos < parent->GetCount() && flags & (RIGHT | RIGHT_CLOSED)) {
OldTreeItem *ptr = &parent->children[pos];
if(!ptr->IsEmpty() && (ptr->IsOpenItem() || flags & RIGHT_CLOSED)) { // descend into child
cursor.Add(0);
return true;
}
}
do {
if(++cursor.Top() < parent->GetCount()) // move to next item in current row
return true;
if(!(flags & LEFT) || cursor.GetCount() <= 1) { // not moving out or global last item encountered - move to end item
cursor.Top() = parent->GetCount();
return true;
}
cursor.Drop();
}
while(parent = parent->parent);
return true;
}
OldTreeCursor& OldTreeCursor::Right()
{
if(!IsEnd())
cursor.Add(0);
return *this;
}
OldTreeCursor& OldTreeCursor::Left()
{
if(!cursor.IsEmpty())
cursor.Drop();
return *this;
}
OldTreeCursor& OldTreeCursor::Begin()
{
cursor.Top() = 0;
return *this;
}
OldTreeCursor& OldTreeCursor::End()
{
OldTreeItem *parent = GetParent();
if(parent)
cursor.Top() = parent->GetCount();
return *this;
}
int TreeCursor_Compare(const OldTreeCursor& a, const OldTreeCursor& b)
{
return OldTreeCursor::Compare(a, b);
}
int OldTreeCursor::Compare(const OldTreeCursor& a, const OldTreeCursor& b)
{
if(a.root != b.root)
return cmp(a.root, b.root);
const int *pa = a.cursor.Begin(), *ea = pa + min(a.cursor.GetCount(), b.cursor.GetCount());
const int *pb = b.cursor.Begin();
for(; pa < ea; pa++, pb++)
if(*pa != *pb)
return cmp(*pa, *pb);
return cmp(a.cursor.GetCount(), b.cursor.GetCount());
}
String OldTreeCursor::ToString() const
{
String out;
out << '[';
for(int i = 0; i < cursor.GetCount(); i++) {
if(i)
out << ", ";
out << cursor[i];
}
out << ']';
return out;
}
bool OldTreeCursor::IsSubtreeOf(const OldTreeCursor& top) const
{
return top.root == root
&& top.cursor.GetCount() <= cursor.GetCount()
&& !memcmp(top.cursor.Begin(), cursor.Begin(), top.cursor.GetCount() * sizeof(int));
}
//////////////////////////////////////////////////////////////////////
// SimpleTreeItem::
SimpleTreeItem::SimpleTreeItem()
{
Zero(*this);
indent = 0;
type = TEXT;
layout = false;
enabled = true;
branch = true;
leaf = true;
auto_size = true;
auto_indent = true;
}
//////////////////////////////////////////////////////////////////////
// OldTreeItem::
OldTreeItem::OldTreeItem()
: text(String::GetVoid())
{
}
OldTreeItem::OldTreeItem(const OldTreeItem& item, int)
: SimpleTreeItem(item)
, text(item.text)
, value(item.value)
, subtree(item.subtree)
, children(item.children, 0)
{
}
OldTreeItem& OldTreeItem::Open(bool _open)
{
if((open = _open) && !up_to_date && subtree) { // expand subtree as necessary
subtree(*this);
if(is_static)
up_to_date = true;
TouchLayout();
}
return *this;
}
void OldTreeItem::SetCursor()
{
if(this) {
ASSERT(owner);
owner->SetCursor(this);
}
}
bool OldTreeItem::IsSubtree() const
{
return !IsEmpty() || (subtree && !up_to_date);
}
bool OldTreeItem::IsOpenFull() const
{
return IsOpenItem() && !IsEmpty();
}
String OldTreeItem::GetTextOrValue() const
{
if(!text.IsVoid())
return text;
return StdFormat(value);
}
OldTreeItem& OldTreeItem::Select(bool _sel)
{
if(this && selected != _sel) {
selected = _sel;
Touch();
}
return *this;
}
OldTreeItem& OldTreeItem::SelectTree(bool _sel)
{
if(this) {
Select(_sel);
for(int i = 0; i < GetCount(); i++)
children[i].SelectTree(_sel);
}
return *this;
}
int OldTreeItem::GetCount(int clss) const
{
int count = (item_class == clss ? 1 : 0);
for(int i = 0; i < children.GetCount(); i++)
count += children[i].GetCount(clss);
return count;
}
int OldTreeItem::GetCountDeep() const
{
if(this == 0)
return 0;
int count = 1;
for(int i = 0; i < children.GetCount(); i++)
count += children[i].GetCountDeep();
return count;
}
const OldTreeItem *OldTreeItem::FindValue(Value _value) const
{
if(value == _value)
return this;
for(int i = 0; i < children.GetCount(); i++) {
const OldTreeItem *p = children[i].FindValue(_value);
if(p)
return p;
}
return 0;
}
OldTreeItem *OldTreeItem::FindValue(Value _value)
{
if(value == _value)
return this;
for(int i = 0; i < children.GetCount(); i++) {
OldTreeItem *p = children[i].FindValue(_value);
if(p)
return p;
}
return 0;
}
void OldTreeItem::Touch()
{
if(this && !owner->updating && !owner->IsFullRefresh())
owner->Refresh(GetClientRect());
}
void OldTreeItem::TouchLayout()
{
if(this && !layout) {
OldTreeItem *p = this;
while(p && !p->layout) {
p->layout = true;
p = p->parent;
}
if(!p && owner)
owner->Refresh();
}
}
Rect OldTreeItem::GetRect(int hittest) const
{
Rect sum = Null;
if(item_size.cy <= 0)
return sum;
if((hittest & HBUTTON) && IsLeaf() && IsSubtree())
sum = RectC(-BUTTON_HALF, (item_size.cy >> 1) - BUTTON_HALF, BUTTON_SIZE, BUTTON_SIZE);
Size iconsize(0, 0);
if(type == TEXT && image)
iconsize = image.GetSize();
int left = 0;
if(leaf)
left += BUTTON_HALF + 1 + ICONGAP;
if(type == TEXT && image) {
if(hittest & HICON)
sum |= RectC(left, 0, iconsize.cx, item_size.cy);
left += iconsize.cx + TEXTGAP;
}
if(hittest & (HTEXT | HPICT | HCTRL))
sum |= Rect(left, 0, item_size.cx ? item_size.cx : tree_size.cx, item_size.cy);
return sum;
}
OldTreeItem& OldTreeItem::Clear()
{
children.Clear();
TouchLayout();
return *this;
}
OldTreeItem& OldTreeItem::SetCount(int n)
{
if(children.GetCount() == n)
return *this;
while(children.GetCount() < n)
Add();
children.SetCount(n);
TouchLayout();
return *this;
}
OldTreeItem *OldTreeItem::Detach(int pos)
{
children[pos].TouchLayout();
return children.Detach(pos);
}
OldTreeItem& OldTreeItem::Add()
{
return Insert(GetCount(), new OldTreeItem);
}
OldTreeItem& OldTreeItem::Insert(int pos)
{
return Insert(pos, new OldTreeItem);
}
OldTreeItem& OldTreeItem::Add(OldTreeItem *item)
{
return Insert(GetCount(), item);
}
static void SetOwner(OldTreeItem& item)
{
for(int i = 0; i < item.GetCount(); i++)
SetOwner(item[i].SetOwner(item.GetOwner()));
}
OldTreeItem& OldTreeItem::Insert(int pos, OldTreeItem *item)
{
ASSERT(owner && item);
children.Insert(pos, item);
item->parent = this;
item->owner = owner;
UPP::SetOwner(*item);
item->TouchLayout();
return *item;
}
void OldTreeItem::Remove(int pos, int count)
{
ASSERT(owner);
ASSERT(pos >= 0 && pos < GetCount());
if(pos >= 0 && pos < GetCount()) {
children.Remove(pos, count);
TouchLayout();
}
}
void OldTreeItem::LayoutItem()
{
ASSERT(owner);
if(!layout)
return;
Size iconsize = image.GetSize();
int leafgap = (leaf ? BUTTON_HALF + 1 + ICONGAP : 0);
if(auto_indent)
indent = leafgap + (iconsize.cx >> 1);
if(auto_size) {
Size textsize(0, 0);
item_size = Size(0, 0);
switch(type) {
case TEXT: {
String display = GetTextOrValue();
if(!IsNull(display)) {
/* if(*display == '\xFF') {
Document doc(display.Begin() + 1);
Size size;
textsize.cx = doc.GetWidth(DOC_SCREEN_ZOOM, ScreenInfo());
textsize.cy = doc.GetHeight(DOC_SCREEN_ZOOM, ScreenInfo(), textsize.cx);
}
else*/
textsize = GetSmartTextSize(display, owner->font);
}
}
break;
case PICT:
textsize = picture->GetStdSize(value);
break;
case CTRL:
textsize = ctrl->GetRect().Size();
break;
}
item_size.cx = iconsize.cx + textsize.cx + leafgap + TSGAP;
item_size.cy = max(iconsize.cy, textsize.cy);
if(iconsize.cx && textsize.cx)
item_size.cx += TEXTGAP;
}
tree_size = item_size;
/* if(subtree && open) { // expand subtree as necessary
if(!up_to_date) {
subtree(*this);
if(is_static)
up_to_date = true;
}
}
*/
if(open) { // include children in raw size
for(int i = 0; i < children.GetCount(); i++) {
OldTreeItem& child = children[i];
child.LayoutItem();
tree_size.cx = max(tree_size.cx, child.tree_size.cx + indent);
tree_size.cy += child.tree_size.cy;
}
}
layout = false;
}
void OldTreeItem::PaintItem(Draw& draw, Point pos) const
{
Rect clip(0, 0, 9999, 9999);
Rect rc = Rect(pos.x - BUTTON_HALF, pos.y, clip.right, pos.y + tree_size.cy) & clip;
if(rc.IsEmpty())
return;
int daddy = pos.x;
Color bg = (owner->IsShowEnabled() ? SWhite : SLtGray);
{ // clear branch area
int lb = pos.x + (leaf ? BUTTON_HALF + 1 : 0);
int rb = pos.x + indent + BUTTON_HALF + 1;
draw.DrawRect(lb, pos.y, clip.right - lb, item_size.cy, DBG_PAINT(bg, LtRed));
draw.DrawRect(lb, pos.y, rb - lb, tree_size.cy, DBG_PAINT(bg, LtMagenta));
/*
int left = parent ? prev + 1 : clip.left;
int right = pos.x + branch_indent + 1;
if(right - left)
draw.DrawRect(left, rc.top, right - left, rc.Height(), bg);
*/
}
if(IsBranch() && IsOpenFull()) { // generate branch
int top = pos.y + item_size.cy;
int bot = Null, bbot = Null;
int tmp = top;
for(int i = 0; i < children.GetCount(); i++) {
const OldTreeItem& child = children[i];
if(child.item_size.cy > 0 && child.leaf) {
bot = tmp + (child.item_size.cy >> 1) + 1;
bbot = tmp + child.item_size.cy;
}
tmp += child.tree_size.cy;
}
if(!IsNull(bot) && item_size.cy > 0) { // vertical joint
int x = pos.x + indent;
DrawRect(draw, Rect(x, top, x + 1, bot), OldTreeCtrlImg::dither());
}
}
if(item_size.cy > 0) {
Rect item(pos, GetItemSize());
// if(prev + 1 < clip.right)
// draw.DrawRect(prev + 1, item.top, clip.right - prev - 1, item.Height(), bg);
if(item.Width() == 0)
item.right = item.left + tree_size.cx;
if(item.bottom > rc.top && item.top < rc.bottom) {
if(leaf) { // draw leaf
int mid = (item.top + item.bottom) >> 1;
DrawRect(draw, pos.x, mid, BUTTON_SIZE + ICONGAP, 1, OldTreeCtrlImg::dither());
if(IsSubtree()) { // draw expand/collapse button
draw.DrawImage(pos.x - BUTTON_HALF, mid - BUTTON_HALF, IsOpenItem() ? OldTreeCtrlImg::minus() : OldTreeCtrlImg::plus());
}
item.left += BUTTON_HALF + 1 + ICONGAP;
}
Color ink = SBlack;
Color bgnd = bg;
if(IsSelected())
if(owner->HasFocus()) {
bgnd = SBlue;
ink = SWhite;
}
else {
bgnd = SLtGray;
ink = SBlack;
}
if(image) {
Size iconsize = image.GetSize();
Point iconpos(item.left, (item.top + item.bottom - iconsize.cy) >> 1);
#ifdef NEWIMAGE
draw.DrawImage(iconpos.x, iconpos.y, enabled ? image : MakeImage(image, Etched));
#else
draw.DrawImage(iconpos.x, iconpos.y, image, enabled ? 0 : image.ETCHED);
#endif
item.left += iconsize.cx + TEXTGAP;
}
switch(type) {
case TEXT: {
String display = GetTextOrValue();
WString wdisplay;
if(!IsNull(display)) {
Size size;
if(*display == '\xFF') {
Document doc;
doc.Qtf(display.Begin() + 1);
size.cx = doc.GetWidth(SCREEN_ZOOM);
size.cy = doc.GetHeight(SCREEN_ZOOM, size.cx);
}
else
size = GetTLTextSize(wdisplay = display.ToWString(), owner->font);
Rect trc = item;
trc.left -= TSGAP;
// trc.top = (item.top + item.bottom - size.cy) >> 1;
// trc.bottom = trc.top + size.cy;
draw.DrawRect(trc, bgnd);
trc.left += TSGAP;
trc.top = (item.top + item.bottom - size.cy) >> 1;
if(*display == '\xFF') {
Document doc;
String s;
s << "[@(" << ink.GetR() << "." << ink.GetG() << "." << ink.GetB() << ") "
<< display.Begin() + 1;
doc.Qtf(s);
doc.Paint(SCREEN_ZOOM, draw, trc.left, trc.top, trc.Width());
}
else
DrawTLText(draw, trc.left, trc.top, trc.Width(), wdisplay, owner->font, ink);
}
}
break;
case PICT:
picture->Paint(draw, item, value, ink, bgnd, 0);
break;
case CTRL:
if(item != ctrl->GetRect())
ctrl->SetRect(item);
break;
}
}
pos.y += item_size.cy;
}
if(IsOpenFull()) {
pos.x += indent;
int i = 0;
for(; i < children.GetCount() && pos.y + children[i].tree_size.cy <= rc.top; i++)
pos.y += children[i].tree_size.cy;
for(; i < children.GetCount() && pos.y < rc.bottom; i++) {
children[i].PaintItem(draw, pos);
pos.y += children[i].tree_size.cy;
}
}
}
OldTreeItem::HITTEST OldTreeItem::FindItem(Point pt, OldTreeItem *& item) const
{
if(pt.y < 0 || pt.y >= tree_size.cy) {
item = NULL;
return HNIL;
}
if(item_size.cy > 0 && pt.y < item_size.cy) { // check this item
item = (OldTreeItem*)this;
if(GetRect(HBUTTON).Contains(pt))
return HBUTTON;
switch(type) {
case TEXT:
if(GetRect(HICON).Contains(pt))
return HICON;
if(GetRect(HTEXT).Contains(pt))
return HTEXT;
break;
case PICT:
if(GetRect(HPICT).Contains(pt))
return HPICT;
break;
case CTRL:
if(GetRect(HCTRL).Contains(pt))
return HCTRL;
break;
}
return HOTHER;
}
if(IsOpenFull() && (pt.y -= item_size.cy) >= 0) {
pt.x -= indent;
for(int i = 0; i < children.GetCount() && pt.y >= 0; pt.y -= children[i++].tree_size.cy) {
HITTEST result;
if((result = children[i].FindItem(pt, item)) != HNIL)
return result;
}
}
item = NULL;
return HNIL;
}
int OldTreeItem::GetOpenCount() const
{
int count = 0;
if(item_size.cy > 0)
count++;
if(IsOpenFull())
for(List::ConstIterator p = children.Begin(), e = children.End(); p < e; p++)
count += p->GetOpenCount();
return count;
}
Rect OldTreeItem::GetItemRect(int hittest) const
{
Rect rc = GetRect(hittest);
for(const OldTreeItem *c = this, *p; p = c->parent; c = p) {
int i;
if(!p->IsOpenFull() || (i = p->children.GetIndex(*c)) < 0)
return Null; // error
int y = p->item_size.cy;
for(int t = 0; t < i; t++)
y += p->children[t].tree_size.cy;
rc.Offset(p->indent, y);
}
return rc;
}
Rect OldTreeItem::GetClientRect(int hittest) const
{
return GetItemRect(hittest) + (owner->offset - owner->old_scroll);
}
Rect OldTreeItem::GetTreeRect() const
{
Rect rc(0, 0, tree_size.cx, tree_size.cy);
for(const OldTreeItem *c = this, *p; p = c->parent; c = p) {
int i;
if(!p->IsOpenFull() || (i = p->children.GetIndex(*c)) < 0)
return Null; // error
int y = p->item_size.cy;
for(int t = 0; t < i; t++)
y += p->children[t].tree_size.cy;
rc.Offset(p->indent, y);
}
return rc;
}
Rect OldTreeItem::GetClientTreeRect() const
{
return GetTreeRect() + (owner->offset - owner->old_scroll);
}
OldTreeItem& OldTreeItem::Set(Proc proc)
{
subtree.Clear();
proc(*this);
return *this;
}
Bar::Item& OldTreeItem::BarOpenClose(Bar& bar)
{
return bar.Add(enabled && IsSubtree(),
IsOpenItem() ? t_("Close") : t_("Open"), callback(this, &OldTreeItem::OnOpenClose))
.Help(IsOpenItem() ? t_("Close child items") : t_("Open child items"));
}
Bar::Item& OldTreeItem::BarOpenSubtree(Bar& bar)
{
return bar.Add(enabled && IsSubtree(),
t_("Open all"), callback(this, &OldTreeItem::OnOpenSubtree))
.Help(t_("Open the whole subtree"));
}
Bar::Item& OldTreeItem::BarCloseSubtree(Bar& bar)
{
return bar.Add(enabled && IsSubtree(),
t_("Close all"), callback(this, &OldTreeItem::OnCloseSubtree))
.Help(t_("Close the whole subtree"));
}
void OldTreeItem::OnOpenClose()
{
if(enabled && IsSubtree()) {
Open(!IsOpenItem());
TouchLayout();
LayoutItem();
ScrollInto();
}
}
void OldTreeItem::OnOpenSubtreeRaw()
{
if(enabled) {
Open();
TouchLayout();
for(int i = 0; i < GetCount(); i++)
children[i].OnOpenSubtree();
}
}
void OldTreeItem::OnOpenSubtree()
{
OnOpenSubtreeRaw();
ScrollInto();
}
void OldTreeItem::OnCloseSubtree()
{
for(int i = 0; i < GetCount(); i++)
children[i].OnCloseSubtree();
Close();
TouchLayout();
}
void OldTreeItem::ScrollInto()
{
if(this && owner/* && owner->IsOpen()*/) {
owner->Layout();
Rect rc = owner->GetOffset() + GetItemRect();
rc.Inflate(4);
owner->ScrollInto(rc);
}
}
//////////////////////////////////////////////////////////////////////
// OldTreeEdit::
OldTreeEdit& OldTreeEdit::Perform()
{
if(!cursor)
return *this;
item.TouchLayout();
OldTreeItem *pos;
switch(op & ~DEEP) {
case INS:
if(pos = cursor.GetParent()) {
int index = minmax(cursor.cursor.Top(), 0, pos->GetCount());
if(op & DEEP)
pos->Insert(index) <<= item;
else
pos->Insert(index) = item;
pos->TouchLayout();
}
else
NEVER();
break;
case INS | KIDS:
if(pos = cursor.Get()) {
if(!item.IsEmpty()) {
if(!pos->IsEmpty()) {
int count = pos->GetCount();
pos->children.SetCount(count + item.GetCount());
OldTreeItem::List::Iterator d = pos->children.GetIter(count);
for(OldTreeItem::List::Iterator p = item.children.Begin(), e = item.children.End(); p != e;)
if(op & DEEP)
*d++ <<= *p++;
else
*d++ = *p++;
}
else {
if(op & DEEP)
pos->children <<= item.children;
else
pos->children = item.children;
}
pos->TouchLayout();
}
}
else
NEVER();
break;
case UPD:
if(pos = cursor.Get()) {
if(op & DEEP)
*pos <<= item;
else
*pos = item;
pos->TouchLayout();
}
else
NEVER();
break;
case UPD | KIDS:
if(pos = cursor.Get()) {
if(op & DEEP)
pos->children <<= item.children;
else
pos->children = item.children;
pos->TouchLayout();
}
else
NEVER();
break;
case DEL:
if(pos = cursor.GetParent()) {
int index = cursor.cursor.Top();
if(index >= 0 && index < pos->GetCount()) {
pos->children.Remove(index);
pos->TouchLayout();
cursor.Adjust();
}
else
NEVER();
}
else
NEVER();
break;
case DEL | KIDS:
if(pos = cursor.Get()) {
pos->Clear();
pos->TouchLayout();
}
else
NEVER();
break;
}
return *this;
}
OldTreeEdit& OldTreeEdit::Adjust(OldTreeCursor& pos)
{
switch(op & ~DEEP) {
case INS: pos.AdjustAfter(cursor, 1); break;
case INS | KIDS: break;
case UPD: break;
case UPD | KIDS: pos.Adjust(); break;
case DEL:
if(pos.IsSubtreeOf(cursor))
pos <<= cursor;
pos.AdjustAfter(cursor, -1);
break;
case DEL | KIDS:
if(pos.IsSubtreeOf(cursor))
pos <<= cursor;
pos.Adjust();
break;
}
return *this;
}
OldTreeEdit& OldTreeEdit::Adjust(const Vector<OldTreeCursor *>& list)
{
for(OldTreeCursor *const *p = list.Begin(), *const *e = list.End(); p < e; p++)
Adjust(**p);
return *this;
}
//////////////////////////////////////////////////////////////////////
// OldTreeCtrl::
OldTreeCtrl::OldTreeCtrl()
: updating(false)
, font(StdFont())
, offset(3 + BUTTON_HALF, 3)
, old_scroll(Null)
, open_menu(true)
, hide_top(false)
, hittest(0)
, multiselect(false)
{
// WheelScroll(scroll);
drag_state = 0;
owner = this;
AddFrame(scroll);
scroll.AutoHide();
// scroll/*.AutoHide()*/.AddTo(*this);
scroll.WhenScroll = THISBACK(OnScroll);
WhenOpenClose = THISBACK(OnOpenClose);
WhenPush = THISBACK(OnPush);
WhenRight = THISBACK(OnRight);
WhenKey = THISBACK(OnKey);
WhenBar = THISBACK(StdBar);
WhenLeftDouble = THISBACK(OnOpenClose);
SetFrame(InsetFrame());
}
OldTreeCtrl::~OldTreeCtrl()
{
}
Value OldTreeCtrl::GetData() const
{
const OldTreeItem *item = GetCursorItem();
if(item)
return item->GetData();
return Value();
}
void OldTreeCtrl::SetData(const Value& value)
{
SetCursor(FindValue(value));
}
OldTreeItem::HITTEST OldTreeCtrl::Find(Point pt, const OldTreeItem *& item) const
{
return FindItem(pt - offset + old_scroll, (OldTreeItem*&)item);
}
OldTreeItem::HITTEST OldTreeCtrl::Find(Point pt, OldTreeItem *& item)
{
return FindItem(pt - offset + old_scroll, item);
}
OldTreeCursor OldTreeCtrl::Find(int y) const
{
OldTreeItem *item;
y += old_scroll.y - offset.cy;
HITTEST ht = FindItem(Point(0, y), item);
if(ht != HNIL && item)
return OldTreeCursor(*item);
OldTreeCursor c(*this);
if(y >= tree_size.cy)
c.EndAll();
return c;
}
OldTreeCtrl& OldTreeCtrl::HideTop()
{
hide_top = true;
Indent(4);
Open();
EnableItem();
return *this;
}
void OldTreeCtrl::Layout()
{
// if(!IsOpen())
// return;
/*
if(IsDesignMode() && IsEmpty()) {
Text("OldTreeCtrl", CtrlImg::new_doc()).Open();
Add().Text("Child1", CtrlImg::save());
OldTreeItem& item = Add().Text("Child2", CtrlImg::save_as()).Open();
item.Add().Text("Child3", CtrlImg::open());
}
*/
LayoutItem();
old_scroll = scroll;
rowht = tree_size.cy / max(GetOpenCount(), 1);
Size total = GetTreeSize() + 2 * offset;
scroll.Set(old_scroll, scroll.GetReducedViewSize(), total);
scroll.SetLine((scroll.GetReducedViewSize().cx >> 3) + 1, rowht);
}
void OldTreeCtrl::Paint(Draw& draw)
{
if(layout) {
Layout();
Refresh();
}
Size size = GetSize();
Rect rc = GetClientTreeRect();
Rect rc_butt = rc;
rc_butt.left += BUTTON_HALF + 1 + ICONGAP;
Color bg = (IsShowEnabled() ? SWhite : SLtGray);
DrawRectMinusRect(draw, GetSize(), rc_butt, DBG_PAINT(bg, LtGreen));
PaintItem(draw, rc.TopLeft());
}
void OldTreeCtrl::OnScroll()
{
if((Point)scroll != old_scroll) {
Size delta = old_scroll - (Point)scroll;
Size view = GetSize();
old_scroll = scroll;
if(IsNull(old_scroll.x) || 2 * tabs(delta.cx) >= view.cx || 2 * tabs(delta.cy) >= view.cy || IsFullRefresh())
Refresh();
else
ScrollView(delta);
}
}
void OldTreeCtrl::LeftDown(Point pt, dword keyflags)
{
SetFocus();
SetCapture();
OldTreeItem *item;
hittest = Find(pt, item);
if(multiselect && hittest == HNIL && !(keyflags & (K_CTRL | K_SHIFT)))
UnselectTree();
else if(item && item->IsEnabledItem()) {
OldTreeCursor new_cursor(*item);
switch(hittest) {
case HBUTTON:
item->Open(!item->IsOpenItem());
item->TouchLayout();
break;
case HNIL:
break;
default:
if(multiselect && (keyflags & K_CTRL))
PickCursor(new_cursor, XOR_SEL);
else if(multiselect && (keyflags & K_SHIFT) && anchor) // anchor selection
PickCursor(new_cursor, DRAG_SEL);
else if(!item->IsSelected())
PickCursor(new_cursor, SET_SEL);
else
PickCursor(new_cursor, KEEP_SEL);
break;
}
}
drag_start = pt;
drag_state = 2;
WhenPush();
}
void OldTreeCtrl::LeftUp(Point pt, dword keyflags)
{
ReleaseCapture();
if(drag_state == 1)
WhenDrag(pt + GetScreenView().TopLeft(), keyflags, 3);
else {
}
// OldTreeItem *item;
// hittest = Find(pt, item);
// SetCursor(item);
Action();
drag_state = 0;
}
void OldTreeCtrl::LeftDouble(Point pt, dword keyflags)
{
IgnoreMouseUp();
OldTreeItem *item;
int hit = Find(pt, item);
if(item == GetCursor().Get() && hit == hittest)
WhenLeftDouble();
}
void OldTreeCtrl::RightDown(Point pt, dword keyflags)
{
if(drag_state == 1) {
WhenDrag(pt + GetScreenView().TopLeft(), keyflags, 2);
drag_state = 0;
}
SetFocus();
OldTreeItem *item;
hittest = Find(pt, item);
SetCursor(item);
Action();
WhenRight();
}
void OldTreeCtrl::MouseMove(Point pt, dword keyflags)
{
Point scrn = pt + GetScreenView().TopLeft();
if(drag_state == 2 && abs2(pt - drag_start) >= 50) { // start d&d
WhenDrag(scrn, keyflags, 0);
drag_state = 1;
}
if(drag_state == 1)
WhenDrag(scrn, keyflags, 1);
}
void OldTreeCtrl::MouseWheel(Point p, int zdelta, dword keyflags)
{
scroll.WheelY(zdelta);
}
Image OldTreeCtrl::CursorImage(Point pt, dword keyflags)
{
Image image = Image::Arrow();
if(drag_state == 1)
WhenDragImage(image);
return image;
}
//////////////////////////////////////////////////////////////////////
// default handlers
void OldTreeCtrl::OnOpenClose()
{
const OldTreeCursor& cursor = GetCursor();
if(cursor) {
cursor->Open(!cursor->IsOpenItem());
cursor.TouchLayout();
}
}
void OldTreeCtrl::OnPush()
{
}
void OldTreeCtrl::OnRight()
{
MenuBar menu;
WhenBar(menu);
menu.Execute();
}
void OldTreeCtrl::StdBar(Bar& bar)
{
const OldTreeCursor& cursor = GetCursor();
if(cursor && cursor->IsSubtree()) {
cursor->BarOpenClose(bar);
cursor->BarOpenSubtree(bar);
cursor->BarCloseSubtree(bar);
}
}
bool OldTreeCtrl::Key(dword key, int repcnt)
{
if(WhenKey(key))
return true;
if(key == K_INSERT)
if(OldTreeItem *item = GetCursorItem()) {
item->OnOpenClose();
return true;
}
return false;
}
bool OldTreeCtrl::OnKey(dword key)
{
OldTreeCursor move = cursor.Copy();
Rect rc(0, 0, 0, 0);
const OldTreeItem *item = move.Get();
if(item)
rc = item->GetClientRect();
int ops = SET_SEL;
if(key & K_CTRL)
ops = XOR_SEL;
else if(key & K_SHIFT)
ops = DRAG_SEL;
switch(key & ~(K_SHIFT | K_CTRL)) {
default:
return Bar::Scan(WhenBar, key);
case K_UP:
if(move)
move.Up(OldTreeCursor::HIERARCHY);
else
move = OldTreeCursor(*this);
break;
case K_DOWN:
if(move)
move.Down(OldTreeCursor::HIERARCHY);
else
move = OldTreeCursor(*this);
break;
case K_PAGEUP:
move = Find(rc.bottom - GetSize().cy);
break;
case K_PAGEDOWN:
move = Find(rc.top + GetSize().cy);
break;
case K_CTRL_PAGEUP:
case K_HOME:
move = OldTreeCursor(*this);
break;
case K_CTRL_PAGEDOWN:
case K_END:
move = OldTreeCursor(*this).EndAll();
break;
}
if(move.IsEnd())
move.Up(OldTreeCursor::HIERARCHY);
if(move.IsBegin() && move && hide_top)
move.Down(OldTreeCursor::HIERARCHY);
if(move != cursor) {
PickCursor(move, ops);
Action();
}
return true;
}
OldTreeCtrl& OldTreeCtrl::PickCursor(pick_ OldTreeCursor& nc, int selection_op)
{
OldTreeCursor new_cursor = nc;
new_cursor.Adjust();
cursor.Touch();
if(selection_op == DRAG_SEL && anchor) {
UnselectTree();
bool anchorup = anchor >= new_cursor;
const OldTreeCursor& b = (anchorup ? new_cursor : anchor);
const OldTreeCursor& l = (anchorup ? anchor : new_cursor);
for(OldTreeCursor p(b, 0); p <= l; p.Down(p.HIERARCHY))
if(p->IsEnabledItem())
p->Select();
}
else if(selection_op != KEEP_SEL) {
anchor <<= new_cursor;
if(selection_op == SET_SEL)
UnselectTree();
if(new_cursor)
new_cursor->Select(selection_op == SET_SEL ? true : !new_cursor->IsSelected());
}
cursor = new_cursor;
cursor.Touch().Open().ScrollInto();
WhenSetCursor();
return *this;
}
OldTreeCtrl& OldTreeCtrl::SetCursor(const OldTreeItem *item)
{
if(!item)
return PickCursor(OldTreeCursor());
ASSERT(item->owner == this);
return PickCursor(OldTreeCursor(*item));
}
void OldTreeCtrl::GotFocus()
{
Refresh();
}
void OldTreeCtrl::LostFocus()
{
Refresh();
}
Point OldTreeCtrl::GetCursorSc() const
{
Point pt = scroll;
OldTreeItem *i = GetCursorItem();
if(i)
pt -= i->GetTreeRect().TopLeft();
return pt;
}
void OldTreeCtrl::ScCursor(Point pt)
{
OldTreeItem *i = GetCursorItem();
if(i)
pt += i->GetTreeRect().TopLeft();
scroll = pt;
}
}