⛏️ index : haiku.git

/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2012-2013, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */

#include "table/TreeTable.h"

#include <new>


// #pragma mark - TreeTableField


class TreeTableField : public BField {
public:
	TreeTableField(void* object)
		:
		fObject(object)
	{
	}

	void* Object() const
	{
		return fObject;
	}

private:
	void*	fObject;
};


// #pragma mark - TreeTablePath


TreeTablePath::TreeTablePath()
{
}


TreeTablePath::TreeTablePath(const TreeTablePath& other)
{
	*this = other;
}


TreeTablePath::TreeTablePath(const TreeTablePath& other, int32 childIndex)
{
	*this = other;
	AddComponent(childIndex);
}


TreeTablePath::~TreeTablePath()
{
}


bool
TreeTablePath::AddComponent(int32 childIndex)
{
	try {
		fComponents.push_back(childIndex);
		return true;
	} catch (...) {
		return false;
	}
}


int32
TreeTablePath::RemoveLastComponent()
{
	if (fComponents.empty())
		return -1;

	int32 index = fComponents.back();
	fComponents.pop_back();
	return index;
}

void
TreeTablePath::Clear()
{
	fComponents.clear();
}


int32
TreeTablePath::CountComponents() const
{
	return fComponents.size();
}


int32
TreeTablePath::ComponentAt(int32 index) const
{
	if (index < 0 || (size_t)index >= fComponents.size())
		return -1;
	return fComponents[index];
}


TreeTablePath&
TreeTablePath::operator=(const TreeTablePath& other)
{
	try {
		fComponents = other.fComponents;
	} catch (...) {
	}
	return *this;
}


bool
TreeTablePath::operator==(const TreeTablePath& other) const
{
	return fComponents == other.fComponents;
}


bool
TreeTablePath::operator!=(const TreeTablePath& other) const
{
	return fComponents != other.fComponents;
}


// #pragma mark - TreeTableModelListener


TreeTableModelListener::~TreeTableModelListener()
{
}


void
TreeTableModelListener::TableNodesAdded(TreeTableModel* model,
	const TreeTablePath& path, int32 childIndex, int32 count)
{
}


void
TreeTableModelListener::TableNodesRemoved(TreeTableModel* model,
	const TreeTablePath& path, int32 childIndex, int32 count)
{
}


void
TreeTableModelListener::TableNodesChanged(TreeTableModel* model,
	const TreeTablePath& path, int32 childIndex, int32 count)
{
}


void
TreeTableModelListener::TableModelReset(TreeTableModel* model)
{
}


// #pragma mark - TreeTableModel


TreeTableModel::~TreeTableModel()
{
}


void*
TreeTableModel::NodeForPath(const TreeTablePath& path) const
{
	void* node = Root();

	int32 count = path.CountComponents();
	for (int32 i = 0; node != NULL && i < count; i++)
		node = ChildAt(node, path.ComponentAt(i));

	return node;
}


bool
TreeTableModel::AddListener(TreeTableModelListener* listener)
{
	return fListeners.AddItem(listener);
}


void
TreeTableModel::RemoveListener(TreeTableModelListener* listener)
{
	fListeners.RemoveItem(listener);
}


void
TreeTableModel::NotifyNodesAdded(const TreeTablePath& path, int32 childIndex,
	int32 count)
{
	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--) {
		TreeTableModelListener* listener = fListeners.ItemAt(i);
		listener->TableNodesAdded(this, path, childIndex, count);
	}
}


void
TreeTableModel::NotifyNodesRemoved(const TreeTablePath& path, int32 childIndex,
	int32 count)
{
	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--) {
		TreeTableModelListener* listener = fListeners.ItemAt(i);
		listener->TableNodesRemoved(this, path, childIndex, count);
	}
}


void
TreeTableModel::NotifyNodesChanged(const TreeTablePath& path, int32 childIndex,
	int32 count)
{
	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--) {
		TreeTableModelListener* listener = fListeners.ItemAt(i);
		listener->TableNodesChanged(this, path, childIndex, count);
	}
}


void
TreeTableModel::NotifyTableModelReset()
{
	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--) {
		TreeTableModelListener* listener = fListeners.ItemAt(i);
		listener->TableModelReset(this);
	}
}

