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