xref: /haiku/src/apps/debuganalyzer/gui/table/TreeTable.cpp (revision 03187b607b2b5eec7ee059f1ead09bdba14991fb)
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 // #pragma mark - Column
258 
259 
260 class TreeTable::Column : public AbstractColumn {
261 public:
262 								Column(TreeTableModel* model,
263 									TableColumn* tableColumn);
264 	virtual						~Column();
265 
266 	virtual	void				SetModel(AbstractTableModelBase* model);
267 
268 protected:
269 	virtual	void				DrawTitle(BRect rect, BView* targetView);
270 	virtual	void				DrawField(BField* field, BRect rect,
271 									BView* targetView);
272 	virtual	int					CompareFields(BField* field1, BField* field2);
273 
274 	virtual	void				GetColumnName(BString* into) const;
275 	virtual	float				GetPreferredWidth(BField* field,
276 									BView* parent) const;
277 
278 private:
279 			TreeTableModel*		fModel;
280 };
281 
282 
283 TreeTable::Column::Column(TreeTableModel* model, TableColumn* tableColumn)
284 	:
285 	AbstractColumn(tableColumn),
286 	fModel(model)
287 {
288 }
289 
290 
291 TreeTable::Column::~Column()
292 {
293 }
294 
295 
296 void
297 TreeTable::Column::SetModel(AbstractTableModelBase* model)
298 {
299 	fModel = dynamic_cast<TreeTableModel*>(model);
300 }
301 
302 
303 void
304 TreeTable::Column::DrawTitle(BRect rect, BView* targetView)
305 {
306 	fTableColumn->DrawTitle(rect, targetView);
307 }
308 
309 
310 void
311 TreeTable::Column::DrawField(BField* _field, BRect rect, BView* targetView)
312 {
313 	TreeTableField* field = dynamic_cast<TreeTableField*>(_field);
314 	if (field == NULL)
315 		return;
316 
317 	int32 modelIndex = fTableColumn->ModelIndex();
318 	BVariant value;
319 	if (!fModel->GetValueAt(field->Object(), modelIndex, value))
320 		return;
321 	fTableColumn->DrawValue(value, rect, targetView);
322 }
323 
324 
325 int
326 TreeTable::Column::CompareFields(BField* _field1, BField* _field2)
327 {
328 	TreeTableField* field1 = dynamic_cast<TreeTableField*>(_field1);
329 	TreeTableField* field2 = dynamic_cast<TreeTableField*>(_field2);
330 
331 	if (field1 == field2)
332 		return 0;
333 
334 	if (field1 == NULL)
335 		return -1;
336 	if (field2 == NULL)
337 		return 1;
338 
339 	int32 modelIndex = fTableColumn->ModelIndex();
340 	BVariant value1;
341 	bool valid1 = fModel->GetValueAt(field1->Object(), modelIndex, value1);
342 	BVariant value2;
343 	bool valid2 = fModel->GetValueAt(field2->Object(), modelIndex, value2);
344 
345 	if (!valid1)
346 		return valid2 ? -1 : 0;
347 	if (!valid2)
348 		return 1;
349 
350 	return fTableColumn->CompareValues(value1, value2);
351 }
352 
353 
354 void
355 TreeTable::Column::GetColumnName(BString* into) const
356 {
357 	fTableColumn->GetColumnName(into);
358 }
359 
360 
361 float
362 TreeTable::Column::GetPreferredWidth(BField* _field, BView* parent) const
363 {
364 	TreeTableField* field = dynamic_cast<TreeTableField*>(_field);
365 	if (field == NULL)
366 		return Width();
367 
368 	int32 modelIndex = fTableColumn->ModelIndex();
369 	BVariant value;
370 	if (!fModel->GetValueAt(field->Object(), modelIndex, value))
371 		return Width();
372 	return fTableColumn->GetPreferredWidth(value, parent);
373 }
374 
375 
376 // #pragma mark - TreeTableRow
377 
378 
379 class TreeTableRow : public BRow {
380 public:
381 	TreeTableRow(TreeTableNode* node)
382 		:
383 		fNode(node)
384 	{
385 	}
386 
387 	TreeTableNode* Node()
388 	{
389 		return fNode;
390 	}
391 
392 private:
393 	TreeTableNode*	fNode;
394 };
395 
396 
397 // #pragma mark - TreeTableNode
398 
399 
400 class TreeTableNode {
401 public:
402 								TreeTableNode(TreeTableNode* parent);
403 								~TreeTableNode();
404 
405 			status_t			Init(void* modelObject, int32 columnCount);
406 			void				DetachRow();
407 
408 			TreeTableNode*		Parent() const	{ return fParent; }
409 			TreeTableRow*		Row() const		{ return fRow; }
410 			void*				ModelObject() const;
411 
412 			bool				AddChild(TreeTableNode* child, int32 index);
413 			TreeTableNode*		RemoveChild(int32 index);
414 
415 			int32				CountChildren() const;
416 			TreeTableNode*		ChildAt(int32 index);
417 			int32				IndexOf(TreeTableNode* child);
418 
419 private:
420 			typedef BObjectList<TreeTableNode> NodeList;
421 
422 private:
423 			TreeTableNode*		fParent;
424 			TreeTableRow*		fRow;
425 			NodeList*			fChildren;
426 };
427 
428 
429 TreeTableNode::TreeTableNode(TreeTableNode* parent)
430 	:
431 	fParent(parent),
432 	fRow(NULL),
433 	fChildren(NULL)
434 {
435 }
436 
437 
438 TreeTableNode::~TreeTableNode()
439 {
440 	delete fChildren;
441 	delete fRow;
442 }
443 
444 
445 status_t
446 TreeTableNode::Init(void* modelObject, int32 columnCount)
447 {
448 	// create row
449 	fRow = new(std::nothrow) TreeTableRow(this);
450 	if (fRow == NULL)
451 		return B_NO_MEMORY;
452 
453 	// add dummy fields to row
454 	for (int32 columnIndex = 0; columnIndex < columnCount; columnIndex++) {
455 		// It would be nice to create only a single field and set it for all
456 		// columns, but the row takes ultimate ownership, so it have to be
457 		// individual objects.
458 		TreeTableField* field = new(std::nothrow) TreeTableField(modelObject);
459 		if (field == NULL)
460 			return B_NO_MEMORY;
461 
462 		fRow->SetField(field, columnIndex);
463 	}
464 
465 	return B_OK;
466 }
467 
468 
469 void
470 TreeTableNode::DetachRow()
471 {
472 
473 	fRow = NULL;
474 
475 	if (fChildren != NULL) {
476 		for (int32 i = 0; TreeTableNode* child = fChildren->ItemAt(i); i++)
477 			child->DetachRow();
478 	}
479 }
480 
481 
482 void*
483 TreeTableNode::ModelObject() const
484 {
485 	TreeTableField* field = dynamic_cast<TreeTableField*>(fRow->GetField(0));
486 	return field->Object();
487 }
488 
489 
490 bool
491 TreeTableNode::AddChild(TreeTableNode* child, int32 index)
492 {
493 	if (fChildren == NULL) {
494 		fChildren = new(std::nothrow) NodeList(10, true);
495 		if (fChildren == NULL)
496 			return false;
497 	}
498 
499 	return fChildren->AddItem(child, index);
500 }
501 
502 
503 TreeTableNode*
504 TreeTableNode::RemoveChild(int32 index)
505 {
506 	return fChildren != NULL ? fChildren->RemoveItemAt(index) : NULL;
507 }
508 
509 
510 int32
511 TreeTableNode::CountChildren() const
512 {
513 	return fChildren != NULL ? fChildren->CountItems() : 0;
514 }
515 
516 
517 TreeTableNode*
518 TreeTableNode::ChildAt(int32 index)
519 {
520 	return fChildren != NULL ? fChildren->ItemAt(index) : NULL;
521 }
522 
523 
524 int32
525 TreeTableNode::IndexOf(TreeTableNode* child)
526 {
527 	return fChildren != NULL ? fChildren->IndexOf(child) : -1;
528 }
529 
530 
531 // #pragma mark - TreeTableSelectionModel
532 
533 
534 TreeTableSelectionModel::TreeTableSelectionModel(TreeTable* table)
535 	:
536 	fTreeTable(table),
537 	fNodes(NULL),
538 	fNodeCount(-1)
539 {
540 }
541 
542 
543 TreeTableSelectionModel::~TreeTableSelectionModel()
544 {
545 	delete[] fNodes;
546 }
547 
548 
549 int32
550 TreeTableSelectionModel::CountNodes()
551 {
552 	_Update();
553 
554 	return fNodeCount;
555 }
556 
557 
558 void*
559 TreeTableSelectionModel::NodeAt(int32 index)
560 {
561 	if (TreeTableNode* node = _NodeAt(index))
562 		return node->ModelObject();
563 	return NULL;
564 }
565 
566 
567 bool
568 TreeTableSelectionModel::GetPathAt(int32 index, TreeTablePath& _path)
569 {
570 	if (TreeTableNode* node = _NodeAt(index)) {
571 		fTreeTable->_GetPathForNode(node, _path);
572 		return true;
573 	}
574 
575 	return false;
576 }
577 
578 
579 void
580 TreeTableSelectionModel::_SelectionChanged()
581 {
582 	if (fNodeCount >= 0) {
583 		fNodeCount = -1;
584 		delete[] fNodes;
585 		fNodes = NULL;
586 	}
587 }
588 
589 
590 void
591 TreeTableSelectionModel::_Update()
592 {
593 	if (fNodeCount >= 0)
594 		return;
595 
596 	// count the nodes
597 	fNodeCount = 0;
598 	BRow* row = NULL;
599 	while ((row = fTreeTable->CurrentSelection(row)) != NULL)
600 		fNodeCount++;
601 
602 	if (fNodeCount == 0)
603 		return;
604 
605 	// allocate node array
606 	fNodes = new(std::nothrow) TreeTableNode*[fNodeCount];
607 	if (fNodes == NULL) {
608 		fNodeCount = 0;
609 		return;
610 	}
611 
612 	// get the nodes
613 	row = NULL;
614 	int32 index = 0;
615 	while ((row = fTreeTable->CurrentSelection(row)) != NULL)
616 		fNodes[index++] = dynamic_cast<TreeTableRow*>(row)->Node();
617 }
618 
619 
620 TreeTableNode*
621 TreeTableSelectionModel::_NodeAt(int32 index)
622 {
623 	_Update();
624 
625 	return index >= 0 && index < fNodeCount ? fNodes[index] : NULL;
626 }
627 
628 
629 // #pragma mark - Table
630 
631 
632 TreeTable::TreeTable(const char* name, uint32 flags, border_style borderStyle,
633 	bool showHorizontalScrollbar)
634 	:
635 	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
636 	fModel(NULL),
637 	fRootNode(NULL),
638 	fSelectionModel(this),
639 	fIgnoreSelectionChange(0)
640 {
641 }
642 
643 
644 TreeTable::TreeTable(TreeTableModel* model, const char* name, uint32 flags,
645 	border_style borderStyle, bool showHorizontalScrollbar)
646 	:
647 	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
648 	fModel(NULL),
649 	fRootNode(NULL),
650 	fSelectionModel(this),
651 	fIgnoreSelectionChange(0)
652 {
653 	SetTreeTableModel(model);
654 }
655 
656 
657 TreeTable::~TreeTable()
658 {
659 	SetTreeTableModel(NULL);
660 
661 	for (int32 i = CountColumns() - 1; i >= 0; i--)
662 		RemoveColumn(ColumnAt(i));
663 }
664 
665 
666 bool
667 TreeTable::SetTreeTableModel(TreeTableModel* model)
668 {
669 	if (model == fModel)
670 		return true;
671 
672 	if (fModel != NULL) {
673 		fModel->RemoveListener(this);
674 
675 		if (fRootNode != NULL) {
676 			fRootNode->DetachRow();
677 			delete fRootNode;
678 			fRootNode = NULL;
679 		}
680 
681 		Clear();
682 
683 		for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
684 			column->SetModel(NULL);
685 	}
686 
687 	fModel = model;
688 
689 	if (fModel == NULL)
690 		return true;
691 
692 	fRootNode = new(std::nothrow) TreeTableNode(NULL);
693 	if (fRootNode == NULL)
694 		return false;
695 
696 	if (fRootNode->Init(fModel->Root(), fModel->CountColumns()) != B_OK) {
697 		delete fRootNode;
698 		fRootNode = NULL;
699 		return false;
700 	}
701 
702 	fModel->AddListener(this);
703 
704 	for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
705 		column->SetModel(fModel);
706 
707 	// recursively create the rows
708 	if (!_AddChildRows(fRootNode, 0, fModel->CountChildren(fModel->Root()),
709 			fModel->CountColumns())) {
710 		SetTreeTableModel(NULL);
711 		return false;
712 	}
713 
714 	return true;
715 }
716 
717 
718 TreeTableSelectionModel*
719 TreeTable::SelectionModel()
720 {
721 	return &fSelectionModel;
722 }
723 
724 
725 void
726 TreeTable::SelectNode(const TreeTablePath& path, bool extendSelection)
727 {
728 	TreeTableNode* node = _NodeForPath(path);
729 	if (node == NULL)
730 		return;
731 
732 	if (!extendSelection) {
733 		fIgnoreSelectionChange++;
734 		DeselectAll();
735 		fIgnoreSelectionChange--;
736 	}
737 
738 	AddToSelection(node->Row());
739 }
740 
741 
742 void
743 TreeTable::DeselectNode(const TreeTablePath& path)
744 {
745 	if (TreeTableNode* node = _NodeForPath(path))
746 		Deselect(node->Row());
747 }
748 
749 
750 void
751 TreeTable::DeselectAllNodes()
752 {
753 	DeselectAll();
754 }
755 
756 
757 bool
758 TreeTable::IsNodeExpanded(const TreeTablePath& path) const
759 {
760 	if (TreeTableNode* node = _NodeForPath(path))
761 		node->Row()->IsExpanded();
762 	return false;
763 }
764 
765 
766 void
767 TreeTable::SetNodeExpanded(const TreeTablePath& path, bool expanded,
768 	bool expandAncestors)
769 {
770 	if (TreeTableNode* node = _NodeForPath(path))
771 		_SetNodeExpanded(node, expanded, expandAncestors);
772 }
773 
774 
775 void
776 TreeTable::ScrollToNode(const TreeTablePath& path)
777 {
778 	if (TreeTableNode* node = _NodeForPath(path))
779 		BColumnListView::ScrollTo(node->Row());
780 }
781 
782 
783 bool
784 TreeTable::AddTreeTableListener(TreeTableListener* listener)
785 {
786 	return fListeners.AddItem(listener);
787 }
788 
789 
790 void
791 TreeTable::RemoveTreeTableListener(TreeTableListener* listener)
792 {
793 	fListeners.RemoveItem(listener);
794 }
795 
796 
797 void
798 TreeTable::SelectionChanged()
799 {
800 	if (fIgnoreSelectionChange > 0)
801 		return;
802 
803 	fSelectionModel._SelectionChanged();
804 
805 	if (!fListeners.IsEmpty()) {
806 		int32 listenerCount = fListeners.CountItems();
807 		for (int32 i = listenerCount - 1; i >= 0; i--)
808 			fListeners.ItemAt(i)->TreeTableSelectionChanged(this);
809 	}
810 }
811 
812 
813 AbstractTable::AbstractColumn*
814 TreeTable::CreateColumn(TableColumn* column)
815 {
816 	return new Column(fModel, column);
817 }
818 
819 
820 void
821 TreeTable::TableNodesAdded(TreeTableModel* model, const TreeTablePath& path,
822 	int32 childIndex, int32 count)
823 {
824 	TreeTableNode* node = _NodeForPath(path);
825 	if (node == NULL)
826 		return;
827 
828 	_AddChildRows(node, childIndex, count, fModel->CountColumns());
829 }
830 
831 
832 void
833 TreeTable::TableNodesRemoved(TreeTableModel* model, const TreeTablePath& path,
834 	int32 childIndex, int32 count)
835 {
836 	TreeTableNode* node = _NodeForPath(path);
837 	if (node == NULL)
838 		return;
839 
840 	_RemoveChildRows(node, childIndex, count);
841 }
842 
843 
844 void
845 TreeTable::TableNodesChanged(TreeTableModel* model, const TreeTablePath& path,
846 	int32 childIndex, int32 count)
847 {
848 	TreeTableNode* node = _NodeForPath(path);
849 	if (node == NULL)
850 		return;
851 
852 	int32 endIndex = childIndex + count;
853 	for (int32 i = childIndex; i < endIndex; i++) {
854 		if (TreeTableNode* child = node->ChildAt(i))
855 			UpdateRow(child->Row());
856 	}
857 }
858 
859 
860 void
861 TreeTable::ExpandOrCollapse(BRow* _row, bool expand)
862 {
863 	TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
864 	if (row == NULL || row->IsExpanded() == expand)
865 		return;
866 
867 	AbstractTable::ExpandOrCollapse(row, expand);
868 
869 	if (row->IsExpanded() != expand)
870 		return;
871 
872 	TreeTablePath path;
873 	_GetPathForNode(row->Node(), path);
874 
875 	int32 listenerCount = fListeners.CountItems();
876 	for (int32 i = listenerCount - 1; i >= 0; i--)
877 		fListeners.ItemAt(i)->TreeTableNodeExpandedChanged(this, path, expand);
878 }
879 
880 
881 void
882 TreeTable::ItemInvoked()
883 {
884 	if (fListeners.IsEmpty())
885 		return;
886 
887 	TreeTableRow* row = dynamic_cast<TreeTableRow*>(CurrentSelection());
888 	if (row == NULL)
889 		return;
890 
891 	TreeTablePath path;
892 	_GetPathForNode(row->Node(), path);
893 
894 	int32 listenerCount = fListeners.CountItems();
895 	for (int32 i = listenerCount - 1; i >= 0; i--)
896 		fListeners.ItemAt(i)->TreeTableNodeInvoked(this, path);
897 }
898 
899 
900 bool
901 TreeTable::_AddChildRows(TreeTableNode* parentNode, int32 childIndex,
902 	int32 count, int32 columnCount)
903 {
904 	int32 childEndIndex = childIndex + count;
905 	for (int32 i = childIndex; i < childEndIndex; i++) {
906 		void* child = fModel->ChildAt(parentNode->ModelObject(), i);
907 
908 		// create node
909 		TreeTableNode* node = new(std::nothrow) TreeTableNode(parentNode);
910 		if (node == NULL || node->Init(child, columnCount) != B_OK
911 			|| !parentNode->AddChild(node, i)) {
912 			delete node;
913 			return false;
914 		}
915 
916 		// add row
917 		AddRow(node->Row(), i,
918 			parentNode != fRootNode ? parentNode->Row() : NULL);
919 
920 		// recursively create children
921 		if (!_AddChildRows(node, 0, fModel->CountChildren(child), columnCount))
922 			return false;
923 	}
924 
925 	return true;
926 }
927 
928 
929 void
930 TreeTable::_RemoveChildRows(TreeTableNode* parentNode, int32 childIndex,
931 	int32 count)
932 {
933 	for (int32 i = childIndex + count - 1; i >= childIndex; i--) {
934 		if (TreeTableNode* child = parentNode->RemoveChild(i)) {
935 			int32 childCount = child->CountChildren();
936 			if (childCount > 0)
937 				_RemoveChildRows(child, 0, childCount);
938 
939 			RemoveRow(child->Row());
940 			delete child;
941 		}
942 	}
943 }
944 
945 
946 void
947 TreeTable::_SetNodeExpanded(TreeTableNode* node, bool expanded,
948 	bool expandAncestors)
949 {
950 	if (expanded && expandAncestors && node != fRootNode)
951 		_SetNodeExpanded(node->Parent(), true, true);
952 
953 	BColumnListView::ExpandOrCollapse(node->Row(), expanded);
954 }
955 
956 
957 TreeTableNode*
958 TreeTable::_NodeForPath(const TreeTablePath& path) const
959 {
960 	TreeTableNode* node = fRootNode;
961 
962 	int32 count = path.CountComponents();
963 	for (int32 i = 0; node != NULL && i < count; i++)
964 		node = node->ChildAt(path.ComponentAt(i));
965 
966 	return node;
967 }
968 
969 
970 void
971 TreeTable::_GetPathForNode(TreeTableNode* node, TreeTablePath& _path) const
972 {
973 	if (node == fRootNode) {
974 		_path.Clear();
975 		return;
976 	}
977 
978 	_GetPathForNode(node->Parent(), _path);
979 	_path.AddComponent(node->Parent()->IndexOf(node));
980 }
981