// #pragma mark - TreeTableToolTipProvider


TreeTableToolTipProvider::~TreeTableToolTipProvider()
{
}


// #pragma mark - TreeTableListener


TreeTableListener::~TreeTableListener()
{
}


void
TreeTableListener::TreeTableSelectionChanged(TreeTable* table)
{
}


void
TreeTableListener::TreeTableNodeInvoked(TreeTable* table,
	const TreeTablePath& path)
{
}


void
TreeTableListener::TreeTableNodeExpandedChanged(TreeTable* table,
	const TreeTablePath& path, bool expanded)
{
}


void
TreeTableListener::TreeTableCellMouseDown(TreeTable* table,
	const TreeTablePath& path, int32 columnIndex, BPoint screenWhere,
	uint32 buttons)
{
}


void
TreeTableListener::TreeTableCellMouseUp(TreeTable* table,
	const TreeTablePath& path, int32 columnIndex, BPoint screenWhere,
	uint32 buttons)
{
}


// #pragma mark - Column


class TreeTable::Column : public AbstractColumn {
public:
								Column(TreeTableModel* model,
									TableColumn* tableColumn);
	virtual						~Column();

	virtual	void				SetModel(AbstractTableModelBase* model);

protected:
	virtual	void				DrawTitle(BRect rect, BView* targetView);
	virtual	void				DrawField(BField* field, BRect rect,
									BView* targetView);
	virtual	int					CompareFields(BField* field1, BField* field2);

	virtual	void				GetColumnName(BString* into) const;
	virtual	float				GetPreferredWidth(BField* field,
									BView* parent) const;

private:
			TreeTableModel*		fModel;
};


TreeTable::Column::Column(TreeTableModel* model, TableColumn* tableColumn)
	:
	AbstractColumn(tableColumn),
	fModel(model)
{
}


TreeTable::Column::~Column()
{
}


void
TreeTable::Column::SetModel(AbstractTableModelBase* model)
{
	fModel = dynamic_cast<TreeTableModel*>(model);
}


void
TreeTable::Column::DrawTitle(BRect rect, BView* targetView)
{
	fTableColumn->DrawTitle(rect, targetView);
}


void
TreeTable::Column::DrawField(BField* _field, BRect rect, BView* targetView)
{
	TreeTableField* field = dynamic_cast<TreeTableField*>(_field);
	if (field == NULL)
		return;

	int32 modelIndex = fTableColumn->ModelIndex();
	BVariant value;
	if (!fModel->GetValueAt(field->Object(), modelIndex, value))
		return;
	fTableColumn->DrawValue(value, rect, targetView);
}


int
TreeTable::Column::CompareFields(BField* _field1, BField* _field2)
{
	TreeTableField* field1 = dynamic_cast<TreeTableField*>(_field1);
	TreeTableField* field2 = dynamic_cast<TreeTableField*>(_field2);

	if (field1 == field2)
		return 0;

	if (field1 == NULL)
		return -1;
	if (field2 == NULL)
		return 1;

	int32 modelIndex = fTableColumn->ModelIndex();
	BVariant value1;
	bool valid1 = fModel->GetValueAt(field1->Object(), modelIndex, value1);
	BVariant value2;
	bool valid2 = fModel->GetValueAt(field2->Object(), modelIndex, value2);

	if (!valid1)
		return valid2 ? -1 : 0;
	if (!valid2)
		return 1;

	return fTableColumn->CompareValues(value1, value2);
}


void
TreeTable::Column::GetColumnName(BString* into) const
{
	fTableColumn->GetColumnName(into);
}


float
TreeTable::Column::GetPreferredWidth(BField* _field, BView* parent) const
{
	TreeTableField* field = dynamic_cast<TreeTableField*>(_field);
	if (field == NULL)
		return Width();

	int32 modelIndex = fTableColumn->ModelIndex();
	BVariant value;
	if (!fModel->GetValueAt(field->Object(), modelIndex, value))
		return Width();
	return fTableColumn->GetPreferredWidth(value, parent);
}


// #pragma mark - TreeTableRow


class TreeTableRow : public BRow {
public:
	TreeTableRow(TreeTableNode* node)
		:
		fNode(node)
	{
	}

