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