diff --git a/src/apps/debuganalyzer/gui/table/TreeTable.cpp b/src/apps/debuganalyzer/gui/table/TreeTable.cpp index 82716af7d8..d08637321d 100644 --- a/src/apps/debuganalyzer/gui/table/TreeTable.cpp +++ b/src/apps/debuganalyzer/gui/table/TreeTable.cpp @@ -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 @@ -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 @@ -46,7 +223,14 @@ TreeTableListener::~TreeTableListener() 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 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(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(row)->Node(); +} + + +TreeTableNode* +TreeTableSelectionModel::_NodeAt(int32 index) +{ + _Update(); + + return index >= 0 && index < fNodeCount ? fNodes[index] : NULL; +} + + // #pragma mark - Table @@ -177,7 +614,10 @@ TreeTable::TreeTable(const char* name, uint32 flags, border_style borderStyle, bool 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) : AbstractTable(name, flags, borderStyle, showHorizontalScrollbar), - fModel(NULL) + fModel(NULL), + fRootNode(NULL), + fSelectionModel(this), + fIgnoreSelectionChange(0) { SetTreeTableModel(model); } @@ -194,18 +637,28 @@ TreeTable::TreeTable(TreeTableModel* model, const char* name, uint32 flags, TreeTable::~TreeTable() { + SetTreeTableModel(NULL); + for (int32 i = CountColumns() - 1; i >= 0; i--) RemoveColumn(ColumnAt(i)); } -void +bool TreeTable::SetTreeTableModel(TreeTableModel* model) { if (model == fModel) - return; + 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++) @@ -215,50 +668,96 @@ TreeTable::SetTreeTableModel(TreeTableModel* model) fModel = model; 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++) column->SetModel(fModel); // 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 -TreeTable::_AddChildRows(void* parent, BRow* parentRow, int32 columnCount) +TreeTable::SelectNode(const TreeTablePath& path, bool extendSelection) { - int32 childCount = fModel->CountChildren(parent); - for (int32 i = 0; i < childCount; i++) { - void* child = fModel->ChildAt(parent, i); + TreeTableNode* node = _NodeForPath(path); + if (node == NULL) + return; - // create row - BRow* row = new(std::nothrow) BRow(); - if (row == NULL) { - // TODO: Report error! - 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); + 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)) + 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* 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 TreeTable::ItemInvoked() { if (fListeners.IsEmpty()) return; - BRow* row = CurrentSelection(); + TreeTableRow* row = dynamic_cast(CurrentSelection()); if (row == NULL) return; - TreeTableField* field = dynamic_cast(row->GetField(0)); - if (field == 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, 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)); } diff --git a/src/apps/debuganalyzer/gui/table/TreeTable.h b/src/apps/debuganalyzer/gui/table/TreeTable.h index 72c650da04..669ad78b79 100644 --- a/src/apps/debuganalyzer/gui/table/TreeTable.h +++ b/src/apps/debuganalyzer/gui/table/TreeTable.h @@ -5,6 +5,8 @@ #ifndef TREE_TABLE_H #define TREE_TABLE_H +#include + #include #include @@ -13,6 +15,51 @@ 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 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 { @@ -27,6 +74,50 @@ public: virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value) = 0; + + virtual void* NodeForPath(const TreeTablePath& path) const; + + virtual bool AddListener(TreeTableModelListener* listener); + virtual void RemoveListener( + TreeTableModelListener* listener); + +protected: + typedef BObjectList 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: virtual ~TreeTableListener(); + virtual void TreeTableSelectionChanged(TreeTable* table); virtual void TreeTableNodeInvoked(TreeTable* table, - void* node); + const TreeTablePath& path); }; -class TreeTable : public AbstractTable { +class TreeTable : public AbstractTable, private TreeTableModelListener { public: TreeTable(const char* name, uint32 flags, border_style borderStyle = B_NO_BORDER, @@ -50,31 +142,74 @@ public: bool showHorizontalScrollbar = true); virtual ~TreeTable(); - void SetTreeTableModel(TreeTableModel* model); + bool SetTreeTableModel(TreeTableModel* model); 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( TreeTableListener* listener); void RemoveTreeTableListener( TreeTableListener* listener); protected: + virtual void SelectionChanged(); + 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: class Column; + friend class TreeTableSelectionModel; + typedef BObjectList ListenerList; private: virtual void ItemInvoked(); - void _AddChildRows(void* parent, BRow* parentRow, + bool _AddChildRows(TreeTableNode* parentNode, + int32 childIndex, int32 count, 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: TreeTableModel* fModel; + TreeTableNode* fRootNode; + TreeTableSelectionModel fSelectionModel; ListenerList fListeners; + int32 fIgnoreSelectionChange; };