	TreeTableNode* Node()
	{
		return fNode;
	}

private:
	TreeTableNode*	fNode;
};


// #pragma mark - TreeTableNode


class TreeTableNode {
public:
								TreeTableNode(TreeTableNode* parent);
								~TreeTableNode();

			status_t			Init(void* modelObject, int32 columnCount);
			void				DetachRow();

			TreeTableNode*		Parent() const	{ return fParent; }
			TreeTableRow*		Row() const		{ return fRow; }
			void*				ModelObject() const;

			bool				AddChild(TreeTableNode* child, int32 index);
			TreeTableNode*		RemoveChild(int32 index);

			int32				CountChildren() const;
			TreeTableNode*		ChildAt(int32 index);
			int32				IndexOf(TreeTableNode* child);

private:
			typedef BObjectList<TreeTableNode> NodeList;

private:
			TreeTableNode*		fParent;
			TreeTableRow*		fRow;
			NodeList*			fChildren;
};


TreeTableNode::TreeTableNode(TreeTableNode* parent)
	:
	fParent(parent),
	fRow(NULL),
	fChildren(NULL)
{
}


TreeTableNode::~TreeTableNode()
{
	delete fChildren;
	delete fRow;
}


status_t
TreeTableNode::Init(void* modelObject, int32 columnCount)
{
	// create row
	fRow = new(std::nothrow) TreeTableRow(this);
	if (fRow == NULL)
		return B_NO_MEMORY;

	// add dummy fields to row
	for (int32 columnIndex = 0; columnIndex < columnCount; columnIndex++) {
		// It would be nice to create only a single field and set it for all
		// columns, but the row takes ultimate ownership, so it have to be
		// individual objects.
		TreeTableField* field = new(std::nothrow) TreeTableField(modelObject);
		if (field == NULL)
			return B_NO_MEMORY;

		fRow->SetField(field, columnIndex);
	}

	return B_OK;
}


void
TreeTableNode::DetachRow()
{

	fRow = NULL;

	if (fChildren != NULL) {
		for (int32 i = 0; TreeTableNode* child = fChildren->ItemAt(i); i++)
			child->DetachRow();
	}
}


void*
TreeTableNode::ModelObject() const
{
	TreeTableField* field = dynamic_cast<TreeTableField*>(fRow->GetField(0));
	return field->Object();
}


bool
TreeTableNode::AddChild(TreeTableNode* child, int32 index)
{
	if (fChildren == NULL) {
		fChildren = new(std::nothrow) NodeList(10, true);
		if (fChildren == NULL)
			return false;
	}

	return fChildren->AddItem(child, index);
}


TreeTableNode*
TreeTableNode::RemoveChild(int32 index)
{
	return fChildren != NULL ? fChildren->RemoveItemAt(index) : NULL;
}


int32
TreeTableNode::CountChildren() const
{
	return fChildren != NULL ? fChildren->CountItems() : 0;
}


TreeTableNode*
TreeTableNode::ChildAt(int32 index)
{
	return fChildren != NULL ? fChildren->ItemAt(index) : NULL;
}


int32
TreeTableNode::IndexOf(TreeTableNode* child)
{
	return fChildren != NULL ? fChildren->IndexOf(child) : -1;
}


// #pragma mark - TreeTableSelectionModel


TreeTableSelectionModel::TreeTableSelectionModel(TreeTable* table)
	:
	fTreeTable(table),
	fNodes(NULL),
	fNodeCount(-1)
{
}


TreeTableSelectionModel::~TreeTableSelectionModel()
{
	delete[] fNodes;
}


int32
TreeTableSelectionModel::CountNodes()
{
	_Update();

	return fNodeCount;
}


void*
TreeTableSelectionModel::NodeAt(int32 index)
{
	if (TreeTableNode* node = _NodeAt(index))
		return node->ModelObject();
	return NULL;
}


bool
TreeTableSelectionModel::GetPathAt(int32 index, TreeTablePath& _path)
{
	if (TreeTableNode* node = _NodeAt(index)) {
		fTreeTable->_GetPathForNode(node, _path);
		return true;
	}

	return false;
}


