Added support for tree table model listeners (mutation events), selection, and
collapsing/expanding nodes. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31269 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -29,6 +29,120 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - TreeTableModel
|
// #pragma mark - TreeTableModel
|
||||||
|
|
||||||
|
|
||||||
@@ -37,6 +151,69 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - TreeTableListener
|
// #pragma mark - TreeTableListener
|
||||||
|
|
||||||
|
|
||||||
@@ -46,7 +223,14 @@ TreeTableListener::~TreeTableListener()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TreeTableListener::TreeTableNodeInvoked(TreeTable* table, void* node)
|
TreeTableListener::TreeTableSelectionChanged(TreeTable* table)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TreeTableListener::TreeTableNodeInvoked(TreeTable* table,
|
||||||
|
const TreeTablePath& path)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +354,259 @@ TreeTable::Column::GetPreferredWidth(BField* _field, BView* parent) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #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
|
// #pragma mark - Table
|
||||||
|
|
||||||
|
|
||||||
@@ -177,7 +614,10 @@ TreeTable::TreeTable(const char* name, uint32 flags, border_style borderStyle,
|
|||||||
bool showHorizontalScrollbar)
|
bool showHorizontalScrollbar)
|
||||||
:
|
:
|
||||||
AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
|
AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
|
||||||
fModel(NULL)
|
fModel(NULL),
|
||||||
|
fRootNode(NULL),
|
||||||
|
fSelectionModel(this),
|
||||||
|
fIgnoreSelectionChange(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +626,10 @@ TreeTable::TreeTable(TreeTableModel* model, const char* name, uint32 flags,
|
|||||||
border_style borderStyle, bool showHorizontalScrollbar)
|
border_style borderStyle, bool showHorizontalScrollbar)
|
||||||
:
|
:
|
||||||
AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
|
AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
|
||||||
fModel(NULL)
|
fModel(NULL),
|
||||||
|
fRootNode(NULL),
|
||||||
|
fSelectionModel(this),
|
||||||
|
fIgnoreSelectionChange(0)
|
||||||
{
|
{
|
||||||
SetTreeTableModel(model);
|
SetTreeTableModel(model);
|
||||||
}
|
}
|
||||||
@@ -194,18 +637,28 @@ TreeTable::TreeTable(TreeTableModel* model, const char* name, uint32 flags,
|
|||||||
|
|
||||||
TreeTable::~TreeTable()
|
TreeTable::~TreeTable()
|
||||||
{
|
{
|
||||||
|
SetTreeTableModel(NULL);
|
||||||
|
|
||||||
for (int32 i = CountColumns() - 1; i >= 0; i--)
|
for (int32 i = CountColumns() - 1; i >= 0; i--)
|
||||||
RemoveColumn(ColumnAt(i));
|
RemoveColumn(ColumnAt(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
bool
|
||||||
TreeTable::SetTreeTableModel(TreeTableModel* model)
|
TreeTable::SetTreeTableModel(TreeTableModel* model)
|
||||||
{
|
{
|
||||||
if (model == fModel)
|
if (model == fModel)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
if (fModel != NULL) {
|
if (fModel != NULL) {
|
||||||
|
fModel->RemoveListener(this);
|
||||||
|
|
||||||
|
if (fRootNode != NULL) {
|
||||||
|
fRootNode->DetachRow();
|
||||||
|
delete fRootNode;
|
||||||
|
fRootNode = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
|
for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
|
||||||
@@ -215,50 +668,96 @@ TreeTable::SetTreeTableModel(TreeTableModel* model)
|
|||||||
fModel = model;
|
fModel = model;
|
||||||
|
|
||||||
if (fModel == NULL)
|
if (fModel == NULL)
|
||||||
return;
|
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++)
|
for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
|
||||||
column->SetModel(fModel);
|
column->SetModel(fModel);
|
||||||
|
|
||||||
// recursively create the rows
|
// recursively create the rows
|
||||||
_AddChildRows(fModel->Root(), NULL, fModel->CountColumns());
|
if (!_AddChildRows(fRootNode, 0, fModel->CountChildren(fModel->Root()),
|
||||||
|
fModel->CountColumns())) {
|
||||||
|
SetTreeTableModel(NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TreeTableSelectionModel*
|
||||||
|
TreeTable::SelectionModel()
|
||||||
|
{
|
||||||
|
return &fSelectionModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TreeTable::_AddChildRows(void* parent, BRow* parentRow, int32 columnCount)
|
TreeTable::SelectNode(const TreeTablePath& path, bool extendSelection)
|
||||||
{
|
{
|
||||||
int32 childCount = fModel->CountChildren(parent);
|
TreeTableNode* node = _NodeForPath(path);
|
||||||
for (int32 i = 0; i < childCount; i++) {
|
if (node == NULL)
|
||||||
void* child = fModel->ChildAt(parent, i);
|
return;
|
||||||
|
|
||||||
// create row
|
if (!extendSelection) {
|
||||||
BRow* row = new(std::nothrow) BRow();
|
fIgnoreSelectionChange++;
|
||||||
if (row == NULL) {
|
DeselectAll();
|
||||||
// TODO: Report error!
|
fIgnoreSelectionChange--;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(child);
|
|
||||||
if (field == NULL) {
|
|
||||||
// TODO: Report error!
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
row->SetField(field, columnIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add row
|
|
||||||
AddRow(row, parentRow);
|
|
||||||
|
|
||||||
// recursively create children
|
|
||||||
_AddChildRows(child, row, columnCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -276,6 +775,22 @@ TreeTable::RemoveTreeTableListener(TreeTableListener* listener)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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*
|
AbstractTable::AbstractColumn*
|
||||||
TreeTable::CreateColumn(TableColumn* column)
|
TreeTable::CreateColumn(TableColumn* column)
|
||||||
{
|
{
|
||||||
@@ -283,21 +798,143 @@ TreeTable::CreateColumn(TableColumn* column)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
void
|
||||||
TreeTable::ItemInvoked()
|
TreeTable::ItemInvoked()
|
||||||
{
|
{
|
||||||
if (fListeners.IsEmpty())
|
if (fListeners.IsEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BRow* row = CurrentSelection();
|
TreeTableRow* row = dynamic_cast<TreeTableRow*>(CurrentSelection());
|
||||||
if (row == NULL)
|
if (row == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TreeTableField* field = dynamic_cast<TreeTableField*>(row->GetField(0));
|
TreeTablePath path;
|
||||||
if (field == NULL)
|
_GetPathForNode(row->Node(), path);
|
||||||
return;
|
|
||||||
|
|
||||||
int32 listenerCount = fListeners.CountItems();
|
int32 listenerCount = fListeners.CountItems();
|
||||||
for (int32 i = listenerCount - 1; i >= 0; i--)
|
for (int32 i = listenerCount - 1; i >= 0; i--)
|
||||||
fListeners.ItemAt(i)->TreeTableNodeInvoked(this, field->Object());
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
BColumnListView::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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#ifndef TREE_TABLE_H
|
#ifndef TREE_TABLE_H
|
||||||
#define TREE_TABLE_H
|
#define TREE_TABLE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <ColumnTypes.h>
|
#include <ColumnTypes.h>
|
||||||
#include <Variant.h>
|
#include <Variant.h>
|
||||||
|
|
||||||
@@ -13,6 +15,51 @@
|
|||||||
|
|
||||||
|
|
||||||
class TreeTable;
|
class TreeTable;
|
||||||
|
class TreeTableModel;
|
||||||
|
class TreeTableNode;
|
||||||
|
class TreeTableRow;
|
||||||
|
|
||||||
|
|
||||||
|
class TreeTablePath {
|
||||||
|
public:
|
||||||
|
TreeTablePath();
|
||||||
|
TreeTablePath(const TreeTablePath& other);
|
||||||
|
TreeTablePath(const TreeTablePath& other,
|
||||||
|
int32 childIndex);
|
||||||
|
~TreeTablePath();
|
||||||
|
|
||||||
|
bool AddComponent(int32 childIndex);
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
int32 CountComponents() const;
|
||||||
|
int32 ComponentAt(int32 index) const;
|
||||||
|
|
||||||
|
TreeTablePath& operator=(const TreeTablePath& other);
|
||||||
|
bool operator==(const TreeTablePath& other) const;
|
||||||
|
bool operator!=(const TreeTablePath& other) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::vector<int32> ComponentVector;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComponentVector fComponents;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TreeTableModelListener {
|
||||||
|
public:
|
||||||
|
virtual ~TreeTableModelListener();
|
||||||
|
|
||||||
|
virtual void TableNodesAdded(TreeTableModel* model,
|
||||||
|
const TreeTablePath& path, int32 childIndex,
|
||||||
|
int32 count);
|
||||||
|
virtual void TableNodesRemoved(TreeTableModel* model,
|
||||||
|
const TreeTablePath& path, int32 childIndex,
|
||||||
|
int32 count);
|
||||||
|
virtual void TableNodesChanged(TreeTableModel* model,
|
||||||
|
const TreeTablePath& path, int32 childIndex,
|
||||||
|
int32 count);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class TreeTableModel : public AbstractTableModelBase {
|
class TreeTableModel : public AbstractTableModelBase {
|
||||||
@@ -27,6 +74,50 @@ public:
|
|||||||
|
|
||||||
virtual bool GetValueAt(void* object, int32 columnIndex,
|
virtual bool GetValueAt(void* object, int32 columnIndex,
|
||||||
BVariant& value) = 0;
|
BVariant& value) = 0;
|
||||||
|
|
||||||
|
virtual void* NodeForPath(const TreeTablePath& path) const;
|
||||||
|
|
||||||
|
virtual bool AddListener(TreeTableModelListener* listener);
|
||||||
|
virtual void RemoveListener(
|
||||||
|
TreeTableModelListener* listener);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef BObjectList<TreeTableModelListener> ListenerList;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void NotifyNodesAdded(const TreeTablePath& path,
|
||||||
|
int32 childIndex, int32 count);
|
||||||
|
void NotifyNodesRemoved(const TreeTablePath& path,
|
||||||
|
int32 childIndex, int32 count);
|
||||||
|
void NotifyNodesChanged(const TreeTablePath& path,
|
||||||
|
int32 childIndex, int32 count);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ListenerList fListeners;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TreeTableSelectionModel {
|
||||||
|
public:
|
||||||
|
TreeTableSelectionModel(TreeTable* table);
|
||||||
|
~TreeTableSelectionModel();
|
||||||
|
|
||||||
|
int32 CountNodes();
|
||||||
|
void* NodeAt(int32 index);
|
||||||
|
bool GetPathAt(int32 index, TreeTablePath& _path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class TreeTable;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _SelectionChanged();
|
||||||
|
void _Update();
|
||||||
|
TreeTableNode* _NodeAt(int32 index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TreeTable* fTreeTable;
|
||||||
|
TreeTableNode** fNodes;
|
||||||
|
int32 fNodeCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -34,12 +125,13 @@ class TreeTableListener {
|
|||||||
public:
|
public:
|
||||||
virtual ~TreeTableListener();
|
virtual ~TreeTableListener();
|
||||||
|
|
||||||
|
virtual void TreeTableSelectionChanged(TreeTable* table);
|
||||||
virtual void TreeTableNodeInvoked(TreeTable* table,
|
virtual void TreeTableNodeInvoked(TreeTable* table,
|
||||||
void* node);
|
const TreeTablePath& path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class TreeTable : public AbstractTable {
|
class TreeTable : public AbstractTable, private TreeTableModelListener {
|
||||||
public:
|
public:
|
||||||
TreeTable(const char* name, uint32 flags,
|
TreeTable(const char* name, uint32 flags,
|
||||||
border_style borderStyle = B_NO_BORDER,
|
border_style borderStyle = B_NO_BORDER,
|
||||||
@@ -50,31 +142,74 @@ public:
|
|||||||
bool showHorizontalScrollbar = true);
|
bool showHorizontalScrollbar = true);
|
||||||
virtual ~TreeTable();
|
virtual ~TreeTable();
|
||||||
|
|
||||||
void SetTreeTableModel(TreeTableModel* model);
|
bool SetTreeTableModel(TreeTableModel* model);
|
||||||
TreeTableModel* GetTreeTableModel() const { return fModel; }
|
TreeTableModel* GetTreeTableModel() const { return fModel; }
|
||||||
|
|
||||||
|
TreeTableSelectionModel* SelectionModel();
|
||||||
|
|
||||||
|
void SelectNode(const TreeTablePath& path,
|
||||||
|
bool extendSelection);
|
||||||
|
void DeselectNode(const TreeTablePath& path);
|
||||||
|
void DeselectAllNodes();
|
||||||
|
|
||||||
|
bool IsNodeExpanded(const TreeTablePath& path) const;
|
||||||
|
void SetNodeExpanded(const TreeTablePath& path,
|
||||||
|
bool expanded,
|
||||||
|
bool expandAncestors = false);
|
||||||
|
|
||||||
|
void ScrollToNode(const TreeTablePath& path);
|
||||||
|
|
||||||
bool AddTreeTableListener(
|
bool AddTreeTableListener(
|
||||||
TreeTableListener* listener);
|
TreeTableListener* listener);
|
||||||
void RemoveTreeTableListener(
|
void RemoveTreeTableListener(
|
||||||
TreeTableListener* listener);
|
TreeTableListener* listener);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void SelectionChanged();
|
||||||
|
|
||||||
virtual AbstractColumn* CreateColumn(TableColumn* column);
|
virtual AbstractColumn* CreateColumn(TableColumn* column);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void TableNodesAdded(TreeTableModel* model,
|
||||||
|
const TreeTablePath& path, int32 childIndex,
|
||||||
|
int32 count);
|
||||||
|
virtual void TableNodesRemoved(TreeTableModel* model,
|
||||||
|
const TreeTablePath& path, int32 childIndex,
|
||||||
|
int32 count);
|
||||||
|
virtual void TableNodesChanged(TreeTableModel* model,
|
||||||
|
const TreeTablePath& path, int32 childIndex,
|
||||||
|
int32 count);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Column;
|
class Column;
|
||||||
|
|
||||||
|
friend class TreeTableSelectionModel;
|
||||||
|
|
||||||
typedef BObjectList<TreeTableListener> ListenerList;
|
typedef BObjectList<TreeTableListener> ListenerList;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void ItemInvoked();
|
virtual void ItemInvoked();
|
||||||
|
|
||||||
void _AddChildRows(void* parent, BRow* parentRow,
|
bool _AddChildRows(TreeTableNode* parentNode,
|
||||||
|
int32 childIndex, int32 count,
|
||||||
int32 columnCount);
|
int32 columnCount);
|
||||||
|
void _RemoveChildRows(TreeTableNode* parentNode,
|
||||||
|
int32 childIndex, int32 count);
|
||||||
|
|
||||||
|
void _SetNodeExpanded(TreeTableNode* node,
|
||||||
|
bool expanded,
|
||||||
|
bool expandAncestors = false);
|
||||||
|
|
||||||
|
TreeTableNode* _NodeForPath(const TreeTablePath& path) const;
|
||||||
|
void _GetPathForNode(TreeTableNode* node,
|
||||||
|
TreeTablePath& _path) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TreeTableModel* fModel;
|
TreeTableModel* fModel;
|
||||||
|
TreeTableNode* fRootNode;
|
||||||
|
TreeTableSelectionModel fSelectionModel;
|
||||||
ListenerList fListeners;
|
ListenerList fListeners;
|
||||||
|
int32 fIgnoreSelectionChange;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user