1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2011-2015, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 #include "ImageFunctionsView.h" 8 9 #include <stdio.h> 10 11 #include <new> 12 #include <set> 13 14 #include <ControlLook.h> 15 #include <LayoutBuilder.h> 16 #include <MessageRunner.h> 17 #include <StringList.h> 18 #include <TextControl.h> 19 20 #include <AutoDeleter.h> 21 #include <RegExp.h> 22 23 #include "table/TableColumns.h" 24 25 #include "FunctionInstance.h" 26 #include "GuiSettingsUtils.h" 27 #include "Image.h" 28 #include "ImageDebugInfo.h" 29 #include "LocatableFile.h" 30 #include "TargetAddressTableColumn.h" 31 #include "Tracing.h" 32 33 34 static const uint32 MSG_FUNCTION_FILTER_CHANGED = 'mffc'; 35 static const uint32 MSG_FUNCTION_TYPING_TIMEOUT = 'mftt'; 36 37 static const uint32 kKeypressTimeout = 250000; 38 39 // from ColumnTypes.cpp 40 static const float kTextMargin = 8.0; 41 42 43 // #pragma mark - SourcePathComponentNode 44 45 46 class ImageFunctionsView::SourcePathComponentNode : public BReferenceable { 47 public: 48 SourcePathComponentNode(SourcePathComponentNode* parent, 49 const BString& componentName, LocatableFile* sourceFile, 50 FunctionInstance* function) 51 : 52 fParent(parent), 53 fComponentName(componentName), 54 fSourceFile(sourceFile), 55 fFunction(function), 56 fFilterMatch(), 57 fHasMatchingChild(false) 58 { 59 if (fSourceFile != NULL) 60 fSourceFile->AcquireReference(); 61 if (fFunction != NULL) 62 fFunction->AcquireReference(); 63 } 64 65 virtual ~SourcePathComponentNode() 66 { 67 for (int32 i = 0; i < fChildPathComponents.CountItems(); i++) 68 fChildPathComponents.ItemAt(i)->ReleaseReference(); 69 70 if (fSourceFile != NULL) 71 fSourceFile->ReleaseReference(); 72 73 if (fFunction != NULL) 74 fFunction->ReleaseReference(); 75 } 76 77 const BString& ComponentName() const 78 { 79 return fComponentName; 80 } 81 82 LocatableFile* SourceFile() const 83 { 84 return fSourceFile; 85 } 86 87 FunctionInstance* Function() const 88 { 89 return fFunction; 90 } 91 92 int32 CountChildren() const 93 { 94 return fChildPathComponents.CountItems(); 95 } 96 97 SourcePathComponentNode* ChildAt(int32 index) 98 { 99 return fChildPathComponents.ItemAt(index); 100 } 101 102 SourcePathComponentNode* FindChildByName(const BString& name) const 103 { 104 return fChildPathComponents.BinarySearchByKey(name, 105 &CompareByComponentName); 106 } 107 108 int32 FindChildIndexByName(const BString& name) const 109 { 110 return fChildPathComponents.BinarySearchIndexByKey(name, 111 &CompareByComponentName); 112 } 113 114 bool AddChild(SourcePathComponentNode* child) 115 { 116 if (!fChildPathComponents.BinaryInsert(child, 117 &CompareComponents)) { 118 return false; 119 } 120 121 child->AcquireReference(); 122 123 return true; 124 } 125 126 bool RemoveChild(SourcePathComponentNode* child) 127 { 128 if (!fChildPathComponents.RemoveItem(child)) 129 return false; 130 131 child->ReleaseReference(); 132 133 return true; 134 } 135 136 bool RemoveAllChildren() 137 { 138 for (int32 i = 0; i < fChildPathComponents.CountItems(); i++) 139 RemoveChild(fChildPathComponents.ItemAt(i)); 140 141 return true; 142 } 143 144 const RegExp::MatchResult& FilterMatch() const 145 { 146 return fFilterMatch; 147 } 148 149 void SetFilterMatch(const RegExp::MatchResult& match) 150 { 151 fFilterMatch = match; 152 } 153 154 bool HasMatchingChild() const 155 { 156 return fHasMatchingChild; 157 } 158 159 void SetHasMatchingChild() 160 { 161 fHasMatchingChild = true; 162 } 163 164 private: 165 friend class ImageFunctionsView::FunctionsTableModel; 166 167 static int CompareByComponentName(const BString* name, const 168 SourcePathComponentNode* node) 169 { 170 return name->Compare(node->ComponentName()); 171 } 172 173 static int CompareComponents(const SourcePathComponentNode* a, 174 const SourcePathComponentNode* b) 175 { 176 return a->ComponentName().Compare(b->ComponentName()); 177 } 178 179 private: 180 typedef BObjectList<SourcePathComponentNode> ChildPathComponentList; 181 182 private: 183 SourcePathComponentNode* fParent; 184 BString fComponentName; 185 LocatableFile* fSourceFile; 186 FunctionInstance* fFunction; 187 ChildPathComponentList fChildPathComponents; 188 RegExp::MatchResult fFilterMatch; 189 bool fHasMatchingChild; 190 }; 191 192 193 // #pragma mark - HighlightingTableColumn 194 195 196 class ImageFunctionsView::HighlightingTableColumn : public StringTableColumn { 197 public: 198 HighlightingTableColumn(int32 modelIndex, const char* title, float width, 199 float minWidth, float maxWidth, uint32 truncate, 200 alignment align = B_ALIGN_LEFT) 201 : 202 StringTableColumn(modelIndex, title, width, minWidth, maxWidth, 203 truncate, align), 204 fHasFilter(false) 205 { 206 } 207 208 void SetHasFilter(bool hasFilter) 209 { 210 fHasFilter = hasFilter; 211 } 212 213 virtual void DrawValue(const BVariant& value, BRect rect, 214 BView* targetView) 215 { 216 StringTableColumn::DrawValue(value, rect, targetView); 217 218 if (fHasFilter) { 219 // TODO: handle this case as well 220 if (fField.HasClippedString()) 221 return; 222 223 const SourcePathComponentNode* node 224 = (const SourcePathComponentNode*)value.ToPointer(); 225 226 const RegExp::MatchResult& match = node->FilterMatch(); 227 if (!match.HasMatched()) 228 return; 229 230 targetView->PushState(); 231 BRect fillRect(rect); 232 fillRect.left += kTextMargin + targetView->StringWidth( 233 fField.String(), match.StartOffset()); 234 float filterWidth = targetView->StringWidth(fField.String() 235 + match.StartOffset(), match.EndOffset() 236 - match.StartOffset()); 237 fillRect.right = fillRect.left + filterWidth; 238 targetView->SetLowColor(255, 255, 0, 255); 239 targetView->SetDrawingMode(B_OP_MIN); 240 targetView->FillRect(fillRect, B_SOLID_LOW); 241 targetView->PopState(); 242 } 243 } 244 245 virtual BField* PrepareField(const BVariant& value) const 246 { 247 const SourcePathComponentNode* node 248 = (const SourcePathComponentNode*)value.ToPointer(); 249 250 BVariant tempValue(node->ComponentName(), B_VARIANT_DONT_COPY_DATA); 251 return StringTableColumn::PrepareField(tempValue); 252 } 253 254 255 private: 256 bool fHasFilter; 257 }; 258 259 260 // #pragma mark - FunctionsTableModel 261 262 263 class ImageFunctionsView::FunctionsTableModel : public TreeTableModel { 264 public: 265 FunctionsTableModel() 266 : 267 fImageDebugInfo(NULL), 268 fSourcelessNode(NULL) 269 { 270 } 271 272 ~FunctionsTableModel() 273 { 274 SetImageDebugInfo(NULL); 275 } 276 277 void SetImageDebugInfo(ImageDebugInfo* imageDebugInfo) 278 { 279 // unset old functions 280 int32 count = fChildPathComponents.CountItems(); 281 if (fImageDebugInfo != NULL) { 282 for (int32 i = 0; i < count; i++) 283 fChildPathComponents.ItemAt(i)->ReleaseReference(); 284 285 fChildPathComponents.MakeEmpty(); 286 fSourcelessNode = NULL; 287 } 288 289 fImageDebugInfo = imageDebugInfo; 290 291 // set new functions 292 if (fImageDebugInfo == NULL || fImageDebugInfo->CountFunctions() 293 == 0) { 294 NotifyNodesRemoved(TreeTablePath(), 0, count); 295 return; 296 } 297 298 std::set<target_addr_t> functionAddresses; 299 300 SourcePathComponentNode* sourcelessNode = new(std::nothrow) 301 SourcePathComponentNode(NULL, "<no source file>", NULL, NULL); 302 BReference<SourcePathComponentNode> sourceNodeRef( 303 sourcelessNode, true); 304 305 LocatableFile* currentFile = NULL; 306 BStringList pathComponents; 307 bool applyFilter = !fFilterString.IsEmpty() 308 && fCurrentFilter.IsValid(); 309 int32 functionCount = fImageDebugInfo->CountFunctions(); 310 for (int32 i = 0; i < functionCount; i++) { 311 FunctionInstance* instance = fImageDebugInfo->FunctionAt(i); 312 target_addr_t address = instance->Address(); 313 if (functionAddresses.find(address) != functionAddresses.end()) 314 continue; 315 else { 316 try { 317 functionAddresses.insert(address); 318 } catch (...) { 319 return; 320 } 321 } 322 323 LocatableFile* sourceFile = instance->SourceFile(); 324 BString sourcePath; 325 if (sourceFile != NULL) 326 sourceFile->GetPath(sourcePath); 327 328 RegExp::MatchResult pathMatch; 329 RegExp::MatchResult functionMatch; 330 if (applyFilter && !_FilterFunction(instance, sourcePath, 331 pathMatch, functionMatch)) { 332 continue; 333 } 334 335 if (sourceFile == NULL) { 336 if (!_AddFunctionNode(sourcelessNode, instance, NULL, 337 functionMatch)) { 338 return; 339 } 340 continue; 341 } 342 343 if (sourceFile != currentFile) { 344 currentFile = sourceFile; 345 pathComponents.MakeEmpty(); 346 if (applyFilter) { 347 pathComponents.Add(sourcePath); 348 } else { 349 if (!_GetSourcePathComponents(currentFile, 350 pathComponents)) { 351 return; 352 } 353 } 354 } 355 356 if (!_AddFunctionByPath(pathComponents, instance, currentFile, 357 pathMatch, functionMatch)) { 358 return; 359 } 360 } 361 362 if (sourcelessNode->CountChildren() != 0) { 363 if (fChildPathComponents.BinaryInsert(sourcelessNode, 364 &SourcePathComponentNode::CompareComponents)) { 365 fSourcelessNode = sourcelessNode; 366 sourceNodeRef.Detach(); 367 } 368 } 369 370 NotifyTableModelReset(); 371 } 372 373 virtual int32 CountColumns() const 374 { 375 return 2; 376 } 377 378 virtual void* Root() const 379 { 380 return (void*)this; 381 } 382 383 virtual int32 CountChildren(void* parent) const 384 { 385 if (parent == this) 386 return fChildPathComponents.CountItems(); 387 388 return ((SourcePathComponentNode*)parent)->CountChildren(); 389 } 390 391 virtual void* ChildAt(void* parent, int32 index) const 392 { 393 if (parent == this) 394 return fChildPathComponents.ItemAt(index); 395 396 return ((SourcePathComponentNode*)parent)->ChildAt(index); 397 } 398 399 virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value) 400 { 401 if (object == this) 402 return false; 403 404 SourcePathComponentNode* node = (SourcePathComponentNode*)object; 405 switch (columnIndex) { 406 case 0: 407 { 408 value.SetTo(node); 409 break; 410 } 411 case 1: 412 { 413 FunctionInstance* function = node->Function(); 414 if (function == NULL) 415 return false; 416 value.SetTo(function->Address()); 417 break; 418 } 419 default: 420 return false; 421 } 422 423 return true; 424 } 425 426 bool HasMatchingChildAt(void* parent, int32 index) const 427 { 428 SourcePathComponentNode* node 429 = (SourcePathComponentNode*)ChildAt(parent, index); 430 if (node != NULL) 431 return node->HasMatchingChild(); 432 433 return false; 434 } 435 436 bool GetFunctionPath(FunctionInstance* function, TreeTablePath& _path) 437 { 438 if (function == NULL) 439 return false; 440 441 LocatableFile* sourceFile = function->SourceFile(); 442 SourcePathComponentNode* node = NULL; 443 int32 childIndex = -1; 444 if (sourceFile == NULL) { 445 node = fSourcelessNode; 446 _path.AddComponent(fChildPathComponents.IndexOf(node)); 447 } else { 448 BStringList pathComponents; 449 if (!_GetSourcePathComponents(sourceFile, pathComponents)) 450 return false; 451 452 for (int32 i = 0; i < pathComponents.CountStrings(); i++) { 453 BString component = pathComponents.StringAt(i); 454 455 if (node == NULL) { 456 childIndex = fChildPathComponents.BinarySearchIndexByKey( 457 component, 458 &SourcePathComponentNode::CompareByComponentName); 459 node = fChildPathComponents.ItemAt(childIndex); 460 } else { 461 childIndex = node->FindChildIndexByName(component); 462 node = node->ChildAt(childIndex); 463 } 464 465 if (childIndex < 0) 466 return false; 467 468 _path.AddComponent(childIndex); 469 } 470 } 471 472 if (node == NULL) 473 return false; 474 475 childIndex = node->FindChildIndexByName(function->PrettyName()); 476 if (childIndex < 0) 477 return false; 478 479 _path.AddComponent(childIndex); 480 return true; 481 } 482 483 bool GetObjectForPath(const TreeTablePath& path, 484 LocatableFile*& _sourceFile, FunctionInstance*& _function) 485 { 486 SourcePathComponentNode* node = fChildPathComponents.ItemAt( 487 path.ComponentAt(0)); 488 489 if (node == NULL) 490 return false; 491 492 for (int32 i = 1; i < path.CountComponents(); i++) 493 node = node->ChildAt(path.ComponentAt(i)); 494 495 if (node != NULL) { 496 _sourceFile = node->SourceFile(); 497 _function = node->Function(); 498 return true; 499 } 500 501 return false; 502 } 503 504 void SetFilter(const char* filter) 505 { 506 fFilterString = filter; 507 if (fFilterString.IsEmpty() 508 || fCurrentFilter.SetPattern(filter, RegExp::PATTERN_TYPE_WILDCARD, 509 false)) { 510 SetImageDebugInfo(fImageDebugInfo); 511 } 512 } 513 514 private: 515 bool _GetSourcePathComponents(LocatableFile* currentFile, 516 BStringList& pathComponents) 517 { 518 BString sourcePath; 519 currentFile->GetPath(sourcePath); 520 if (sourcePath.IsEmpty()) 521 return false; 522 523 int32 startIndex = 0; 524 if (sourcePath[0] == '/') 525 startIndex = 1; 526 527 while (startIndex < sourcePath.Length()) { 528 int32 searchIndex = sourcePath.FindFirst('/', startIndex); 529 BString data; 530 if (searchIndex < 0) 531 searchIndex = sourcePath.Length(); 532 533 sourcePath.CopyInto(data, startIndex, searchIndex - startIndex); 534 if (!pathComponents.Add(data)) 535 return false; 536 537 startIndex = searchIndex + 1; 538 } 539 540 return true; 541 } 542 543 bool _AddFunctionByPath(const BStringList& pathComponents, 544 FunctionInstance* function, LocatableFile* file, 545 RegExp::MatchResult& pathMatch, RegExp::MatchResult& functionMatch) 546 { 547 SourcePathComponentNode* parentNode = NULL; 548 SourcePathComponentNode* currentNode = NULL; 549 for (int32 i = 0; i < pathComponents.CountStrings(); i++) { 550 const BString pathComponent = pathComponents.StringAt(i); 551 if (parentNode == NULL) { 552 currentNode = fChildPathComponents.BinarySearchByKey( 553 pathComponent, 554 SourcePathComponentNode::CompareByComponentName); 555 } else 556 currentNode = parentNode->FindChildByName(pathComponent); 557 558 if (currentNode == NULL) { 559 currentNode = new(std::nothrow) SourcePathComponentNode( 560 parentNode, pathComponent, NULL, NULL); 561 if (currentNode == NULL) 562 return false; 563 564 if (pathComponents.CountStrings() == 1) 565 currentNode->SetFilterMatch(pathMatch); 566 567 BReference<SourcePathComponentNode> nodeReference(currentNode, 568 true); 569 if (parentNode != NULL) { 570 if (!parentNode->AddChild(currentNode)) 571 return false; 572 } else { 573 if (!fChildPathComponents.BinaryInsert(currentNode, 574 &SourcePathComponentNode::CompareComponents)) { 575 return false; 576 } 577 578 nodeReference.Detach(); 579 } 580 } 581 582 if (functionMatch.HasMatched()) 583 currentNode->SetHasMatchingChild(); 584 585 parentNode = currentNode; 586 587 } 588 589 return _AddFunctionNode(currentNode, function, file, 590 functionMatch); 591 } 592 593 bool _AddFunctionNode(SourcePathComponentNode* parent, 594 FunctionInstance* function, LocatableFile* file, 595 RegExp::MatchResult& match) 596 { 597 SourcePathComponentNode* functionNode = new(std::nothrow) 598 SourcePathComponentNode(parent, function->PrettyName(), file, 599 function); 600 601 if (functionNode == NULL) 602 return B_NO_MEMORY; 603 604 functionNode->SetFilterMatch(match); 605 606 BReference<SourcePathComponentNode> nodeReference(functionNode, true); 607 if (!parent->AddChild(functionNode)) 608 return false; 609 610 return true; 611 } 612 613 bool _FilterFunction(FunctionInstance* instance, const BString& sourcePath, 614 RegExp::MatchResult& pathMatch, RegExp::MatchResult& functionMatch) 615 { 616 functionMatch = fCurrentFilter.Match(instance->PrettyName()); 617 pathMatch = fCurrentFilter.Match(sourcePath.String()); 618 619 return functionMatch.HasMatched() || pathMatch.HasMatched(); 620 } 621 622 623 private: 624 typedef BObjectList<SourcePathComponentNode> ChildPathComponentList; 625 626 private: 627 ImageDebugInfo* fImageDebugInfo; 628 ChildPathComponentList fChildPathComponents; 629 SourcePathComponentNode* fSourcelessNode; 630 BString fFilterString; 631 RegExp fCurrentFilter; 632 }; 633 634 635 // #pragma mark - ImageFunctionsView 636 637 638 ImageFunctionsView::ImageFunctionsView(Listener* listener) 639 : 640 BGroupView(B_VERTICAL), 641 fImageDebugInfo(NULL), 642 fFilterField(NULL), 643 fFunctionsTable(NULL), 644 fFunctionsTableModel(NULL), 645 fListener(listener), 646 fHighlightingColumn(NULL), 647 fLastFilterKeypress(0) 648 { 649 SetName("Functions"); 650 } 651 652 653 ImageFunctionsView::~ImageFunctionsView() 654 { 655 SetImageDebugInfo(NULL); 656 fFunctionsTable->SetTreeTableModel(NULL); 657 delete fFunctionsTableModel; 658 } 659 660 661 /*static*/ ImageFunctionsView* 662 ImageFunctionsView::Create(Listener* listener) 663 { 664 ImageFunctionsView* self = new ImageFunctionsView(listener); 665 666 try { 667 self->_Init(); 668 } catch (...) { 669 delete self; 670 throw; 671 } 672 673 return self; 674 } 675 676 677 void 678 ImageFunctionsView::UnsetListener() 679 { 680 fListener = NULL; 681 } 682 683 684 void 685 ImageFunctionsView::SetImageDebugInfo(ImageDebugInfo* imageDebugInfo) 686 { 687 if (imageDebugInfo == fImageDebugInfo) 688 return; 689 690 TRACE_GUI("ImageFunctionsView::SetImageDebugInfo(%p)\n", imageDebugInfo); 691 692 if (fImageDebugInfo != NULL) 693 fImageDebugInfo->ReleaseReference(); 694 695 fImageDebugInfo = imageDebugInfo; 696 697 if (fImageDebugInfo != NULL) 698 fImageDebugInfo->AcquireReference(); 699 700 fFunctionsTableModel->SetImageDebugInfo(fImageDebugInfo); 701 702 // If there's only one source file (i.e. "no source file"), expand the item. 703 if (fImageDebugInfo != NULL 704 && fFunctionsTableModel->CountChildren(fFunctionsTableModel) == 1) { 705 TreeTablePath path; 706 path.AddComponent(0); 707 fFunctionsTable->SetNodeExpanded(path, true, false); 708 } 709 710 TRACE_GUI("ImageFunctionsView::SetImageDebugInfo(%p) done\n", 711 imageDebugInfo); 712 } 713 714 715 void 716 ImageFunctionsView::SetFunction(FunctionInstance* function) 717 { 718 TRACE_GUI("ImageFunctionsView::SetFunction(%p)\n", function); 719 720 TreeTablePath path; 721 if (fFunctionsTableModel->GetFunctionPath(function, path)) { 722 fFunctionsTable->SetNodeExpanded(path, true, true); 723 fFunctionsTable->SelectNode(path, false); 724 fFunctionsTable->ScrollToNode(path); 725 } else 726 fFunctionsTable->DeselectAllNodes(); 727 } 728 729 730 void 731 ImageFunctionsView::AttachedToWindow() 732 { 733 BView::AttachedToWindow(); 734 735 fFilterField->SetTarget(this); 736 } 737 738 739 void 740 ImageFunctionsView::MessageReceived(BMessage* message) 741 { 742 switch (message->what) { 743 case MSG_FUNCTION_FILTER_CHANGED: 744 { 745 fLastFilterKeypress = system_time(); 746 BMessage keypressMessage(MSG_FUNCTION_TYPING_TIMEOUT); 747 BMessageRunner::StartSending(BMessenger(this), &keypressMessage, 748 kKeypressTimeout, 1); 749 break; 750 } 751 752 case MSG_FUNCTION_TYPING_TIMEOUT: 753 { 754 if (system_time() - fLastFilterKeypress >= kKeypressTimeout) { 755 fFunctionsTableModel->SetFilter(fFilterField->Text()); 756 fHighlightingColumn->SetHasFilter( 757 fFilterField->TextView()->TextLength() > 0); 758 _ExpandFilteredNodes(); 759 } 760 break; 761 } 762 763 default: 764 BView::MessageReceived(message); 765 break; 766 } 767 } 768 769 770 void 771 ImageFunctionsView::LoadSettings(const BMessage& settings) 772 { 773 BMessage tableSettings; 774 if (settings.FindMessage("functionsTable", &tableSettings) == B_OK) { 775 GuiSettingsUtils::UnarchiveTableSettings(tableSettings, 776 fFunctionsTable); 777 } 778 } 779 780 781 status_t 782 ImageFunctionsView::SaveSettings(BMessage& settings) 783 { 784 settings.MakeEmpty(); 785 786 BMessage tableSettings; 787 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings, 788 fFunctionsTable); 789 if (result == B_OK) 790 result = settings.AddMessage("functionsTable", &tableSettings); 791 792 return result; 793 } 794 795 796 void 797 ImageFunctionsView::TreeTableSelectionChanged(TreeTable* table) 798 { 799 if (fListener == NULL) 800 return; 801 802 LocatableFile* sourceFile = NULL; 803 FunctionInstance* function = NULL; 804 TreeTablePath path; 805 if (table->SelectionModel()->GetPathAt(0, path)) 806 fFunctionsTableModel->GetObjectForPath(path, sourceFile, function); 807 808 fListener->FunctionSelectionChanged(function); 809 } 810 811 812 void 813 ImageFunctionsView::_Init() 814 { 815 fFunctionsTable = new TreeTable("functions", 0, B_FANCY_BORDER); 816 fFunctionsTable->SetFont(B_FONT_ROW, be_fixed_font); 817 AddChild(fFunctionsTable->ToView()); 818 AddChild(fFilterField = new BTextControl("filtertext", "Filter:", 819 NULL, NULL)); 820 821 fFilterField->SetModificationMessage(new BMessage( 822 MSG_FUNCTION_FILTER_CHANGED)); 823 fFunctionsTable->SetSortingEnabled(false); 824 825 float addressWidth = be_plain_font->StringWidth("0x00000000") 826 + be_control_look->DefaultLabelSpacing() * 3; 827 828 // columns 829 fFunctionsTable->AddColumn(fHighlightingColumn 830 = new HighlightingTableColumn(0, "File/Function", 300, 100, 1000, 831 B_TRUNCATE_BEGINNING, B_ALIGN_LEFT)); 832 fFunctionsTable->AddColumn(new TargetAddressTableColumn(1, "Address", 833 addressWidth, 40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT)); 834 835 fFunctionsTableModel = new FunctionsTableModel(); 836 fFunctionsTable->SetTreeTableModel(fFunctionsTableModel); 837 838 fFunctionsTable->SetSelectionMode(B_SINGLE_SELECTION_LIST); 839 fFunctionsTable->AddTreeTableListener(this); 840 } 841 842 843 void 844 ImageFunctionsView::_ExpandFilteredNodes() 845 { 846 if (fFilterField->TextView()->TextLength() == 0) 847 return; 848 849 for (int32 i = 0; i < fFunctionsTableModel->CountChildren( 850 fFunctionsTableModel); i++) { 851 // only expand nodes if the match actually hit a function, 852 // and not just the containing path. 853 if (fFunctionsTableModel->CountChildren(fFunctionsTableModel) == 1 854 || fFunctionsTableModel->HasMatchingChildAt(fFunctionsTableModel, 855 i)) { 856 TreeTablePath path; 857 path.AddComponent(i); 858 fFunctionsTable->SetNodeExpanded(path, true, true); 859 } 860 } 861 } 862 863 864 // #pragma mark - Listener 865 866 867 ImageFunctionsView::Listener::~Listener() 868 { 869 } 870