void
TreeTableSelectionModel::_SelectionChanged()
{
	if (fNodeCount >= 0) {
		fNodeCount = -1;
		delete[] fNodes;
		fNodes = NULL;
	}
}


void
TreeTableSelectionModel::_Update()
{
	if (fNodeCount >= 0)
		return;

	// count the nodes
	fNodeCount = 0;
	BRow* row = NULL;
	while ((row = fTreeTable->CurrentSelection(row)) != NULL)
		fNodeCount++;

	if (fNodeCount == 0)
		return;

	// allocate node array
	fNodes = new(std::nothrow) TreeTableNode*[fNodeCount];
	if (fNodes == NULL) {
		fNodeCount = 0;
		return;
	}

	// get the nodes
	row = NULL;
	int32 index = 0;
	while ((row = fTreeTable->CurrentSelection(row)) != NULL)
		fNodes[index++] = dynamic_cast<TreeTableRow*>(row)->Node();
}


TreeTableNode*
TreeTableSelectionModel::_NodeAt(int32 index)
{
	_Update();

	return index >= 0 && index < fNodeCount ? fNodes[index] : NULL;
}


// #pragma mark - Table


TreeTable::TreeTable(const char* name, uint32 flags, border_style borderStyle,
	bool showHorizontalScrollbar)
	:
	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
	fModel(NULL),
	fToolTipProvider(NULL),
	fRootNode(NULL),
	fSelectionModel(this),
	fIgnoreSelectionChange(0)
{
}


TreeTable::TreeTable(TreeTableModel* model, const char* name, uint32 flags,
	border_style borderStyle, bool showHorizontalScrollbar)
	:
	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
	fModel(NULL),
	fToolTipProvider(NULL),
	fRootNode(NULL),
	fSelectionModel(this),
	fIgnoreSelectionChange(0)
{
	SetTreeTableModel(model);
}


TreeTable::~TreeTable()
{
	SetTreeTableModel(NULL);
}


bool
TreeTable::SetTreeTableModel(TreeTableModel* model)
{
	if (model == fModel)
		return true;

	if (fModel != NULL) {
		fModel->RemoveListener(this);

		if (fRootNode != NULL) {
			fRootNode->DetachRow();
			delete fRootNode;
			fRootNode = NULL;
		}

		Clear();

		for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
			column->SetModel(NULL);
	}

	fModel = model;

	if (fModel == NULL)
		return true;

	fRootNode = new(std::nothrow) TreeTableNode(NULL);
	if (fRootNode == NULL)
		return false;

	if (fRootNode->Init(fModel->Root(), fModel->CountColumns()) != B_OK) {
		delete fRootNode;
		fRootNode = NULL;
		return false;
	}

	fModel->AddListener(this);

	for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
		column->SetModel(fModel);

	// recursively create the rows
	if (!_AddChildRows(fRootNode, 0, fModel->CountChildren(fModel->Root()),
			fModel->CountColumns())) {
		SetTreeTableModel(NULL);
		return false;
	}

	return true;
}


void
TreeTable::SetToolTipProvider(TreeTableToolTipProvider* toolTipProvider)
{
	fToolTipProvider = toolTipProvider;
}


TreeTableSelectionModel*
TreeTable::SelectionModel()
{
	return &fSelectionModel;
}


void
TreeTable::SelectNode(const TreeTablePath& path, bool extendSelection)
{
	TreeTableNode* node = _NodeForPath(path);
	if (node == NULL)
		return;

	if (!extendSelection) {
		fIgnoreSelectionChange++;
		DeselectAll();
		fIgnoreSelectionChange--;
	}

	AddToSelection(node->Row());
}


void
TreeTable::DeselectNode(const TreeTablePath& path)
{
	if (TreeTableNode* node = _NodeForPath(path))
		Deselect(node->Row());
}


void
TreeTable::DeselectAllNodes()
{
	DeselectAll();
}


bool
TreeTable::IsNodeExpanded(const TreeTablePath& path) const
{
	if (TreeTableNode* node = _NodeForPath(path))
		return node->Row()->IsExpanded();
	return false;
}


void
TreeTable::SetNodeExpanded(const TreeTablePath& path, bool expanded,
	bool expandAncestors)
{
	if (TreeTableNode* node = _NodeForPath(path))
		_SetNodeExpanded(node, expanded, expandAncestors);
}


