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