xref: /haiku/src/apps/debuganalyzer/gui/table/TreeTable.cpp (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 	fToolTipProvider(NULL),
692 	fRootNode(NULL),
693 	fSelectionModel(this),
694 	fIgnoreSelectionChange(0)
695 {
696 	SetTreeTableModel(model);
697 }
698 
699 
700 TreeTable::~TreeTable()
701 {
702 	SetTreeTableModel(NULL);
703 }
704 
705 
706 bool
707 TreeTable::SetTreeTableModel(TreeTableModel* model)
708 {
709 	if (model == fModel)
710 		return true;
711 
712 	if (fModel != NULL) {
713 		fModel->RemoveListener(this);
714 
715 		if (fRootNode != NULL) {
716 			fRootNode->DetachRow();
717 			delete fRootNode;
718 			fRootNode = NULL;
719 		}
720 
721 		Clear();
722 
723 		for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
724 			column->SetModel(NULL);
725 	}
726 
727 	fModel = model;
728 
729 	if (fModel == NULL)
730 		return true;
731 
732 	fRootNode = new(std::nothrow) TreeTableNode(NULL);
733 	if (fRootNode == NULL)
734 		return false;
735 
736 	if (fRootNode->Init(fModel->Root(), fModel->CountColumns()) != B_OK) {
737 		delete fRootNode;
738 		fRootNode = NULL;
739 		return false;
740 	}
741 
742 	fModel->AddListener(this);
743 
744 	for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
745 		column->SetModel(fModel);
746 
747 	// recursively create the rows
748 	if (!_AddChildRows(fRootNode, 0, fModel->CountChildren(fModel->Root()),
749 			fModel->CountColumns())) {
750 		SetTreeTableModel(NULL);
751 		return false;
752 	}
753 
754 	return true;
755 }
756 
757 
758 void
759 TreeTable::SetToolTipProvider(TreeTableToolTipProvider* toolTipProvider)
760 {
761 	fToolTipProvider = toolTipProvider;
762 }
763 
764 
765 TreeTableSelectionModel*
766 TreeTable::SelectionModel()
767 {
768 	return &fSelectionModel;
769 }
770 
771 
772 void
773 TreeTable::SelectNode(const TreeTablePath& path, bool extendSelection)
774 {
775 	TreeTableNode* node = _NodeForPath(path);
776 	if (node == NULL)
777 		return;
778 
779 	if (!extendSelection) {
780 		fIgnoreSelectionChange++;
781 		DeselectAll();
782 		fIgnoreSelectionChange--;
783 	}
784 
785 	AddToSelection(node->Row());
786 }
787 
788 
789 void
790 TreeTable::DeselectNode(const TreeTablePath& path)
791 {
792 	if (TreeTableNode* node = _NodeForPath(path))
793 		Deselect(node->Row());
794 }
795 
796 
797 void
798 TreeTable::DeselectAllNodes()
799 {
800 	DeselectAll();
801 }
802 
803 
804 bool
805 TreeTable::IsNodeExpanded(const TreeTablePath& path) const
806 {
807 	if (TreeTableNode* node = _NodeForPath(path))
808 		return node->Row()->IsExpanded();
809 	return false;
810 }
811 
812 
813 void
814 TreeTable::SetNodeExpanded(const TreeTablePath& path, bool expanded,
815 	bool expandAncestors)
816 {
817 	if (TreeTableNode* node = _NodeForPath(path))
818 		_SetNodeExpanded(node, expanded, expandAncestors);
819 }
820 
821 
822 void
823 TreeTable::ScrollToNode(const TreeTablePath& path)
824 {
825 	if (TreeTableNode* node = _NodeForPath(path))
826 		BColumnListView::ScrollTo(node->Row());
827 }
828 
829 
830 bool
831 TreeTable::AddTreeTableListener(TreeTableListener* listener)
832 {
833 	return fListeners.AddItem(listener);
834 }
835 
836 
837 void
838 TreeTable::RemoveTreeTableListener(TreeTableListener* listener)
839 {
840 	fListeners.RemoveItem(listener);
841 }
842 
843 
844 status_t
845 TreeTable::GetCellRectAt(const TreeTablePath& path, int32 colIndex,
846 	BRect& _output) const
847 {
848 	TreeTableNode* node = _NodeForPath(path);
849 	if (node == NULL)
850 		return B_ENTRY_NOT_FOUND;
851 
852 	AbstractColumn* column = fColumns.ItemAt(colIndex);
853 	if (column == NULL)
854 		return B_ENTRY_NOT_FOUND;
855 
856 	_output = GetFieldRect(node->Row(), column);
857 
858 	return B_OK;
859 }
860 
861 
862 bool
863 TreeTable::GetToolTipAt(BPoint point, BToolTip** _tip)
864 {
865 	if (fToolTipProvider == NULL)
866 		return AbstractTable::GetToolTipAt(point, _tip);
867 
868 	// get the table row
869 	BRow* row = RowAt(point);
870 	if (row == NULL)
871 		return AbstractTable::GetToolTipAt(point, _tip);
872 
873 	TreeTableRow* treeRow = dynamic_cast<TreeTableRow*>(row);
874 	// get the table column
875 	BColumn* column = ColumnAt(point);
876 
877 	int32 columnIndex = column != NULL ? column->LogicalFieldNum() : -1;
878 
879 	TreeTablePath path;
880 	_GetPathForNode(treeRow->Node(), path);
881 
882 	return fToolTipProvider->GetToolTipForTablePath(path, columnIndex,
883 		_tip);
884 }
885 
886 
887 void
888 TreeTable::SelectionChanged()
889 {
890 	if (fIgnoreSelectionChange > 0)
891 		return;
892 
893 	fSelectionModel._SelectionChanged();
894 
895 	if (!fListeners.IsEmpty()) {
896 		int32 listenerCount = fListeners.CountItems();
897 		for (int32 i = listenerCount - 1; i >= 0; i--)
898 			fListeners.ItemAt(i)->TreeTableSelectionChanged(this);
899 	}
900 }
901 
902 
903 AbstractTable::AbstractColumn*
904 TreeTable::CreateColumn(TableColumn* column)
905 {
906 	return new Column(fModel, column);
907 }
908 
909 
910 void
911 TreeTable::ColumnMouseDown(AbstractColumn* column, BRow* _row, BField* field,
912 	BPoint screenWhere, uint32 buttons)
913 {
914 	if (!fListeners.IsEmpty()) {
915 		// get the table node and the column index
916 		TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
917 		int32 columnIndex = column->LogicalFieldNum();
918 		if (row == NULL || columnIndex < 0)
919 			return;
920 
921 		// get the node path
922 		TreeTablePath path;
923 		_GetPathForNode(row->Node(), path);
924 
925 		// notify listeners
926 		int32 listenerCount = fListeners.CountItems();
927 		for (int32 i = listenerCount - 1; i >= 0; i--) {
928 			fListeners.ItemAt(i)->TreeTableCellMouseDown(this, path,
929 				columnIndex, screenWhere, buttons);
930 		}
931 	}
932 }
933 
934 
935 void
936 TreeTable::ColumnMouseUp(AbstractColumn* column, BRow* _row, BField* field,
937 	BPoint screenWhere, uint32 buttons)
938 {
939 	if (!fListeners.IsEmpty()) {
940 		// get the table node and the column index
941 		TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
942 		int32 columnIndex = column->LogicalFieldNum();
943 		if (row == NULL || columnIndex < 0)
944 			return;
945 
946 		// get the node path
947 		TreeTablePath path;
948 		_GetPathForNode(row->Node(), path);
949 
950 		// notify listeners
951 		int32 listenerCount = fListeners.CountItems();
952 		for (int32 i = listenerCount - 1; i >= 0; i--) {
953 			fListeners.ItemAt(i)->TreeTableCellMouseUp(this, path, columnIndex,
954 				screenWhere, buttons);
955 		}
956 	}
957 }
958 
959 
960 void
961 TreeTable::TableNodesAdded(TreeTableModel* model, const TreeTablePath& path,
962 	int32 childIndex, int32 count)
963 {
964 	TreeTableNode* node = _NodeForPath(path);
965 	if (node == NULL)
966 		return;
967 
968 	_AddChildRows(node, childIndex, count, fModel->CountColumns());
969 }
970 
971 
972 void
973 TreeTable::TableNodesRemoved(TreeTableModel* model, const TreeTablePath& path,
974 	int32 childIndex, int32 count)
975 {
976 	TreeTableNode* node = _NodeForPath(path);
977 	if (node == NULL)
978 		return;
979 
980 	_RemoveChildRows(node, childIndex, count);
981 }
982 
983 
984 void
985 TreeTable::TableNodesChanged(TreeTableModel* model, const TreeTablePath& path,
986 	int32 childIndex, int32 count)
987 {
988 	TreeTableNode* node = _NodeForPath(path);
989 	if (node == NULL)
990 		return;
991 
992 	int32 endIndex = childIndex + count;
993 	for (int32 i = childIndex; i < endIndex; i++) {
994 		if (TreeTableNode* child = node->ChildAt(i))
995 			UpdateRow(child->Row());
996 	}
997 }
998 
999 
1000 void
1001 TreeTable::TableModelReset(TreeTableModel* model)
1002 {
1003 	_RemoveChildRows(fRootNode, 0, fRootNode->CountChildren());
1004 	_AddChildRows(fRootNode, 0, fModel->CountChildren(
1005 		fModel->Root()), fModel->CountColumns());
1006 }
1007 
1008 
1009 void
1010 TreeTable::ExpandOrCollapse(BRow* _row, bool expand)
1011 {
1012 	TreeTableRow* row = dynamic_cast<TreeTableRow*>(_row);
1013 	if (row == NULL || row->IsExpanded() == expand)
1014 		return;
1015 
1016 	AbstractTable::ExpandOrCollapse(row, expand);
1017 
1018 	if (row->IsExpanded() != expand)
1019 		return;
1020 
1021 	TreeTablePath path;
1022 	_GetPathForNode(row->Node(), path);
1023 
1024 	int32 listenerCount = fListeners.CountItems();
1025 	for (int32 i = listenerCount - 1; i >= 0; i--)
1026 		fListeners.ItemAt(i)->TreeTableNodeExpandedChanged(this, path, expand);
1027 }
1028 
1029 
1030 void
1031 TreeTable::ItemInvoked()
1032 {
1033 	if (fListeners.IsEmpty())
1034 		return;
1035 
1036 	TreeTableRow* row = dynamic_cast<TreeTableRow*>(CurrentSelection());
1037 	if (row == NULL)
1038 		return;
1039 
1040 	TreeTablePath path;
1041 	_GetPathForNode(row->Node(), path);
1042 
1043 	int32 listenerCount = fListeners.CountItems();
1044 	for (int32 i = listenerCount - 1; i >= 0; i--)
1045 		fListeners.ItemAt(i)->TreeTableNodeInvoked(this, path);
1046 }
1047 
1048 
1049 bool
1050 TreeTable::_AddChildRows(TreeTableNode* parentNode, int32 childIndex,
1051 	int32 count, int32 columnCount)
1052 {
1053 	int32 childEndIndex = childIndex + count;
1054 	for (int32 i = childIndex; i < childEndIndex; i++) {
1055 		void* child = fModel->ChildAt(parentNode->ModelObject(), i);
1056 
1057 		// create node
1058 		TreeTableNode* node = new(std::nothrow) TreeTableNode(parentNode);
1059 		if (node == NULL || node->Init(child, columnCount) != B_OK
1060 			|| !parentNode->AddChild(node, i)) {
1061 			delete node;
1062 			return false;
1063 		}
1064 
1065 		// add row
1066 		AddRow(node->Row(), i,
1067 			parentNode != fRootNode ? parentNode->Row() : NULL);
1068 
1069 		// recursively create children
1070 		if (!_AddChildRows(node, 0, fModel->CountChildren(child), columnCount))
1071 			return false;
1072 	}
1073 
1074 	return true;
1075 }
1076 
1077 
1078 void
1079 TreeTable::_RemoveChildRows(TreeTableNode* parentNode, int32 childIndex,
1080 	int32 count)
1081 {
1082 	// check if the removal request would in effect remove all
1083 	// existing nodes.
1084 	if (parentNode == fRootNode && childIndex == 0
1085 		&& count == parentNode->CountChildren()) {
1086 		Clear();
1087 		return;
1088 	}
1089 
1090 	for (int32 i = childIndex + count - 1; i >= childIndex; i--) {
1091 		if (TreeTableNode* child = parentNode->RemoveChild(i)) {
1092 			int32 childCount = child->CountChildren();
1093 			if (childCount > 0)
1094 				_RemoveChildRows(child, 0, childCount);
1095 
1096 			RemoveRow(child->Row());
1097 			delete child;
1098 		}
1099 	}
1100 }
1101 
1102 
1103 void
1104 TreeTable::_SetNodeExpanded(TreeTableNode* node, bool expanded,
1105 	bool expandAncestors)
1106 {
1107 	if (expanded && expandAncestors && node != fRootNode)
1108 		_SetNodeExpanded(node->Parent(), true, true);
1109 
1110 	ExpandOrCollapse(node->Row(), expanded);
1111 }
1112 
1113 
1114 TreeTableNode*
1115 TreeTable::_NodeForPath(const TreeTablePath& path) const
1116 {
1117 	TreeTableNode* node = fRootNode;
1118 
1119 	int32 count = path.CountComponents();
1120 	for (int32 i = 0; node != NULL && i < count; i++)
1121 		node = node->ChildAt(path.ComponentAt(i));
1122 
1123 	return node;
1124 }
1125 
1126 
1127 void
1128 TreeTable::_GetPathForNode(TreeTableNode* node, TreeTablePath& _path) const
1129 {
1130 	if (node == fRootNode) {
1131 		_path.Clear();
1132 		return;
1133 	}
1134 
1135 	_GetPathForNode(node->Parent(), _path);
1136 	_path.AddComponent(node->Parent()->IndexOf(node));
1137 }
1138