void
TreeTable::ScrollToNode(const TreeTablePath& path)
{
	if (TreeTableNode* node = _NodeForPath(path))
		BColumnListView::ScrollTo(node->Row());
}


bool
TreeTable::AddTreeTableListener(TreeTableListener* listener)
{
	return fListeners.AddItem(listener);
}


void
TreeTable::RemoveTreeTableListener(TreeTableListener* listener)
{
	fListeners.RemoveItem(listener);
}


status_t
TreeTable::GetCellRectAt(const TreeTablePath& path, int32 colIndex,
	BRect& _output) const
{
	TreeTableNode* node = _NodeForPath(path);
	if (node == NULL)
		return B_ENTRY_NOT_FOUND;

	AbstractColumn* column = fColumns.ItemAt(colIndex);
	if (column == NULL)
		return B_ENTRY_NOT_FOUND;

	_output = GetFieldRect(node->Row(), column);

	return B_OK;
}


bool
TreeTable::GetToolTipAt(BPoint point, BToolTip** _tip)
{
	if (fToolTipProvider == NULL)
		return AbstractTable::GetToolTipAt(point, _tip);

	// get the table row
	BRow* row = RowAt(point);
	if (row == NULL)
		return AbstractTable::GetToolTipAt(point, _tip);

	TreeTableRow* treeRow = dynamic_cast<TreeTableRow*>(row);
	// get the table column
	BColumn* column = ColumnAt(point);

	int32 columnIndex = column != NULL ? column->LogicalFieldNum() : -1;

	TreeTablePath path;
	_GetPathForNode(treeRow->Node(), path);

	return fToolTipProvider->GetToolTipForTablePath(path, columnIndex,
		_tip);
}


void
TreeTable::SelectionChanged()
{
	if (fIgnoreSelectionChange > 0)
		return;

	fSelectionModel._SelectionChanged();

	if (!fListeners.IsEmpty()) {
		int32 listenerCount = fListeners.CountItems();
		for (int32 i = listenerCount - 1; i >= 0; i--)
			fListeners.ItemAt(i)->TreeTableSelectionChanged(this);
	}
}


AbstractTable::AbstractColumn*
TreeTable::CreateColumn(TableColumn* column)
{
	return new Column(fModel, column);
}


void
TreeTable::ColumnMouseDown(AbstractColumn* column, BRow* _row, BField* field,
	BPoint screenWhere, uint32 buttons)
{
	if (!fListeners.IsEmpty()) {
		// get the table node and the column index
		TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
		int32 columnIndex = column->LogicalFieldNum();
		if (row == NULL || columnIndex < 0)
			return;

		// get the node path
		TreeTablePath path;
		_GetPathForNode(row->Node(), path);

		// notify listeners
		int32 listenerCount = fListeners.CountItems();
		for (int32 i = listenerCount - 1; i >= 0; i--) {
			fListeners.ItemAt(i)->TreeTableCellMouseDown(this, path,
				columnIndex, screenWhere, buttons);
		}
	}
}


void
TreeTable::ColumnMouseUp(AbstractColumn* column, BRow* _row, BField* field,
	BPoint screenWhere, uint32 buttons)
{
	if (!fListeners.IsEmpty()) {
		// get the table node and the column index
		TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
		int32 columnIndex = column->LogicalFieldNum();
		if (row == NULL || columnIndex < 0)
			return;

		// get the node path
		TreeTablePath path;
		_GetPathForNode(row->Node(), path);

		// notify listeners
		int32 listenerCount = fListeners.CountItems();
		for (int32 i = listenerCount - 1; i >= 0; i--) {
			fListeners.ItemAt(i)->TreeTableCellMouseUp(this, path, columnIndex,
				screenWhere, buttons);
		}
	}
}


void
TreeTable::TableNodesAdded(TreeTableModel* model, const TreeTablePath& path,
	int32 childIndex, int32 count)
{
	TreeTableNode* node = _NodeForPath(path);
	if (node == NULL)
		return;

	_AddChildRows(node, childIndex, count, fModel->CountColumns());
}


