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
15class TreeTableField : public BField {
16public:
17	TreeTableField(void* object)
18		:
19		fObject(object)
20	{
21	}
22
23	void* Object() const
24	{
25		return fObject;
26	}
27
28private:
29	void*	fObject;
30};
31
32
33// #pragma mark - TreeTablePath
34
35
36TreeTablePath::TreeTablePath()
37{
38}
39
40
41TreeTablePath::TreeTablePath(const TreeTablePath& other)
42{
43	*this = other;
44}
45
46
47TreeTablePath::TreeTablePath(const TreeTablePath& other, int32 childIndex)
48{
49	*this = other;
50	AddComponent(childIndex);
51}
52
53
54TreeTablePath::~TreeTablePath()
55{
56}
57
58
59bool
60TreeTablePath::AddComponent(int32 childIndex)
61{
62	try {
63		fComponents.push_back(childIndex);
64		return true;
65	} catch (...) {
66		return false;
67	}
68}
69
70
71int32
72TreeTablePath::RemoveLastComponent()
73{
74	if (fComponents.empty())
75		return -1;
76
77	int32 index = fComponents.back();
78	fComponents.pop_back();
79	return index;
80}
81
82void
83TreeTablePath::Clear()
84{
85	fComponents.clear();
86}
87
88
89int32
90TreeTablePath::CountComponents() const
91{
92	return fComponents.size();
93}
94
95
96int32
97TreeTablePath::ComponentAt(int32 index) const
98{
99	if (index < 0 || (size_t)index >= fComponents.size())
100		return -1;
101	return fComponents[index];
102}
103
104
105TreeTablePath&
106TreeTablePath::operator=(const TreeTablePath& other)
107{
108	try {
109		fComponents = other.fComponents;
110	} catch (...) {
111	}
112	return *this;
113}
114
115
116bool
117TreeTablePath::operator==(const TreeTablePath& other) const
118{
119	return fComponents == other.fComponents;
120}
121
122
123bool
124TreeTablePath::operator!=(const TreeTablePath& other) const
125{
126	return fComponents != other.fComponents;
127}
128
129
130// #pragma mark - TreeTableModelListener
131
132
133TreeTableModelListener::~TreeTableModelListener()
134{
135}
136
137
138void
139TreeTableModelListener::TableNodesAdded(TreeTableModel* model,
140	const TreeTablePath& path, int32 childIndex, int32 count)
141{
142}
143
144
145void
146TreeTableModelListener::TableNodesRemoved(TreeTableModel* model,
147	const TreeTablePath& path, int32 childIndex, int32 count)
148{
149}
150
151
152void
153TreeTableModelListener::TableNodesChanged(TreeTableModel* model,
154	const TreeTablePath& path, int32 childIndex, int32 count)
155{
156}
157
158
159void
160TreeTableModelListener::TableModelReset(TreeTableModel* model)
161{
162}
163
164
165// #pragma mark - TreeTableModel
166
167
168TreeTableModel::~TreeTableModel()
169{
170}
171
172
173void*
174TreeTableModel::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
186bool
187TreeTableModel::AddListener(TreeTableModelListener* listener)
188{
189	return fListeners.AddItem(listener);
190}
191
192
193void
194TreeTableModel::RemoveListener(TreeTableModelListener* listener)
195{
196	fListeners.RemoveItem(listener);
197}
198
199
200void
201TreeTableModel::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
212void
213TreeTableModel::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
224void
225TreeTableModel::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
236void
237TreeTableModel::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
249TreeTableToolTipProvider::~TreeTableToolTipProvider()
250{
251}
252
253
254// #pragma mark - TreeTableListener
255
256
257TreeTableListener::~TreeTableListener()
258{
259}
260
261
262void
263TreeTableListener::TreeTableSelectionChanged(TreeTable* table)
264{
265}
266
267
268void
269TreeTableListener::TreeTableNodeInvoked(TreeTable* table,
270	const TreeTablePath& path)
271{
272}
273
274
275void
276TreeTableListener::TreeTableNodeExpandedChanged(TreeTable* table,
277	const TreeTablePath& path, bool expanded)
278{
279}
280
281
282void
283TreeTableListener::TreeTableCellMouseDown(TreeTable* table,
284	const TreeTablePath& path, int32 columnIndex, BPoint screenWhere,
285	uint32 buttons)
286{
287}
288
289
290void
291TreeTableListener::TreeTableCellMouseUp(TreeTable* table,
292	const TreeTablePath& path, int32 columnIndex, BPoint screenWhere,
293	uint32 buttons)
294{
295}
296
297
298// #pragma mark - Column
299
300
301class TreeTable::Column : public AbstractColumn {
302public:
303								Column(TreeTableModel* model,
304									TableColumn* tableColumn);
305	virtual						~Column();
306
307	virtual	void				SetModel(AbstractTableModelBase* model);
308
309protected:
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
319private:
320			TreeTableModel*		fModel;
321};
322
323
324TreeTable::Column::Column(TreeTableModel* model, TableColumn* tableColumn)
325	:
326	AbstractColumn(tableColumn),
327	fModel(model)
328{
329}
330
331
332TreeTable::Column::~Column()
333{
334}
335
336
337void
338TreeTable::Column::SetModel(AbstractTableModelBase* model)
339{
340	fModel = dynamic_cast<TreeTableModel*>(model);
341}
342
343
344void
345TreeTable::Column::DrawTitle(BRect rect, BView* targetView)
346{
347	fTableColumn->DrawTitle(rect, targetView);
348}
349
350
351void
352TreeTable::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
366int
367TreeTable::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
395void
396TreeTable::Column::GetColumnName(BString* into) const
397{
398	fTableColumn->GetColumnName(into);
399}
400
401
402float
403TreeTable::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
420class TreeTableRow : public BRow {
421public:
422	TreeTableRow(TreeTableNode* node)
423		:
424		fNode(node)
425	{
426	}
427
428	TreeTableNode* Node()
429	{
430		return fNode;
431	}
432
433private:
434	TreeTableNode*	fNode;
435};
436
437
438// #pragma mark - TreeTableNode
439
440
441class TreeTableNode {
442public:
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
460private:
461			typedef BObjectList<TreeTableNode> NodeList;
462
463private:
464			TreeTableNode*		fParent;
465			TreeTableRow*		fRow;
466			NodeList*			fChildren;
467};
468
469
470TreeTableNode::TreeTableNode(TreeTableNode* parent)
471	:
472	fParent(parent),
473	fRow(NULL),
474	fChildren(NULL)
475{
476}
477
478
479TreeTableNode::~TreeTableNode()
480{
481	delete fChildren;
482	delete fRow;
483}
484
485
486status_t
487TreeTableNode::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
510void
511TreeTableNode::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
523void*
524TreeTableNode::ModelObject() const
525{
526	TreeTableField* field = dynamic_cast<TreeTableField*>(fRow->GetField(0));
527	return field->Object();
528}
529
530
531bool
532TreeTableNode::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
544TreeTableNode*
545TreeTableNode::RemoveChild(int32 index)
546{
547	return fChildren != NULL ? fChildren->RemoveItemAt(index) : NULL;
548}
549
550
551int32
552TreeTableNode::CountChildren() const
553{
554	return fChildren != NULL ? fChildren->CountItems() : 0;
555}
556
557
558TreeTableNode*
559TreeTableNode::ChildAt(int32 index)
560{
561	return fChildren != NULL ? fChildren->ItemAt(index) : NULL;
562}
563
564
565int32
566TreeTableNode::IndexOf(TreeTableNode* child)
567{
568	return fChildren != NULL ? fChildren->IndexOf(child) : -1;
569}
570
571
572// #pragma mark - TreeTableSelectionModel
573
574
575TreeTableSelectionModel::TreeTableSelectionModel(TreeTable* table)
576	:
577	fTreeTable(table),
578	fNodes(NULL),
579	fNodeCount(-1)
580{
581}
582
583
584TreeTableSelectionModel::~TreeTableSelectionModel()
585{
586	delete[] fNodes;
587}
588
589
590int32
591TreeTableSelectionModel::CountNodes()
592{
593	_Update();
594
595	return fNodeCount;
596}
597
598
599void*
600TreeTableSelectionModel::NodeAt(int32 index)
601{
602	if (TreeTableNode* node = _NodeAt(index))
603		return node->ModelObject();
604	return NULL;
605}
606
607
608bool
609TreeTableSelectionModel::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
620void
621TreeTableSelectionModel::_SelectionChanged()
622{
623	if (fNodeCount >= 0) {
624		fNodeCount = -1;
625		delete[] fNodes;
626		fNodes = NULL;
627	}
628}
629
630
631void
632TreeTableSelectionModel::_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
661TreeTableNode*
662TreeTableSelectionModel::_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
673TreeTable::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
686TreeTable::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
700TreeTable::~TreeTable()
701{
702	SetTreeTableModel(NULL);
703}
704
705
706bool
707TreeTable::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
758void
759TreeTable::SetToolTipProvider(TreeTableToolTipProvider* toolTipProvider)
760{
761	fToolTipProvider = toolTipProvider;
762}
763
764
765TreeTableSelectionModel*
766TreeTable::SelectionModel()
767{
768	return &fSelectionModel;
769}
770
771
772void
773TreeTable::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
789void
790TreeTable::DeselectNode(const TreeTablePath& path)
791{
792	if (TreeTableNode* node = _NodeForPath(path))
793		Deselect(node->Row());
794}
795
796
797void
798TreeTable::DeselectAllNodes()
799{
800	DeselectAll();
801}
802
803
804bool
805TreeTable::IsNodeExpanded(const TreeTablePath& path) const
806{
807	if (TreeTableNode* node = _NodeForPath(path))
808		return node->Row()->IsExpanded();
809	return false;
810}
811
812
813void
814TreeTable::SetNodeExpanded(const TreeTablePath& path, bool expanded,
815	bool expandAncestors)
816{
817	if (TreeTableNode* node = _NodeForPath(path))
818		_SetNodeExpanded(node, expanded, expandAncestors);
819}
820
821
822void
823TreeTable::ScrollToNode(const TreeTablePath& path)
824{
825	if (TreeTableNode* node = _NodeForPath(path))
826		BColumnListView::ScrollTo(node->Row());
827}
828
829
830bool
831TreeTable::AddTreeTableListener(TreeTableListener* listener)
832{
833	return fListeners.AddItem(listener);
834}
835
836
837void
838TreeTable::RemoveTreeTableListener(TreeTableListener* listener)
839{
840	fListeners.RemoveItem(listener);
841}
842
843
844status_t
845TreeTable::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
862bool
863TreeTable::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
887void
888TreeTable::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
903AbstractTable::AbstractColumn*
904TreeTable::CreateColumn(TableColumn* column)
905{
906	return new Column(fModel, column);
907}
908
909
910void
911TreeTable::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
935void
936TreeTable::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
960void
961TreeTable::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
972void
973TreeTable::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
984void
985TreeTable::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
1000void
1001TreeTable::TableModelReset(TreeTableModel* model)
1002{
1003	_RemoveChildRows(fRootNode, 0, fRootNode->CountChildren());
1004	_AddChildRows(fRootNode, 0, fModel->CountChildren(
1005		fModel->Root()), fModel->CountColumns());
1006}
1007
1008
1009void
1010TreeTable::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
1030void
1031TreeTable::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
1049bool
1050TreeTable::_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
1078void
1079TreeTable::_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
1103void
1104TreeTable::_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
1114TreeTableNode*
1115TreeTable::_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
1127void
1128TreeTable::_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