xref: /haiku/src/apps/debuganalyzer/gui/table/TreeTable.cpp (revision b289aaf66bbf6e173aa90fa194fc256965f1b34d)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "table/TreeTable.h"
7 
8 #include <new>
9 
10 
11 // #pragma mark - TreeTableField
12 
13 
14 class TreeTableField : public BField {
15 public:
16 	TreeTableField(void* object)
17 		:
18 		fObject(object)
19 	{
20 	}
21 
22 	void* Object() const
23 	{
24 		return fObject;
25 	}
26 
27 private:
28 	void*	fObject;
29 };
30 
31 
32 // #pragma mark - TreeTablePath
33 
34 
35 TreeTablePath::TreeTablePath()
36 {
37 }
38 
39 
40 TreeTablePath::TreeTablePath(const TreeTablePath& other)
41 {
42 	*this = other;
43 }
44 
45 
46 TreeTablePath::TreeTablePath(const TreeTablePath& other, int32 childIndex)
47 {
48 	*this = other;
49 	AddComponent(childIndex);
50 }
51 
52 
53 TreeTablePath::~TreeTablePath()
54 {
55 }
56 
57 
58 bool
59 TreeTablePath::AddComponent(int32 childIndex)
60 {
61 	try {
62 		fComponents.push_back(childIndex);
63 		return true;
64 	} catch (...) {
65 		return false;
66 	}
67 }
68 
69 
70 int32
71 TreeTablePath::RemoveLastComponent()
72 {
73 	if (fComponents.empty())
74 		return -1;
75 
76 	int32 index = fComponents.back();
77 	fComponents.pop_back();
78 	return index;
79 }
80 
81 void
82 TreeTablePath::Clear()
83 {
84 	fComponents.clear();
85 }
86 
87 
88 int32
89 TreeTablePath::CountComponents() const
90 {
91 	return fComponents.size();
92 }
93 
94 
95 int32
96 TreeTablePath::ComponentAt(int32 index) const
97 {
98 	if (index < 0 || (size_t)index >= fComponents.size())
99 		return -1;
100 	return fComponents[index];
101 }
102 
103 
104 TreeTablePath&
105 TreeTablePath::operator=(const TreeTablePath& other)
106 {
107 	try {
108 		fComponents = other.fComponents;
109 	} catch (...) {
110 	}
111 	return *this;
112 }
113 
114 
115 bool
116 TreeTablePath::operator==(const TreeTablePath& other) const
117 {
118 	return fComponents == other.fComponents;
119 }
120 
121 
122 bool
123 TreeTablePath::operator!=(const TreeTablePath& other) const
124 {
125 	return fComponents != other.fComponents;
126 }
127 
128 
129 // #pragma mark - TreeTableModelListener
130 
131 
132 TreeTableModelListener::~TreeTableModelListener()
133 {
134 }
135 
136 
137 void
138 TreeTableModelListener::TableNodesAdded(TreeTableModel* model,
139 	const TreeTablePath& path, int32 childIndex, int32 count)
140 {
141 }
142 
143 
144 void
145 TreeTableModelListener::TableNodesRemoved(TreeTableModel* model,
146 	const TreeTablePath& path, int32 childIndex, int32 count)
147 {
148 }
149 
150 
151 void
152 TreeTableModelListener::TableNodesChanged(TreeTableModel* model,
153 	const TreeTablePath& path, int32 childIndex, int32 count)
154 {
155 }
156 
157 
158 // #pragma mark - TreeTableModel
159 
160 
161 TreeTableModel::~TreeTableModel()
162 {
163 }
164 
165 
166 void*
167 TreeTableModel::NodeForPath(const TreeTablePath& path) const
168 {
169 	void* node = Root();
170 
171 	int32 count = path.CountComponents();
172 	for (int32 i = 0; node != NULL && i < count; i++)
173 		node = ChildAt(node, path.ComponentAt(i));
174 
175 	return node;
176 }
177 
178 
179 bool
180 TreeTableModel::AddListener(TreeTableModelListener* listener)
181 {
182 	return fListeners.AddItem(listener);
183 }
184 
185 
186 void
187 TreeTableModel::RemoveListener(TreeTableModelListener* listener)
188 {
189 	fListeners.RemoveItem(listener);
190 }
191 
192 
193 void
194 TreeTableModel::NotifyNodesAdded(const TreeTablePath& path, int32 childIndex,
195 	int32 count)
196 {
197 	int32 listenerCount = fListeners.CountItems();
198 	for (int32 i = listenerCount - 1; i >= 0; i--) {
199 		TreeTableModelListener* listener = fListeners.ItemAt(i);
200 		listener->TableNodesAdded(this, path, childIndex, count);
201 	}
202 }
203 
204 
205 void
206 TreeTableModel::NotifyNodesRemoved(const TreeTablePath& path, int32 childIndex,
207 	int32 count)
208 {
209 	int32 listenerCount = fListeners.CountItems();
210 	for (int32 i = listenerCount - 1; i >= 0; i--) {
211 		TreeTableModelListener* listener = fListeners.ItemAt(i);
212 		listener->TableNodesRemoved(this, path, childIndex, count);
213 	}
214 }
215 
216 
217 void
218 TreeTableModel::NotifyNodesChanged(const TreeTablePath& path, int32 childIndex,
219 	int32 count)
220 {
221 	int32 listenerCount = fListeners.CountItems();
222 	for (int32 i = listenerCount - 1; i >= 0; i--) {
223 		TreeTableModelListener* listener = fListeners.ItemAt(i);
224 		listener->TableNodesChanged(this, path, childIndex, count);
225 	}
226 }
227 
228 
229 // #pragma mark - TreeTableListener
230 
231 
232 TreeTableListener::~TreeTableListener()
233 {
234 }
235 
236 
237 void
238 TreeTableListener::TreeTableSelectionChanged(TreeTable* table)
239 {
240 }
241 
242 
243 void
244 TreeTableListener::TreeTableNodeInvoked(TreeTable* table,
245 	const TreeTablePath& path)
246 {
247 }
248 
249 
250 void
251 TreeTableListener::TreeTableNodeExpandedChanged(TreeTable* table,
252 	const TreeTablePath& path, bool expanded)
253 {
254 }
255 
256 
257 void
258 TreeTableListener::TreeTableCellMouseDown(TreeTable* table,
259 	const TreeTablePath& path, int32 columnIndex, BPoint screenWhere,
260 	uint32 buttons)
261 {
262 }
263 
264 
265 void
266 TreeTableListener::TreeTableCellMouseUp(TreeTable* table,
267 	const TreeTablePath& path, int32 columnIndex, BPoint screenWhere,
268 	uint32 buttons)
269 {
270 }
271 
272 
273 // #pragma mark - Column
274 
275 
276 class TreeTable::Column : public AbstractColumn {
277 public:
278 								Column(TreeTableModel* model,
279 									TableColumn* tableColumn);
280 	virtual						~Column();
281 
282 	virtual	void				SetModel(AbstractTableModelBase* model);
283 
284 protected:
285 	virtual	void				DrawTitle(BRect rect, BView* targetView);
286 	virtual	void				DrawField(BField* field, BRect rect,
287 									BView* targetView);
288 	virtual	int					CompareFields(BField* field1, BField* field2);
289 
290 	virtual	void				GetColumnName(BString* into) const;
291 	virtual	float				GetPreferredWidth(BField* field,
292 									BView* parent) const;
293 
294 private:
295 			TreeTableModel*		fModel;
296 };
297 
298 
299 TreeTable::Column::Column(TreeTableModel* model, TableColumn* tableColumn)
300 	:
301 	AbstractColumn(tableColumn),
302 	fModel(model)
303 {
304 }
305 
306 
307 TreeTable::Column::~Column()
308 {
309 }
310 
311 
312 void
313 TreeTable::Column::SetModel(AbstractTableModelBase* model)
314 {
315 	fModel = dynamic_cast<TreeTableModel*>(model);
316 }
317 
318 
319 void
320 TreeTable::Column::DrawTitle(BRect rect, BView* targetView)
321 {
322 	fTableColumn->DrawTitle(rect, targetView);
323 }
324 
325 
326 void
327 TreeTable::Column::DrawField(BField* _field, BRect rect, BView* targetView)
328 {
329 	TreeTableField* field = dynamic_cast<TreeTableField*>(_field);
330 	if (field == NULL)
331 		return;
332 
333 	int32 modelIndex = fTableColumn->ModelIndex();
334 	BVariant value;
335 	if (!fModel->GetValueAt(field->Object(), modelIndex, value))
336 		return;
337 	fTableColumn->DrawValue(value, rect, targetView);
338 }
339 
340 
341 int
342 TreeTable::Column::CompareFields(BField* _field1, BField* _field2)
343 {
344 	TreeTableField* field1 = dynamic_cast<TreeTableField*>(_field1);
345 	TreeTableField* field2 = dynamic_cast<TreeTableField*>(_field2);
346 
347 	if (field1 == field2)
348 		return 0;
349 
350 	if (field1 == NULL)
351 		return -1;
352 	if (field2 == NULL)
353 		return 1;
354 
355 	int32 modelIndex = fTableColumn->ModelIndex();
356 	BVariant value1;
357 	bool valid1 = fModel->GetValueAt(field1->Object(), modelIndex, value1);
358 	BVariant value2;
359 	bool valid2 = fModel->GetValueAt(field2->Object(), modelIndex, value2);
360 
361 	if (!valid1)
362 		return valid2 ? -1 : 0;
363 	if (!valid2)
364 		return 1;
365 
366 	return fTableColumn->CompareValues(value1, value2);
367 }
368 
369 
370 void
371 TreeTable::Column::GetColumnName(BString* into) const
372 {
373 	fTableColumn->GetColumnName(into);
374 }
375 
376 
377 float
378 TreeTable::Column::GetPreferredWidth(BField* _field, BView* parent) const
379 {
380 	TreeTableField* field = dynamic_cast<TreeTableField*>(_field);
381 	if (field == NULL)
382 		return Width();
383 
384 	int32 modelIndex = fTableColumn->ModelIndex();
385 	BVariant value;
386 	if (!fModel->GetValueAt(field->Object(), modelIndex, value))
387 		return Width();
388 	return fTableColumn->GetPreferredWidth(value, parent);
389 }
390 
391 
392 // #pragma mark - TreeTableRow
393 
394 
395 class TreeTableRow : public BRow {
396 public:
397 	TreeTableRow(TreeTableNode* node)
398 		:
399 		fNode(node)
400 	{
401 	}
402 
403 	TreeTableNode* Node()
404 	{
405 		return fNode;
406 	}
407 
408 private:
409 	TreeTableNode*	fNode;
410 };
411 
412 
413 // #pragma mark - TreeTableNode
414 
415 
416 class TreeTableNode {
417 public:
418 								TreeTableNode(TreeTableNode* parent);
419 								~TreeTableNode();
420 
421 			status_t			Init(void* modelObject, int32 columnCount);
422 			void				DetachRow();
423 
424 			TreeTableNode*		Parent() const	{ return fParent; }
425 			TreeTableRow*		Row() const		{ return fRow; }
426 			void*				ModelObject() const;
427 
428 			bool				AddChild(TreeTableNode* child, int32 index);
429 			TreeTableNode*		RemoveChild(int32 index);
430 
431 			int32				CountChildren() const;
432 			TreeTableNode*		ChildAt(int32 index);
433 			int32				IndexOf(TreeTableNode* child);
434 
435 private:
436 			typedef BObjectList<TreeTableNode> NodeList;
437 
438 private:
439 			TreeTableNode*		fParent;
440 			TreeTableRow*		fRow;
441 			NodeList*			fChildren;
442 };
443 
444 
445 TreeTableNode::TreeTableNode(TreeTableNode* parent)
446 	:
447 	fParent(parent),
448 	fRow(NULL),
449 	fChildren(NULL)
450 {
451 }
452 
453 
454 TreeTableNode::~TreeTableNode()
455 {
456 	delete fChildren;
457 	delete fRow;
458 }
459 
460 
461 status_t
462 TreeTableNode::Init(void* modelObject, int32 columnCount)
463 {
464 	// create row
465 	fRow = new(std::nothrow) TreeTableRow(this);
466 	if (fRow == NULL)
467 		return B_NO_MEMORY;
468 
469 	// add dummy fields to row
470 	for (int32 columnIndex = 0; columnIndex < columnCount; columnIndex++) {
471 		// It would be nice to create only a single field and set it for all
472 		// columns, but the row takes ultimate ownership, so it have to be
473 		// individual objects.
474 		TreeTableField* field = new(std::nothrow) TreeTableField(modelObject);
475 		if (field == NULL)
476 			return B_NO_MEMORY;
477 
478 		fRow->SetField(field, columnIndex);
479 	}
480 
481 	return B_OK;
482 }
483 
484 
485 void
486 TreeTableNode::DetachRow()
487 {
488 
489 	fRow = NULL;
490 
491 	if (fChildren != NULL) {
492 		for (int32 i = 0; TreeTableNode* child = fChildren->ItemAt(i); i++)
493 			child->DetachRow();
494 	}
495 }
496 
497 
498 void*
499 TreeTableNode::ModelObject() const
500 {
501 	TreeTableField* field = dynamic_cast<TreeTableField*>(fRow->GetField(0));
502 	return field->Object();
503 }
504 
505 
506 bool
507 TreeTableNode::AddChild(TreeTableNode* child, int32 index)
508 {
509 	if (fChildren == NULL) {
510 		fChildren = new(std::nothrow) NodeList(10, true);
511 		if (fChildren == NULL)
512 			return false;
513 	}
514 
515 	return fChildren->AddItem(child, index);
516 }
517 
518 
519 TreeTableNode*
520 TreeTableNode::RemoveChild(int32 index)
521 {
522 	return fChildren != NULL ? fChildren->RemoveItemAt(index) : NULL;
523 }
524 
525 
526 int32
527 TreeTableNode::CountChildren() const
528 {
529 	return fChildren != NULL ? fChildren->CountItems() : 0;
530 }
531 
532 
533 TreeTableNode*
534 TreeTableNode::ChildAt(int32 index)
535 {
536 	return fChildren != NULL ? fChildren->ItemAt(index) : NULL;
537 }
538 
539 
540 int32
541 TreeTableNode::IndexOf(TreeTableNode* child)
542 {
543 	return fChildren != NULL ? fChildren->IndexOf(child) : -1;
544 }
545 
546 
547 // #pragma mark - TreeTableSelectionModel
548 
549 
550 TreeTableSelectionModel::TreeTableSelectionModel(TreeTable* table)
551 	:
552 	fTreeTable(table),
553 	fNodes(NULL),
554 	fNodeCount(-1)
555 {
556 }
557 
558 
559 TreeTableSelectionModel::~TreeTableSelectionModel()
560 {
561 	delete[] fNodes;
562 }
563 
564 
565 int32
566 TreeTableSelectionModel::CountNodes()
567 {
568 	_Update();
569 
570 	return fNodeCount;
571 }
572 
573 
574 void*
575 TreeTableSelectionModel::NodeAt(int32 index)
576 {
577 	if (TreeTableNode* node = _NodeAt(index))
578 		return node->ModelObject();
579 	return NULL;
580 }
581 
582 
583 bool
584 TreeTableSelectionModel::GetPathAt(int32 index, TreeTablePath& _path)
585 {
586 	if (TreeTableNode* node = _NodeAt(index)) {
587 		fTreeTable->_GetPathForNode(node, _path);
588 		return true;
589 	}
590 
591 	return false;
592 }
593 
594 
595 void
596 TreeTableSelectionModel::_SelectionChanged()
597 {
598 	if (fNodeCount >= 0) {
599 		fNodeCount = -1;
600 		delete[] fNodes;
601 		fNodes = NULL;
602 	}
603 }
604 
605 
606 void
607 TreeTableSelectionModel::_Update()
608 {
609 	if (fNodeCount >= 0)
610 		return;
611 
612 	// count the nodes
613 	fNodeCount = 0;
614 	BRow* row = NULL;
615 	while ((row = fTreeTable->CurrentSelection(row)) != NULL)
616 		fNodeCount++;
617 
618 	if (fNodeCount == 0)
619 		return;
620 
621 	// allocate node array
622 	fNodes = new(std::nothrow) TreeTableNode*[fNodeCount];
623 	if (fNodes == NULL) {
624 		fNodeCount = 0;
625 		return;
626 	}
627 
628 	// get the nodes
629 	row = NULL;
630 	int32 index = 0;
631 	while ((row = fTreeTable->CurrentSelection(row)) != NULL)
632 		fNodes[index++] = dynamic_cast<TreeTableRow*>(row)->Node();
633 }
634 
635 
636 TreeTableNode*
637 TreeTableSelectionModel::_NodeAt(int32 index)
638 {
639 	_Update();
640 
641 	return index >= 0 && index < fNodeCount ? fNodes[index] : NULL;
642 }
643 
644 
645 // #pragma mark - Table
646 
647 
648 TreeTable::TreeTable(const char* name, uint32 flags, border_style borderStyle,
649 	bool showHorizontalScrollbar)
650 	:
651 	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
652 	fModel(NULL),
653 	fRootNode(NULL),
654 	fSelectionModel(this),
655 	fIgnoreSelectionChange(0)
656 {
657 }
658 
659 
660 TreeTable::TreeTable(TreeTableModel* model, const char* name, uint32 flags,
661 	border_style borderStyle, bool showHorizontalScrollbar)
662 	:
663 	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
664 	fModel(NULL),
665 	fRootNode(NULL),
666 	fSelectionModel(this),
667 	fIgnoreSelectionChange(0)
668 {
669 	SetTreeTableModel(model);
670 }
671 
672 
673 TreeTable::~TreeTable()
674 {
675 	SetTreeTableModel(NULL);
676 
677 	for (int32 i = CountColumns() - 1; i >= 0; i--)
678 		RemoveColumn(ColumnAt(i));
679 }
680 
681 
682 bool
683 TreeTable::SetTreeTableModel(TreeTableModel* model)
684 {
685 	if (model == fModel)
686 		return true;
687 
688 	if (fModel != NULL) {
689 		fModel->RemoveListener(this);
690 
691 		if (fRootNode != NULL) {
692 			fRootNode->DetachRow();
693 			delete fRootNode;
694 			fRootNode = NULL;
695 		}
696 
697 		Clear();
698 
699 		for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
700 			column->SetModel(NULL);
701 	}
702 
703 	fModel = model;
704 
705 	if (fModel == NULL)
706 		return true;
707 
708 	fRootNode = new(std::nothrow) TreeTableNode(NULL);
709 	if (fRootNode == NULL)
710 		return false;
711 
712 	if (fRootNode->Init(fModel->Root(), fModel->CountColumns()) != B_OK) {
713 		delete fRootNode;
714 		fRootNode = NULL;
715 		return false;
716 	}
717 
718 	fModel->AddListener(this);
719 
720 	for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
721 		column->SetModel(fModel);
722 
723 	// recursively create the rows
724 	if (!_AddChildRows(fRootNode, 0, fModel->CountChildren(fModel->Root()),
725 			fModel->CountColumns())) {
726 		SetTreeTableModel(NULL);
727 		return false;
728 	}
729 
730 	return true;
731 }
732 
733 
734 TreeTableSelectionModel*
735 TreeTable::SelectionModel()
736 {
737 	return &fSelectionModel;
738 }
739 
740 
741 void
742 TreeTable::SelectNode(const TreeTablePath& path, bool extendSelection)
743 {
744 	TreeTableNode* node = _NodeForPath(path);
745 	if (node == NULL)
746 		return;
747 
748 	if (!extendSelection) {
749 		fIgnoreSelectionChange++;
750 		DeselectAll();
751 		fIgnoreSelectionChange--;
752 	}
753 
754 	AddToSelection(node->Row());
755 }
756 
757 
758 void
759 TreeTable::DeselectNode(const TreeTablePath& path)
760 {
761 	if (TreeTableNode* node = _NodeForPath(path))
762 		Deselect(node->Row());
763 }
764 
765 
766 void
767 TreeTable::DeselectAllNodes()
768 {
769 	DeselectAll();
770 }
771 
772 
773 bool
774 TreeTable::IsNodeExpanded(const TreeTablePath& path) const
775 {
776 	if (TreeTableNode* node = _NodeForPath(path))
777 		return node->Row()->IsExpanded();
778 	return false;
779 }
780 
781 
782 void
783 TreeTable::SetNodeExpanded(const TreeTablePath& path, bool expanded,
784 	bool expandAncestors)
785 {
786 	if (TreeTableNode* node = _NodeForPath(path))
787 		_SetNodeExpanded(node, expanded, expandAncestors);
788 }
789 
790 
791 void
792 TreeTable::ScrollToNode(const TreeTablePath& path)
793 {
794 	if (TreeTableNode* node = _NodeForPath(path))
795 		BColumnListView::ScrollTo(node->Row());
796 }
797 
798 
799 bool
800 TreeTable::AddTreeTableListener(TreeTableListener* listener)
801 {
802 	return fListeners.AddItem(listener);
803 }
804 
805 
806 void
807 TreeTable::RemoveTreeTableListener(TreeTableListener* listener)
808 {
809 	fListeners.RemoveItem(listener);
810 }
811 
812 
813 void
814 TreeTable::SelectionChanged()
815 {
816 	if (fIgnoreSelectionChange > 0)
817 		return;
818 
819 	fSelectionModel._SelectionChanged();
820 
821 	if (!fListeners.IsEmpty()) {
822 		int32 listenerCount = fListeners.CountItems();
823 		for (int32 i = listenerCount - 1; i >= 0; i--)
824 			fListeners.ItemAt(i)->TreeTableSelectionChanged(this);
825 	}
826 }
827 
828 
829 AbstractTable::AbstractColumn*
830 TreeTable::CreateColumn(TableColumn* column)
831 {
832 	return new Column(fModel, column);
833 }
834 
835 
836 void
837 TreeTable::ColumnMouseDown(AbstractColumn* column, BRow* _row, BField* field,
838 	BPoint screenWhere, uint32 buttons)
839 {
840 	if (!fListeners.IsEmpty()) {
841 		// get the table node and the column index
842 		TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
843 		int32 columnIndex = column->LogicalFieldNum();
844 		if (row == NULL || columnIndex < 0)
845 			return;
846 
847 		// get the node path
848 		TreeTablePath path;
849 		_GetPathForNode(row->Node(), path);
850 
851 		// notify listeners
852 		int32 listenerCount = fListeners.CountItems();
853 		for (int32 i = listenerCount - 1; i >= 0; i--) {
854 			fListeners.ItemAt(i)->TreeTableCellMouseDown(this, path,
855 				columnIndex, screenWhere, buttons);
856 		}
857 	}
858 }
859 
860 
861 void
862 TreeTable::ColumnMouseUp(AbstractColumn* column, BRow* _row, BField* field,
863 	BPoint screenWhere, uint32 buttons)
864 {
865 	if (!fListeners.IsEmpty()) {
866 		// get the table node and the column index
867 		TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
868 		int32 columnIndex = column->LogicalFieldNum();
869 		if (row == NULL || columnIndex < 0)
870 			return;
871 
872 		// get the node path
873 		TreeTablePath path;
874 		_GetPathForNode(row->Node(), path);
875 
876 		// notify listeners
877 		int32 listenerCount = fListeners.CountItems();
878 		for (int32 i = listenerCount - 1; i >= 0; i--) {
879 			fListeners.ItemAt(i)->TreeTableCellMouseUp(this, path, columnIndex,
880 				screenWhere, buttons);
881 		}
882 	}
883 }
884 
885 
886 void
887 TreeTable::TableNodesAdded(TreeTableModel* model, const TreeTablePath& path,
888 	int32 childIndex, int32 count)
889 {
890 	TreeTableNode* node = _NodeForPath(path);
891 	if (node == NULL)
892 		return;
893 
894 	_AddChildRows(node, childIndex, count, fModel->CountColumns());
895 }
896 
897 
898 void
899 TreeTable::TableNodesRemoved(TreeTableModel* model, const TreeTablePath& path,
900 	int32 childIndex, int32 count)
901 {
902 	TreeTableNode* node = _NodeForPath(path);
903 	if (node == NULL)
904 		return;
905 
906 	_RemoveChildRows(node, childIndex, count);
907 }
908 
909 
910 void
911 TreeTable::TableNodesChanged(TreeTableModel* model, const TreeTablePath& path,
912 	int32 childIndex, int32 count)
913 {
914 	TreeTableNode* node = _NodeForPath(path);
915 	if (node == NULL)
916 		return;
917 
918 	int32 endIndex = childIndex + count;
919 	for (int32 i = childIndex; i < endIndex; i++) {
920 		if (TreeTableNode* child = node->ChildAt(i))
921 			UpdateRow(child->Row());
922 	}
923 }
924 
925 
926 void
927 TreeTable::ExpandOrCollapse(BRow* _row, bool expand)
928 {
929 	TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
930 	if (row == NULL || row->IsExpanded() == expand)
931 		return;
932 
933 	AbstractTable::ExpandOrCollapse(row, expand);
934 
935 	if (row->IsExpanded() != expand)
936 		return;
937 
938 	TreeTablePath path;
939 	_GetPathForNode(row->Node(), path);
940 
941 	int32 listenerCount = fListeners.CountItems();
942 	for (int32 i = listenerCount - 1; i >= 0; i--)
943 		fListeners.ItemAt(i)->TreeTableNodeExpandedChanged(this, path, expand);
944 }
945 
946 
947 void
948 TreeTable::ItemInvoked()
949 {
950 	if (fListeners.IsEmpty())
951 		return;
952 
953 	TreeTableRow* row = dynamic_cast<TreeTableRow*>(CurrentSelection());
954 	if (row == NULL)
955 		return;
956 
957 	TreeTablePath path;
958 	_GetPathForNode(row->Node(), path);
959 
960 	int32 listenerCount = fListeners.CountItems();
961 	for (int32 i = listenerCount - 1; i >= 0; i--)
962 		fListeners.ItemAt(i)->TreeTableNodeInvoked(this, path);
963 }
964 
965 
966 bool
967 TreeTable::_AddChildRows(TreeTableNode* parentNode, int32 childIndex,
968 	int32 count, int32 columnCount)
969 {
970 	int32 childEndIndex = childIndex + count;
971 	for (int32 i = childIndex; i < childEndIndex; i++) {
972 		void* child = fModel->ChildAt(parentNode->ModelObject(), i);
973 
974 		// create node
975 		TreeTableNode* node = new(std::nothrow) TreeTableNode(parentNode);
976 		if (node == NULL || node->Init(child, columnCount) != B_OK
977 			|| !parentNode->AddChild(node, i)) {
978 			delete node;
979 			return false;
980 		}
981 
982 		// add row
983 		AddRow(node->Row(), i,
984 			parentNode != fRootNode ? parentNode->Row() : NULL);
985 
986 		// recursively create children
987 		if (!_AddChildRows(node, 0, fModel->CountChildren(child), columnCount))
988 			return false;
989 	}
990 
991 	return true;
992 }
993 
994 
995 void
996 TreeTable::_RemoveChildRows(TreeTableNode* parentNode, int32 childIndex,
997 	int32 count)
998 {
999 	for (int32 i = childIndex + count - 1; i >= childIndex; i--) {
1000 		if (TreeTableNode* child = parentNode->RemoveChild(i)) {
1001 			int32 childCount = child->CountChildren();
1002 			if (childCount > 0)
1003 				_RemoveChildRows(child, 0, childCount);
1004 
1005 			RemoveRow(child->Row());
1006 			delete child;
1007 		}
1008 	}
1009 }
1010 
1011 
1012 void
1013 TreeTable::_SetNodeExpanded(TreeTableNode* node, bool expanded,
1014 	bool expandAncestors)
1015 {
1016 	if (expanded && expandAncestors && node != fRootNode)
1017 		_SetNodeExpanded(node->Parent(), true, true);
1018 
1019 	ExpandOrCollapse(node->Row(), expanded);
1020 }
1021 
1022 
1023 TreeTableNode*
1024 TreeTable::_NodeForPath(const TreeTablePath& path) const
1025 {
1026 	TreeTableNode* node = fRootNode;
1027 
1028 	int32 count = path.CountComponents();
1029 	for (int32 i = 0; node != NULL && i < count; i++)
1030 		node = node->ChildAt(path.ComponentAt(i));
1031 
1032 	return node;
1033 }
1034 
1035 
1036 void
1037 TreeTable::_GetPathForNode(TreeTableNode* node, TreeTablePath& _path) const
1038 {
1039 	if (node == fRootNode) {
1040 		_path.Clear();
1041 		return;
1042 	}
1043 
1044 	_GetPathForNode(node->Parent(), _path);
1045 	_path.AddComponent(node->Parent()->IndexOf(node));
1046 }
1047