void
TreeTable::TableNodesRemoved(TreeTableModel* model, const TreeTablePath& path,
	int32 childIndex, int32 count)
{
	TreeTableNode* node = _NodeForPath(path);
	if (node == NULL)
		return;

	_RemoveChildRows(node, childIndex, count);
}


void
TreeTable::TableNodesChanged(TreeTableModel* model, const TreeTablePath& path,
	int32 childIndex, int32 count)
{
	TreeTableNode* node = _NodeForPath(path);
	if (node == NULL)
		return;

	int32 endIndex = childIndex + count;
	for (int32 i = childIndex; i < endIndex; i++) {
		if (TreeTableNode* child = node->ChildAt(i))
			UpdateRow(child->Row());
	}
}


void
TreeTable::TableModelReset(TreeTableModel* model)
{
	_RemoveChildRows(fRootNode, 0, fRootNode->CountChildren());
	_AddChildRows(fRootNode, 0, fModel->CountChildren(
		fModel->Root()), fModel->CountColumns());
}


void
TreeTable::ExpandOrCollapse(BRow* _row, bool expand)
{
	TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
	if (row == NULL || row->IsExpanded() == expand)
		return;

	AbstractTable::ExpandOrCollapse(row, expand);

	if (row->IsExpanded() != expand)
		return;

	TreeTablePath path;
	_GetPathForNode(row->Node(), path);

	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--)
		fListeners.ItemAt(i)->TreeTableNodeExpandedChanged(this, path, expand);
}


void
TreeTable::ItemInvoked()
{
	if (fListeners.IsEmpty())
		return;

	TreeTableRow* row = dynamic_cast<TreeTableRow*>(CurrentSelection());
	if (row == NULL)
		return;

	TreeTablePath path;
	_GetPathForNode(row->Node(), path);

	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--)
		fListeners.ItemAt(i)->TreeTableNodeInvoked(this, path);
}


bool
TreeTable::_AddChildRows(TreeTableNode* parentNode, int32 childIndex,
	int32 count, int32 columnCount)
{
	int32 childEndIndex = childIndex + count;
	for (int32 i = childIndex; i < childEndIndex; i++) {
		void* child = fModel->ChildAt(parentNode->ModelObject(), i);

		// create node
		TreeTableNode* node = new(std::nothrow) TreeTableNode(parentNode);
		if (node == NULL || node->Init(child, columnCount) != B_OK
			|| !parentNode->AddChild(node, i)) {
			delete node;
			return false;
		}

		// add row
		AddRow(node->Row(), i,
			parentNode != fRootNode ? parentNode->Row() : NULL);

		// recursively create children
		if (!_AddChildRows(node, 0, fModel->CountChildren(child), columnCount))
			return false;
	}

	return true;
}


void
TreeTable::_RemoveChildRows(TreeTableNode* parentNode, int32 childIndex,
	int32 count)
{
	// check if the removal request would in effect remove all
	// existing nodes.
	if (parentNode == fRootNode && childIndex == 0
		&& count == parentNode->CountChildren()) {
		Clear();
		return;
	}

	for (int32 i = childIndex + count - 1; i >= childIndex; i--) {
		if (TreeTableNode* child = parentNode->RemoveChild(i)) {
			int32 childCount = child->CountChildren();
			if (childCount > 0)
				_RemoveChildRows(child, 0, childCount);

			RemoveRow(child->Row());
			delete child;
		}
	}
}


void
TreeTable::_SetNodeExpanded(TreeTableNode* node, bool expanded,
	bool expandAncestors)
{
	if (expanded && expandAncestors && node != fRootNode)
		_SetNodeExpanded(node->Parent(), true, true);

	ExpandOrCollapse(node->Row(), expanded);
}


TreeTableNode*
TreeTable::_NodeForPath(const TreeTablePath& path) const
{
	TreeTableNode* node = fRootNode;

	int32 count = path.CountComponents();
	for (int32 i = 0; node != NULL && i < count; i++)
		node = node->ChildAt(path.ComponentAt(i));

	return node;
}


void
TreeTable::_GetPathForNode(TreeTableNode* node, TreeTablePath& _path) const
{
	if (node == fRootNode) {
		_path.Clear();
		return;
	}

	_GetPathForNode(node->Parent(), _path);
	_path.AddComponent(node->Parent()->IndexOf(node));
}