1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2011-2013, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "VariablesView.h" 9 10 #include <stdio.h> 11 12 #include <new> 13 14 #include <debugger.h> 15 16 #include <Alert.h> 17 #include <Looper.h> 18 #include <PopUpMenu.h> 19 #include <ToolTip.h> 20 21 #include <AutoDeleter.h> 22 #include <AutoLocker.h> 23 #include <PromptWindow.h> 24 25 #include "table/TableColumns.h" 26 27 #include "ActionMenuItem.h" 28 #include "Architecture.h" 29 #include "FileSourceCode.h" 30 #include "Function.h" 31 #include "FunctionID.h" 32 #include "FunctionInstance.h" 33 #include "GuiSettingsUtils.h" 34 #include "MessageCodes.h" 35 #include "RangeList.h" 36 #include "Register.h" 37 #include "SettingsMenu.h" 38 #include "SourceLanguage.h" 39 #include "StackTrace.h" 40 #include "StackFrame.h" 41 #include "StackFrameValues.h" 42 #include "TableCellValueRenderer.h" 43 #include "Team.h" 44 #include "TeamDebugInfo.h" 45 #include "Thread.h" 46 #include "Tracing.h" 47 #include "TypeComponentPath.h" 48 #include "TypeHandlerRoster.h" 49 #include "TypeLookupConstraints.h" 50 #include "UiUtils.h" 51 #include "Value.h" 52 #include "ValueHandler.h" 53 #include "ValueHandlerRoster.h" 54 #include "ValueLocation.h" 55 #include "ValueNode.h" 56 #include "ValueNodeManager.h" 57 #include "Variable.h" 58 #include "VariableValueNodeChild.h" 59 #include "VariablesViewState.h" 60 #include "VariablesViewStateHistory.h" 61 62 63 enum { 64 VALUE_NODE_TYPE = 'valn' 65 }; 66 67 68 enum { 69 MSG_MODEL_NODE_HIDDEN = 'monh', 70 MSG_VALUE_NODE_NEEDS_VALUE = 'mvnv', 71 MSG_RESTORE_PARTIAL_VIEW_STATE = 'mpvs' 72 }; 73 74 75 // maximum number of array elements to show by default 76 static const uint64 kMaxArrayElementCount = 10; 77 78 79 class VariablesView::ContainerListener : public ValueNodeContainer::Listener { 80 public: 81 ContainerListener(BHandler* indirectTarget); 82 83 void SetModel(VariableTableModel* model); 84 85 virtual void ValueNodeChanged(ValueNodeChild* nodeChild, 86 ValueNode* oldNode, ValueNode* newNode); 87 virtual void ValueNodeChildrenCreated(ValueNode* node); 88 virtual void ValueNodeChildrenDeleted(ValueNode* node); 89 virtual void ValueNodeValueChanged(ValueNode* node); 90 91 virtual void ModelNodeHidden(ModelNode* node); 92 93 virtual void ModelNodeValueRequested(ModelNode* node); 94 95 virtual void ModelNodeRestoreViewStateRequested(ModelNode* node); 96 97 private: 98 BHandler* fIndirectTarget; 99 VariableTableModel* fModel; 100 }; 101 102 103 class VariablesView::ModelNode : public BReferenceable { 104 public: 105 ModelNode(ModelNode* parent, Variable* variable, ValueNodeChild* nodeChild, 106 bool isPresentationNode) 107 : 108 fParent(parent), 109 fNodeChild(nodeChild), 110 fVariable(variable), 111 fValue(NULL), 112 fValueHandler(NULL), 113 fTableCellRenderer(NULL), 114 fLastRendererSettings(), 115 fCastedType(NULL), 116 fComponentPath(NULL), 117 fIsPresentationNode(isPresentationNode), 118 fHidden(false) 119 { 120 fNodeChild->AcquireReference(); 121 } 122 123 ~ModelNode() 124 { 125 SetTableCellRenderer(NULL); 126 SetValueHandler(NULL); 127 SetValue(NULL); 128 129 for (int32 i = 0; ModelNode* child = fChildren.ItemAt(i); i++) 130 child->ReleaseReference(); 131 132 fNodeChild->ReleaseReference(); 133 134 if (fComponentPath != NULL) 135 fComponentPath->ReleaseReference(); 136 137 if (fCastedType != NULL) 138 fCastedType->ReleaseReference(); 139 } 140 141 status_t Init() 142 { 143 fComponentPath = new(std::nothrow) TypeComponentPath(); 144 if (fComponentPath == NULL) 145 return B_NO_MEMORY; 146 147 if (fParent != NULL) 148 *fComponentPath = *fParent->GetPath(); 149 150 TypeComponent component; 151 // TODO: this should actually discriminate between different 152 // classes of type component kinds 153 component.SetToBaseType(fNodeChild->GetType()->Kind(), 154 0, fNodeChild->Name()); 155 156 fComponentPath->AddComponent(component); 157 158 return B_OK; 159 } 160 161 ModelNode* Parent() const 162 { 163 return fParent; 164 } 165 166 ValueNodeChild* NodeChild() const 167 { 168 return fNodeChild; 169 } 170 171 const BString& Name() const 172 { 173 return fNodeChild->Name(); 174 } 175 176 Type* GetType() const 177 { 178 if (fCastedType != NULL) 179 return fCastedType; 180 181 return fNodeChild->GetType(); 182 } 183 184 Variable* GetVariable() const 185 { 186 return fVariable; 187 } 188 189 Value* GetValue() const 190 { 191 return fValue; 192 } 193 194 void SetValue(Value* value) 195 { 196 if (value == fValue) 197 return; 198 199 if (fValue != NULL) 200 fValue->ReleaseReference(); 201 202 fValue = value; 203 204 if (fValue != NULL) 205 fValue->AcquireReference(); 206 } 207 208 Type* GetCastedType() const 209 { 210 return fCastedType; 211 } 212 213 void SetCastedType(Type* type) 214 { 215 if (fCastedType != NULL) 216 fCastedType->ReleaseReference(); 217 218 fCastedType = type; 219 if (type != NULL) 220 fCastedType->AcquireReference(); 221 } 222 223 const BMessage& GetLastRendererSettings() const 224 { 225 return fLastRendererSettings; 226 } 227 228 void SetLastRendererSettings(const BMessage& settings) 229 { 230 fLastRendererSettings = settings; 231 } 232 233 TypeComponentPath* GetPath() const 234 { 235 return fComponentPath; 236 } 237 238 ValueHandler* GetValueHandler() const 239 { 240 return fValueHandler; 241 } 242 243 void SetValueHandler(ValueHandler* handler) 244 { 245 if (handler == fValueHandler) 246 return; 247 248 if (fValueHandler != NULL) 249 fValueHandler->ReleaseReference(); 250 251 fValueHandler = handler; 252 253 if (fValueHandler != NULL) 254 fValueHandler->AcquireReference(); 255 } 256 257 258 TableCellValueRenderer* TableCellRenderer() const 259 { 260 return fTableCellRenderer; 261 } 262 263 void SetTableCellRenderer(TableCellValueRenderer* renderer) 264 { 265 if (renderer == fTableCellRenderer) 266 return; 267 268 if (fTableCellRenderer != NULL) 269 fTableCellRenderer->ReleaseReference(); 270 271 fTableCellRenderer = renderer; 272 273 if (fTableCellRenderer != NULL) 274 fTableCellRenderer->AcquireReference(); 275 } 276 277 bool IsPresentationNode() const 278 { 279 return fIsPresentationNode; 280 } 281 282 bool IsHidden() const 283 { 284 return fHidden; 285 } 286 287 void SetHidden(bool hidden) 288 { 289 fHidden = hidden; 290 } 291 292 int32 CountChildren() const 293 { 294 return fChildren.CountItems(); 295 } 296 297 ModelNode* ChildAt(int32 index) const 298 { 299 return fChildren.ItemAt(index); 300 } 301 302 int32 IndexOf(ModelNode* child) const 303 { 304 return fChildren.IndexOf(child); 305 } 306 307 bool AddChild(ModelNode* child) 308 { 309 if (!fChildren.AddItem(child)) 310 return false; 311 312 child->AcquireReference(); 313 return true; 314 } 315 316 bool RemoveChild(ModelNode* child) 317 { 318 if (!fChildren.RemoveItem(child)) 319 return false; 320 321 child->ReleaseReference(); 322 return true; 323 } 324 325 bool RemoveAllChildren() 326 { 327 for (int32 i = 0; i < fChildren.CountItems(); i++) 328 RemoveChild(fChildren.ItemAt(i)); 329 330 return true; 331 } 332 333 private: 334 typedef BObjectList<ModelNode> ChildList; 335 336 private: 337 ModelNode* fParent; 338 ValueNodeChild* fNodeChild; 339 Variable* fVariable; 340 Value* fValue; 341 ValueHandler* fValueHandler; 342 TableCellValueRenderer* fTableCellRenderer; 343 BMessage fLastRendererSettings; 344 Type* fCastedType; 345 ChildList fChildren; 346 TypeComponentPath* fComponentPath; 347 bool fIsPresentationNode; 348 bool fHidden; 349 350 public: 351 ModelNode* fNext; 352 }; 353 354 355 // #pragma mark - VariableValueColumn 356 357 358 class VariablesView::VariableValueColumn : public StringTableColumn { 359 public: 360 VariableValueColumn(int32 modelIndex, const char* title, float width, 361 float minWidth, float maxWidth, uint32 truncate = B_TRUNCATE_MIDDLE, 362 alignment align = B_ALIGN_RIGHT) 363 : 364 StringTableColumn(modelIndex, title, width, minWidth, maxWidth, 365 truncate, align) 366 { 367 } 368 369 protected: 370 void DrawValue(const BVariant& value, BRect rect, BView* targetView) 371 { 372 // draw the node's value with the designated renderer 373 if (value.Type() == VALUE_NODE_TYPE) { 374 ModelNode* node = dynamic_cast<ModelNode*>(value.ToReferenceable()); 375 if (node != NULL && node->GetValue() != NULL 376 && node->TableCellRenderer() != NULL) { 377 node->TableCellRenderer()->RenderValue(node->GetValue(), rect, 378 targetView); 379 return; 380 } 381 } else if (value.Type() == B_STRING_TYPE) { 382 fField.SetString(value.ToString()); 383 } else { 384 // fall back to drawing an empty string 385 fField.SetString(""); 386 } 387 fField.SetWidth(Width()); 388 fColumn.DrawField(&fField, rect, targetView); 389 } 390 391 float GetPreferredWidth(const BVariant& value, BView* targetView) const 392 { 393 // get the preferred width from the node's designated renderer 394 if (value.Type() == VALUE_NODE_TYPE) { 395 ModelNode* node = dynamic_cast<ModelNode*>(value.ToReferenceable()); 396 if (node != NULL && node->GetValue() != NULL 397 && node->TableCellRenderer() != NULL) { 398 return node->TableCellRenderer()->PreferredValueWidth( 399 node->GetValue(), targetView); 400 } 401 } 402 403 return fColumn.BTitledColumn::GetPreferredWidth(NULL, targetView); 404 } 405 406 virtual BField* PrepareField(const BVariant& _value) const 407 { 408 return NULL; 409 } 410 }; 411 412 413 // #pragma mark - VariableTableModel 414 415 416 class VariablesView::VariableTableModel : public TreeTableModel, 417 public TreeTableToolTipProvider { 418 public: 419 VariableTableModel(); 420 ~VariableTableModel(); 421 422 status_t Init(); 423 424 void SetContainerListener( 425 ContainerListener* listener); 426 427 void SetStackFrame(Thread* thread, 428 StackFrame* stackFrame); 429 430 void ValueNodeChanged(ValueNodeChild* nodeChild, 431 ValueNode* oldNode, ValueNode* newNode); 432 void ValueNodeChildrenCreated(ValueNode* node); 433 void ValueNodeChildrenDeleted(ValueNode* node); 434 void ValueNodeValueChanged(ValueNode* node); 435 436 virtual int32 CountColumns() const; 437 virtual void* Root() const; 438 virtual int32 CountChildren(void* parent) const; 439 virtual void* ChildAt(void* parent, int32 index) const; 440 virtual bool GetValueAt(void* object, int32 columnIndex, 441 BVariant& _value); 442 443 bool GetTreePath(ModelNode* node, 444 TreeTablePath& _path) const; 445 446 void NodeExpanded(ModelNode* node); 447 448 void NotifyNodeChanged(ModelNode* node); 449 void NotifyNodeHidden(ModelNode* node); 450 451 virtual bool GetToolTipForTablePath( 452 const TreeTablePath& path, 453 int32 columnIndex, BToolTip** _tip); 454 455 private: 456 struct NodeHashDefinition { 457 typedef ValueNodeChild* KeyType; 458 typedef ModelNode ValueType; 459 460 size_t HashKey(const ValueNodeChild* key) const 461 { 462 return (size_t)key; 463 } 464 465 size_t Hash(const ModelNode* value) const 466 { 467 return HashKey(value->NodeChild()); 468 } 469 470 bool Compare(const ValueNodeChild* key, 471 const ModelNode* value) const 472 { 473 return value->NodeChild() == key; 474 } 475 476 ModelNode*& GetLink(ModelNode* value) const 477 { 478 return value->fNext; 479 } 480 }; 481 482 typedef BObjectList<ModelNode> NodeList; 483 typedef BOpenHashTable<NodeHashDefinition> NodeTable; 484 485 private: 486 // container must be locked 487 488 status_t _AddNode(Variable* variable, ModelNode* parent, 489 ValueNodeChild* nodeChild, 490 bool isPresentationNode = false, 491 bool isOnlyChild = false); 492 493 private: 494 Thread* fThread; 495 ValueNodeManager* fNodeManager; 496 ContainerListener* fContainerListener; 497 NodeList fNodes; 498 NodeTable fNodeTable; 499 }; 500 501 502 class VariablesView::ContextMenu : public BPopUpMenu { 503 public: 504 ContextMenu(const BMessenger& parent, const char* name) 505 : BPopUpMenu(name, false, false), 506 fParent(parent) 507 { 508 } 509 510 virtual void Hide() 511 { 512 BPopUpMenu::Hide(); 513 514 BMessage message(MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE); 515 message.AddPointer("menu", this); 516 fParent.SendMessage(&message); 517 } 518 519 private: 520 BMessenger fParent; 521 }; 522 523 524 // #pragma mark - TableCellContextMenuTracker 525 526 527 class VariablesView::TableCellContextMenuTracker : public BReferenceable, 528 Settings::Listener { 529 public: 530 TableCellContextMenuTracker(ModelNode* node, BLooper* parentLooper, 531 const BMessenger& parent) 532 : 533 fNode(node), 534 fParentLooper(parentLooper), 535 fParent(parent), 536 fRendererSettings(NULL), 537 fRendererSettingsMenu(NULL), 538 fRendererMenuAdded(false), 539 fMenuPreparedToShow(false) 540 { 541 fNode->AcquireReference(); 542 } 543 544 ~TableCellContextMenuTracker() 545 { 546 FinishMenu(true); 547 548 if (fRendererSettingsMenu != NULL) 549 fRendererSettingsMenu->ReleaseReference(); 550 551 if (fRendererSettings != NULL) 552 fRendererSettings->ReleaseReference(); 553 554 fNode->ReleaseReference(); 555 } 556 557 status_t Init(Settings* rendererSettings, 558 SettingsMenu* rendererSettingsMenu, 559 ContextActionList* preSettingsActions = NULL, 560 ContextActionList* postSettingsActions = NULL) 561 { 562 if (rendererSettings == NULL && preSettingsActions == NULL 563 && postSettingsActions == NULL) { 564 return B_BAD_VALUE; 565 } 566 567 if (rendererSettings != NULL) { 568 fRendererSettings = rendererSettings; 569 fRendererSettings->AcquireReference(); 570 571 572 fRendererSettingsMenu = rendererSettingsMenu; 573 fRendererSettingsMenu->AcquireReference(); 574 } 575 576 fContextMenu = new(std::nothrow) ContextMenu(fParent, 577 "table cell settings popup"); 578 if (fContextMenu == NULL) 579 return B_NO_MEMORY; 580 581 status_t error = B_OK; 582 if (preSettingsActions != NULL 583 && preSettingsActions->CountItems() > 0) { 584 error = _AddActionItems(preSettingsActions); 585 if (error != B_OK) 586 return error; 587 588 if (fRendererSettingsMenu != NULL || postSettingsActions != NULL) 589 fContextMenu->AddSeparatorItem(); 590 } 591 592 if (fRendererSettingsMenu != NULL) { 593 error = fRendererSettingsMenu->AddToMenu(fContextMenu, 594 fContextMenu->CountItems()); 595 if (error != B_OK) 596 return error; 597 598 if (postSettingsActions != NULL) 599 fContextMenu->AddSeparatorItem(); 600 } 601 602 if (postSettingsActions != NULL) { 603 error = _AddActionItems(postSettingsActions); 604 if (error != B_OK) 605 return error; 606 607 } 608 609 if (fRendererSettings != NULL) { 610 AutoLocker<Settings> settingsLocker(fRendererSettings); 611 fRendererSettings->AddListener(this); 612 fRendererMenuAdded = true; 613 } 614 615 return B_OK; 616 } 617 618 void ShowMenu(BPoint screenWhere) 619 { 620 if (fRendererMenuAdded) 621 fRendererSettingsMenu->PrepareToShow(fParentLooper); 622 623 for (int32 i = 0; i < fContextMenu->CountItems(); i++) { 624 ActionMenuItem* item = dynamic_cast<ActionMenuItem*>( 625 fContextMenu->ItemAt(i)); 626 if (item != NULL) 627 item->PrepareToShow(fParentLooper, fParent.Target(NULL)); 628 } 629 630 fMenuPreparedToShow = true; 631 632 BRect mouseRect(screenWhere, screenWhere); 633 mouseRect.InsetBy(-4.0, -4.0); 634 fContextMenu->Go(screenWhere, true, false, mouseRect, true); 635 } 636 637 bool FinishMenu(bool force) 638 { 639 bool stillActive = false; 640 641 if (fMenuPreparedToShow) { 642 if (fRendererMenuAdded) 643 stillActive = fRendererSettingsMenu->Finish(fParentLooper, 644 force); 645 for (int32 i = 0; i < fContextMenu->CountItems(); i++) { 646 ActionMenuItem* item = dynamic_cast<ActionMenuItem*>( 647 fContextMenu->ItemAt(i)); 648 if (item != NULL) { 649 stillActive |= item->Finish(fParentLooper, 650 fParent.Target(NULL), force); 651 } 652 } 653 654 fMenuPreparedToShow = stillActive; 655 } 656 657 if (fRendererMenuAdded) { 658 fRendererSettingsMenu->RemoveFromMenu(); 659 fRendererSettings->RemoveListener(this); 660 fRendererMenuAdded = false; 661 } 662 663 if (fContextMenu != NULL) { 664 delete fContextMenu; 665 fContextMenu = NULL; 666 } 667 668 return stillActive; 669 } 670 671 private: 672 // Settings::Listener 673 674 virtual void SettingValueChanged(Setting* setting) 675 { 676 BMessage message(MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED); 677 fNode->AcquireReference(); 678 if (message.AddPointer("node", fNode) != B_OK 679 || fParent.SendMessage(&message) != B_OK) { 680 fNode->ReleaseReference(); 681 } 682 } 683 684 status_t _AddActionItems(ContextActionList* actions) 685 { 686 if (fContextMenu == NULL) 687 return B_BAD_VALUE; 688 689 int32 index = fContextMenu->CountItems(); 690 for (int32 i = 0; ActionMenuItem* item = actions->ItemAt(i); i++) { 691 if (!fContextMenu->AddItem(item, index + i)) { 692 for (i--; i >= 0; i--) 693 fContextMenu->RemoveItem(fContextMenu->ItemAt(index + i)); 694 695 return B_NO_MEMORY; 696 } 697 } 698 699 return B_OK; 700 } 701 702 private: 703 ModelNode* fNode; 704 BLooper* fParentLooper; 705 BMessenger fParent; 706 ContextMenu* fContextMenu; 707 Settings* fRendererSettings; 708 SettingsMenu* fRendererSettingsMenu; 709 bool fRendererMenuAdded; 710 bool fMenuPreparedToShow; 711 }; 712 713 714 // #pragma mark - ContainerListener 715 716 717 VariablesView::ContainerListener::ContainerListener(BHandler* indirectTarget) 718 : 719 fIndirectTarget(indirectTarget), 720 fModel(NULL) 721 { 722 } 723 724 725 void 726 VariablesView::ContainerListener::SetModel(VariableTableModel* model) 727 { 728 fModel = model; 729 } 730 731 732 void 733 VariablesView::ContainerListener::ValueNodeChanged(ValueNodeChild* nodeChild, 734 ValueNode* oldNode, ValueNode* newNode) 735 { 736 // If the looper is already locked, invoke the model's hook synchronously. 737 if (fIndirectTarget->Looper()->IsLocked()) { 738 fModel->ValueNodeChanged(nodeChild, oldNode, newNode); 739 return; 740 } 741 742 // looper not locked yet -- call asynchronously to avoid reverse locking 743 // order 744 BReference<ValueNodeChild> nodeChildReference(nodeChild); 745 BReference<ValueNode> oldNodeReference(oldNode); 746 BReference<ValueNode> newNodeReference(newNode); 747 748 BMessage message(MSG_VALUE_NODE_CHANGED); 749 if (message.AddPointer("nodeChild", nodeChild) == B_OK 750 && message.AddPointer("oldNode", oldNode) == B_OK 751 && message.AddPointer("newNode", newNode) == B_OK 752 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 753 == B_OK) { 754 nodeChildReference.Detach(); 755 oldNodeReference.Detach(); 756 newNodeReference.Detach(); 757 } 758 } 759 760 761 void 762 VariablesView::ContainerListener::ValueNodeChildrenCreated(ValueNode* node) 763 { 764 // If the looper is already locked, invoke the model's hook synchronously. 765 if (fIndirectTarget->Looper()->IsLocked()) { 766 fModel->ValueNodeChildrenCreated(node); 767 return; 768 } 769 770 // looper not locked yet -- call asynchronously to avoid reverse locking 771 // order 772 BReference<ValueNode> nodeReference(node); 773 774 BMessage message(MSG_VALUE_NODE_CHILDREN_CREATED); 775 if (message.AddPointer("node", node) == B_OK 776 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 777 == B_OK) { 778 nodeReference.Detach(); 779 } 780 } 781 782 783 void 784 VariablesView::ContainerListener::ValueNodeChildrenDeleted(ValueNode* node) 785 { 786 // If the looper is already locked, invoke the model's hook synchronously. 787 if (fIndirectTarget->Looper()->IsLocked()) { 788 fModel->ValueNodeChildrenDeleted(node); 789 return; 790 } 791 792 // looper not locked yet -- call asynchronously to avoid reverse locking 793 // order 794 BReference<ValueNode> nodeReference(node); 795 796 BMessage message(MSG_VALUE_NODE_CHILDREN_DELETED); 797 if (message.AddPointer("node", node) == B_OK 798 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 799 == B_OK) { 800 nodeReference.Detach(); 801 } 802 } 803 804 805 void 806 VariablesView::ContainerListener::ValueNodeValueChanged(ValueNode* node) 807 { 808 // If the looper is already locked, invoke the model's hook synchronously. 809 if (fIndirectTarget->Looper()->IsLocked()) { 810 fModel->ValueNodeValueChanged(node); 811 return; 812 } 813 814 // looper not locked yet -- call asynchronously to avoid reverse locking 815 // order 816 BReference<ValueNode> nodeReference(node); 817 818 BMessage message(MSG_VALUE_NODE_VALUE_CHANGED); 819 if (message.AddPointer("node", node) == B_OK 820 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 821 == B_OK) { 822 nodeReference.Detach(); 823 } 824 } 825 826 827 void 828 VariablesView::ContainerListener::ModelNodeHidden(ModelNode* node) 829 { 830 BReference<ModelNode> nodeReference(node); 831 832 BMessage message(MSG_MODEL_NODE_HIDDEN); 833 if (message.AddPointer("node", node) == B_OK 834 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 835 == B_OK) { 836 nodeReference.Detach(); 837 } 838 } 839 840 841 void 842 VariablesView::ContainerListener::ModelNodeValueRequested(ModelNode* node) 843 { 844 BReference<ModelNode> nodeReference(node); 845 846 BMessage message(MSG_VALUE_NODE_NEEDS_VALUE); 847 if (message.AddPointer("node", node) == B_OK 848 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 849 == B_OK) { 850 nodeReference.Detach(); 851 } 852 } 853 854 855 void 856 VariablesView::ContainerListener::ModelNodeRestoreViewStateRequested( 857 ModelNode* node) 858 { 859 BReference<ModelNode> nodeReference(node); 860 861 BMessage message(MSG_RESTORE_PARTIAL_VIEW_STATE); 862 if (message.AddPointer("node", node) == B_OK 863 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 864 == B_OK) { 865 nodeReference.Detach(); 866 } 867 } 868 869 870 // #pragma mark - VariableTableModel 871 872 873 VariablesView::VariableTableModel::VariableTableModel() 874 : 875 fThread(NULL), 876 fNodeManager(NULL), 877 fContainerListener(NULL), 878 fNodeTable() 879 { 880 } 881 882 883 VariablesView::VariableTableModel::~VariableTableModel() 884 { 885 if (fNodeManager != NULL) 886 fNodeManager->ReleaseReference(); 887 } 888 889 890 status_t 891 VariablesView::VariableTableModel::Init() 892 { 893 fNodeManager = new(std::nothrow) ValueNodeManager(); 894 if (fNodeManager == NULL) 895 return B_NO_MEMORY; 896 897 return fNodeTable.Init(); 898 } 899 900 901 void 902 VariablesView::VariableTableModel::SetContainerListener( 903 ContainerListener* listener) 904 { 905 if (listener == fContainerListener) 906 return; 907 908 if (fContainerListener != NULL) { 909 if (fNodeManager != NULL) 910 fNodeManager->RemoveListener(fContainerListener); 911 912 fContainerListener->SetModel(NULL); 913 } 914 915 fContainerListener = listener; 916 917 if (fContainerListener != NULL) { 918 fContainerListener->SetModel(this); 919 920 if (fNodeManager != NULL) 921 fNodeManager->AddListener(fContainerListener); 922 } 923 } 924 925 926 void 927 VariablesView::VariableTableModel::SetStackFrame(Thread* thread, 928 StackFrame* stackFrame) 929 { 930 fThread = thread; 931 932 fNodeManager->SetStackFrame(thread, stackFrame); 933 934 int32 count = fNodes.CountItems(); 935 fNodeTable.Clear(true); 936 937 if (!fNodes.IsEmpty()) { 938 for (int32 i = 0; i < count; i++) 939 fNodes.ItemAt(i)->ReleaseReference(); 940 fNodes.MakeEmpty(); 941 } 942 943 NotifyNodesRemoved(TreeTablePath(), 0, count); 944 945 if (stackFrame == NULL) 946 return; 947 948 ValueNodeContainer* container = fNodeManager->GetContainer(); 949 AutoLocker<ValueNodeContainer> containerLocker(container); 950 951 for (int32 i = 0; i < container->CountChildren(); i++) { 952 VariableValueNodeChild* child = dynamic_cast<VariableValueNodeChild *>( 953 container->ChildAt(i)); 954 _AddNode(child->GetVariable(), NULL, child); 955 // top level nodes get their children added immediately 956 // so those won't invoke our callback hook. Add them directly here. 957 ValueNodeChildrenCreated(child->Node()); 958 } 959 } 960 961 962 void 963 VariablesView::VariableTableModel::ValueNodeChanged(ValueNodeChild* nodeChild, 964 ValueNode* oldNode, ValueNode* newNode) 965 { 966 AutoLocker<ValueNodeContainer> containerLocker( 967 fNodeManager->GetContainer()); 968 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 969 if (modelNode == NULL) 970 return; 971 972 if (oldNode != NULL) { 973 ValueNodeChildrenDeleted(oldNode); 974 NotifyNodeChanged(modelNode); 975 } 976 } 977 978 979 void 980 VariablesView::VariableTableModel::ValueNodeChildrenCreated( 981 ValueNode* valueNode) 982 { 983 AutoLocker<ValueNodeContainer> containerLocker( 984 fNodeManager->GetContainer()); 985 986 // check whether we know the node 987 ValueNodeChild* nodeChild = valueNode->NodeChild(); 988 if (nodeChild == NULL) 989 return; 990 991 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 992 if (modelNode == NULL) 993 return; 994 995 // Iterate through the children and create model nodes for the ones we 996 // don't know yet. 997 int32 childCount = valueNode->CountChildren(); 998 for (int32 i = 0; i < childCount; i++) { 999 ValueNodeChild* child = valueNode->ChildAt(i); 1000 if (fNodeTable.Lookup(child) == NULL) { 1001 _AddNode(modelNode->GetVariable(), modelNode, child, 1002 child->IsInternal(), childCount == 1); 1003 } 1004 1005 ModelNode* childNode = fNodeTable.Lookup(child); 1006 if (childNode != NULL) 1007 fContainerListener->ModelNodeValueRequested(childNode); 1008 } 1009 1010 if (valueNode->ChildCreationNeedsValue()) 1011 fContainerListener->ModelNodeRestoreViewStateRequested(modelNode); 1012 } 1013 1014 1015 void 1016 VariablesView::VariableTableModel::ValueNodeChildrenDeleted(ValueNode* node) 1017 { 1018 AutoLocker<ValueNodeContainer> containerLocker( 1019 fNodeManager->GetContainer()); 1020 1021 // check whether we know the node 1022 ValueNodeChild* nodeChild = node->NodeChild(); 1023 if (nodeChild == NULL) 1024 return; 1025 1026 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1027 if (modelNode == NULL) 1028 return; 1029 1030 // in the case of an address node with a hidden child, 1031 // we want to send removal notifications for the children 1032 // instead. 1033 BReference<ModelNode> hiddenChild; 1034 if (modelNode->CountChildren() == 1 1035 && modelNode->ChildAt(0)->IsHidden()) { 1036 hiddenChild.SetTo(modelNode->ChildAt(0)); 1037 modelNode->RemoveChild(hiddenChild); 1038 modelNode = hiddenChild; 1039 fNodeTable.Remove(hiddenChild); 1040 } 1041 1042 for (int32 i = modelNode->CountChildren() - 1; i >= 0 ; i--) { 1043 BReference<ModelNode> childNode = modelNode->ChildAt(i); 1044 // recursively remove the current node's child hierarchy. 1045 if (childNode->CountChildren() != 0) 1046 ValueNodeChildrenDeleted(childNode->NodeChild()->Node()); 1047 1048 TreeTablePath treePath; 1049 if (GetTreePath(childNode, treePath)) { 1050 int32 index = treePath.RemoveLastComponent(); 1051 NotifyNodesRemoved(treePath, index, 1); 1052 } 1053 modelNode->RemoveChild(childNode); 1054 fNodeTable.Remove(childNode); 1055 } 1056 } 1057 1058 1059 void 1060 VariablesView::VariableTableModel::ValueNodeValueChanged(ValueNode* valueNode) 1061 { 1062 AutoLocker<ValueNodeContainer> containerLocker( 1063 fNodeManager->GetContainer()); 1064 1065 // check whether we know the node 1066 ValueNodeChild* nodeChild = valueNode->NodeChild(); 1067 if (nodeChild == NULL) 1068 return; 1069 1070 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1071 if (modelNode == NULL) 1072 return; 1073 1074 // check whether the value actually changed 1075 Value* value = valueNode->GetValue(); 1076 if (value == modelNode->GetValue()) 1077 return; 1078 1079 // get a value handler 1080 ValueHandler* valueHandler; 1081 status_t error = ValueHandlerRoster::Default()->FindValueHandler(value, 1082 valueHandler); 1083 if (error != B_OK) 1084 return; 1085 BReference<ValueHandler> handlerReference(valueHandler, true); 1086 1087 // create a table cell renderer for the value 1088 TableCellValueRenderer* renderer = NULL; 1089 error = valueHandler->GetTableCellValueRenderer(value, renderer); 1090 if (error != B_OK) 1091 return; 1092 1093 // set value/handler/renderer 1094 modelNode->SetValue(value); 1095 modelNode->SetValueHandler(valueHandler); 1096 modelNode->SetTableCellRenderer(renderer); 1097 1098 // we have to restore renderer settings here since until this point 1099 // we don't yet know what renderer is in use. 1100 if (renderer != NULL) { 1101 Settings* settings = renderer->GetSettings(); 1102 if (settings != NULL) 1103 settings->RestoreValues(modelNode->GetLastRendererSettings()); 1104 } 1105 1106 // notify table model listeners 1107 NotifyNodeChanged(modelNode); 1108 } 1109 1110 1111 int32 1112 VariablesView::VariableTableModel::CountColumns() const 1113 { 1114 return 3; 1115 } 1116 1117 1118 void* 1119 VariablesView::VariableTableModel::Root() const 1120 { 1121 return (void*)this; 1122 } 1123 1124 1125 int32 1126 VariablesView::VariableTableModel::CountChildren(void* parent) const 1127 { 1128 if (parent == this) 1129 return fNodes.CountItems(); 1130 1131 // If the node only has a hidden child, pretend the node directly has the 1132 // child's children. 1133 ModelNode* modelNode = (ModelNode*)parent; 1134 int32 childCount = modelNode->CountChildren(); 1135 if (childCount == 1) { 1136 ModelNode* child = modelNode->ChildAt(0); 1137 if (child->IsHidden()) 1138 return child->CountChildren(); 1139 } 1140 1141 return childCount; 1142 } 1143 1144 1145 void* 1146 VariablesView::VariableTableModel::ChildAt(void* parent, int32 index) const 1147 { 1148 if (parent == this) 1149 return fNodes.ItemAt(index); 1150 1151 // If the node only has a hidden child, pretend the node directly has the 1152 // child's children. 1153 ModelNode* modelNode = (ModelNode*)parent; 1154 int32 childCount = modelNode->CountChildren(); 1155 if (childCount == 1) { 1156 ModelNode* child = modelNode->ChildAt(0); 1157 if (child->IsHidden()) 1158 return child->ChildAt(index); 1159 } 1160 1161 return modelNode->ChildAt(index); 1162 } 1163 1164 1165 bool 1166 VariablesView::VariableTableModel::GetValueAt(void* object, int32 columnIndex, 1167 BVariant& _value) 1168 { 1169 ModelNode* node = (ModelNode*)object; 1170 1171 switch (columnIndex) { 1172 case 0: 1173 _value.SetTo(node->Name(), B_VARIANT_DONT_COPY_DATA); 1174 return true; 1175 case 1: 1176 if (node->GetValue() == NULL) { 1177 ValueLocation* location = node->NodeChild()->Location(); 1178 if (location == NULL) 1179 return false; 1180 1181 Type* nodeChildRawType = node->NodeChild()->Node()->GetType() 1182 ->ResolveRawType(false); 1183 if (nodeChildRawType->Kind() == TYPE_COMPOUND) 1184 { 1185 if (location->CountPieces() > 1) 1186 return false; 1187 1188 BString data; 1189 ValuePieceLocation piece = location->PieceAt(0); 1190 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 1191 return false; 1192 1193 data.SetToFormat("[@ %#" B_PRIx64 "]", piece.address); 1194 _value.SetTo(data); 1195 return true; 1196 } 1197 return false; 1198 } 1199 1200 _value.SetTo(node, VALUE_NODE_TYPE); 1201 return true; 1202 case 2: 1203 { 1204 Type* type = node->GetType(); 1205 if (type == NULL) 1206 return false; 1207 1208 _value.SetTo(type->Name(), B_VARIANT_DONT_COPY_DATA); 1209 return true; 1210 } 1211 default: 1212 return false; 1213 } 1214 } 1215 1216 1217 void 1218 VariablesView::VariableTableModel::NodeExpanded(ModelNode* node) 1219 { 1220 AutoLocker<ValueNodeContainer> containerLocker( 1221 fNodeManager->GetContainer()); 1222 // add children of all children 1223 1224 // If the node only has a hidden child, add the child's children instead. 1225 if (node->CountChildren() == 1) { 1226 ModelNode* child = node->ChildAt(0); 1227 if (child->IsHidden()) 1228 node = child; 1229 } 1230 1231 // add the children 1232 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) 1233 fNodeManager->AddChildNodes(child->NodeChild()); 1234 } 1235 1236 1237 void 1238 VariablesView::VariableTableModel::NotifyNodeChanged(ModelNode* node) 1239 { 1240 if (!node->IsHidden()) { 1241 TreeTablePath treePath; 1242 if (GetTreePath(node, treePath)) { 1243 int32 index = treePath.RemoveLastComponent(); 1244 NotifyNodesChanged(treePath, index, 1); 1245 } 1246 } 1247 } 1248 1249 1250 void 1251 VariablesView::VariableTableModel::NotifyNodeHidden(ModelNode* node) 1252 { 1253 fContainerListener->ModelNodeHidden(node); 1254 } 1255 1256 1257 bool 1258 VariablesView::VariableTableModel::GetToolTipForTablePath( 1259 const TreeTablePath& path, int32 columnIndex, BToolTip** _tip) 1260 { 1261 ModelNode* node = (ModelNode*)NodeForPath(path); 1262 if (node == NULL) 1263 return false; 1264 1265 if (node->NodeChild()->LocationResolutionState() != B_OK) 1266 return false; 1267 1268 BString tipData; 1269 switch (columnIndex) { 1270 case 0: 1271 { 1272 ValueLocation* location = node->NodeChild()->Location(); 1273 for (int32 i = 0; i < location->CountPieces(); i++) { 1274 ValuePieceLocation piece = location->PieceAt(i); 1275 BString pieceData; 1276 switch (piece.type) { 1277 case VALUE_PIECE_LOCATION_MEMORY: 1278 pieceData.SetToFormat("(%" B_PRId32 "): Address: %#" 1279 B_PRIx64 ", Size: %" B_PRId64 " bytes", i, 1280 piece.address, piece.size); 1281 break; 1282 case VALUE_PIECE_LOCATION_REGISTER: 1283 { 1284 Architecture* architecture = fThread->GetTeam() 1285 ->GetArchitecture(); 1286 pieceData.SetToFormat("(%" B_PRId32 "): Register (%s)", 1287 i, architecture->Registers()[piece.reg].Name()); 1288 break; 1289 } 1290 default: 1291 break; 1292 } 1293 1294 tipData += pieceData; 1295 if (i < location->CountPieces() - 1) 1296 tipData += "\n"; 1297 } 1298 break; 1299 } 1300 case 1: 1301 { 1302 Value* value = node->GetValue(); 1303 if (value != NULL) 1304 value->ToString(tipData); 1305 1306 break; 1307 } 1308 default: 1309 break; 1310 } 1311 1312 if (tipData.IsEmpty()) 1313 return false; 1314 1315 *_tip = new(std::nothrow) BTextToolTip(tipData); 1316 if (*_tip == NULL) 1317 return false; 1318 1319 return true; 1320 } 1321 1322 1323 status_t 1324 VariablesView::VariableTableModel::_AddNode(Variable* variable, 1325 ModelNode* parent, ValueNodeChild* nodeChild, bool isPresentationNode, 1326 bool isOnlyChild) 1327 { 1328 // Don't create nodes for unspecified types -- we can't get/show their 1329 // value anyway. 1330 Type* nodeChildRawType = nodeChild->GetType()->ResolveRawType(false); 1331 if (nodeChildRawType->Kind() == TYPE_UNSPECIFIED) 1332 return B_OK; 1333 1334 ModelNode* node = new(std::nothrow) ModelNode(parent, variable, nodeChild, 1335 isPresentationNode); 1336 BReference<ModelNode> nodeReference(node, true); 1337 if (node == NULL || node->Init() != B_OK) 1338 return B_NO_MEMORY; 1339 1340 int32 childIndex; 1341 1342 if (parent != NULL) { 1343 childIndex = parent->CountChildren(); 1344 1345 if (!parent->AddChild(node)) 1346 return B_NO_MEMORY; 1347 // the parent has a reference, now 1348 } else { 1349 childIndex = fNodes.CountItems(); 1350 1351 if (!fNodes.AddItem(node)) 1352 return B_NO_MEMORY; 1353 nodeReference.Detach(); 1354 // the fNodes list has a reference, now 1355 } 1356 1357 fNodeTable.Insert(node); 1358 1359 // if an address type node has only a single child, and that child 1360 // is a compound type, mark it hidden 1361 if (isOnlyChild && parent != NULL) { 1362 ValueNode* parentValueNode = parent->NodeChild()->Node(); 1363 if (parentValueNode != NULL) { 1364 if (parentValueNode->GetType()->ResolveRawType(false)->Kind() 1365 == TYPE_ADDRESS) { 1366 type_kind childKind = nodeChildRawType->Kind(); 1367 if (childKind == TYPE_COMPOUND || childKind == TYPE_ARRAY) { 1368 node->SetHidden(true); 1369 1370 // we need to tell the listener about nodes like this so 1371 // any necessary actions can be taken for them (i.e. value 1372 // resolution), since they're otherwise invisible to 1373 // outsiders. 1374 NotifyNodeHidden(node); 1375 } 1376 } 1377 } 1378 } 1379 1380 // notify table model listeners 1381 if (!node->IsHidden()) { 1382 TreeTablePath path; 1383 if (parent == NULL || GetTreePath(parent, path)) 1384 NotifyNodesAdded(path, childIndex, 1); 1385 } 1386 1387 // if the node is hidden, add its children 1388 if (node->IsHidden()) 1389 fNodeManager->AddChildNodes(nodeChild); 1390 1391 return B_OK; 1392 } 1393 1394 1395 bool 1396 VariablesView::VariableTableModel::GetTreePath(ModelNode* node, 1397 TreeTablePath& _path) const 1398 { 1399 // recurse, if the node has a parent 1400 if (ModelNode* parent = node->Parent()) { 1401 if (!GetTreePath(parent, _path)) 1402 return false; 1403 1404 if (node->IsHidden()) 1405 return true; 1406 1407 return _path.AddComponent(parent->IndexOf(node)); 1408 } 1409 1410 // no parent -- get the index and start the path 1411 int32 index = fNodes.IndexOf(node); 1412 _path.Clear(); 1413 return index >= 0 && _path.AddComponent(index); 1414 } 1415 1416 1417 // #pragma mark - VariablesView 1418 1419 1420 VariablesView::VariablesView(Listener* listener) 1421 : 1422 BGroupView(B_VERTICAL), 1423 fThread(NULL), 1424 fStackFrame(NULL), 1425 fVariableTable(NULL), 1426 fVariableTableModel(NULL), 1427 fContainerListener(NULL), 1428 fPreviousViewState(NULL), 1429 fViewStateHistory(NULL), 1430 fTableCellContextMenuTracker(NULL), 1431 fFrameClearPending(false), 1432 fListener(listener) 1433 { 1434 SetName("Variables"); 1435 } 1436 1437 1438 VariablesView::~VariablesView() 1439 { 1440 SetStackFrame(NULL, NULL); 1441 fVariableTable->SetTreeTableModel(NULL); 1442 1443 if (fPreviousViewState != NULL) 1444 fPreviousViewState->ReleaseReference(); 1445 delete fViewStateHistory; 1446 1447 if (fVariableTableModel != NULL) { 1448 fVariableTableModel->SetContainerListener(NULL); 1449 delete fVariableTableModel; 1450 } 1451 1452 delete fContainerListener; 1453 } 1454 1455 1456 /*static*/ VariablesView* 1457 VariablesView::Create(Listener* listener) 1458 { 1459 VariablesView* self = new VariablesView(listener); 1460 1461 try { 1462 self->_Init(); 1463 } catch (...) { 1464 delete self; 1465 throw; 1466 } 1467 1468 return self; 1469 } 1470 1471 1472 void 1473 VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame) 1474 { 1475 fFrameClearPending = false; 1476 1477 if (thread == fThread && stackFrame == fStackFrame) 1478 return; 1479 1480 _SaveViewState(); 1481 1482 _FinishContextMenu(true); 1483 1484 if (fThread != NULL) 1485 fThread->ReleaseReference(); 1486 if (fStackFrame != NULL) 1487 fStackFrame->ReleaseReference(); 1488 1489 fThread = thread; 1490 fStackFrame = stackFrame; 1491 1492 if (fThread != NULL) 1493 fThread->AcquireReference(); 1494 if (fStackFrame != NULL) 1495 fStackFrame->AcquireReference(); 1496 1497 fVariableTableModel->SetStackFrame(fThread, fStackFrame); 1498 1499 // request loading the parameter and variable values 1500 if (fThread != NULL && fStackFrame != NULL) { 1501 AutoLocker<Team> locker(fThread->GetTeam()); 1502 1503 void* root = fVariableTableModel->Root(); 1504 int32 count = fVariableTableModel->CountChildren(root); 1505 for (int32 i = 0; i < count; i++) { 1506 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i); 1507 _RequestNodeValue(node); 1508 } 1509 } 1510 1511 _RestoreViewState(); 1512 } 1513 1514 1515 void 1516 VariablesView::MessageReceived(BMessage* message) 1517 { 1518 switch (message->what) { 1519 case MSG_SHOW_INSPECTOR_WINDOW: 1520 { 1521 // TODO: it'd probably be more ideal to extend the context 1522 // action mechanism to allow one to specify an explicit 1523 // target for each action rather than them all defaulting 1524 // to targetting here. 1525 Looper()->PostMessage(message); 1526 break; 1527 } 1528 case MSG_SHOW_TYPECAST_NODE_PROMPT: 1529 { 1530 BMessage* promptMessage = new(std::nothrow) BMessage( 1531 MSG_TYPECAST_NODE); 1532 1533 if (promptMessage == NULL) 1534 return; 1535 1536 ObjectDeleter<BMessage> messageDeleter(promptMessage); 1537 promptMessage->AddPointer("node", fVariableTable 1538 ->SelectionModel()->NodeAt(0)); 1539 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 1540 "Specify Type", "Type: ", NULL, BMessenger(this), 1541 promptMessage); 1542 if (promptWindow == NULL) 1543 return; 1544 1545 messageDeleter.Detach(); 1546 promptWindow->CenterOnScreen(); 1547 promptWindow->Show(); 1548 break; 1549 } 1550 case MSG_TYPECAST_NODE: 1551 { 1552 ModelNode* node = NULL; 1553 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 1554 != B_OK) { 1555 break; 1556 } 1557 1558 Type* type = NULL; 1559 BString typeExpression = message->FindString("text"); 1560 if (typeExpression.Length() == 0) 1561 break; 1562 1563 FileSourceCode* code = fStackFrame->Function()->GetFunction() 1564 ->GetSourceCode(); 1565 if (code == NULL) 1566 break; 1567 1568 SourceLanguage* language = code->GetSourceLanguage(); 1569 if (language == NULL) 1570 break; 1571 1572 if (language->ParseTypeExpression(typeExpression, 1573 fThread->GetTeam()->DebugInfo(), type) != B_OK) { 1574 BString errorMessage; 1575 errorMessage.SetToFormat("Failed to resolve type %s", 1576 typeExpression.String()); 1577 BAlert* alert = new(std::nothrow) BAlert("Error", 1578 errorMessage.String(), "Close"); 1579 if (alert != NULL) 1580 alert->Go(); 1581 break; 1582 } 1583 1584 BReference<Type> typeRef(type, true); 1585 ValueNode* valueNode = NULL; 1586 if (TypeHandlerRoster::Default()->CreateValueNode( 1587 node->NodeChild(), type, valueNode) != B_OK) { 1588 break; 1589 } 1590 1591 typeRef.Detach(); 1592 node->NodeChild()->SetNode(valueNode); 1593 node->SetCastedType(type); 1594 fVariableTableModel->NotifyNodeChanged(node); 1595 break; 1596 } 1597 case MSG_TYPECAST_TO_ARRAY: 1598 { 1599 ModelNode* node = NULL; 1600 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 1601 != B_OK) { 1602 break; 1603 } 1604 1605 Type* baseType = dynamic_cast<AddressType*>(node->NodeChild() 1606 ->Node()->GetType())->BaseType(); 1607 ArrayType* arrayType = NULL; 1608 if (baseType->CreateDerivedArrayType(0, kMaxArrayElementCount, 1609 false, arrayType) != B_OK) { 1610 break; 1611 } 1612 1613 AddressType* addressType = NULL; 1614 BReference<Type> typeRef(arrayType, true); 1615 if (arrayType->CreateDerivedAddressType(DERIVED_TYPE_POINTER, 1616 addressType) != B_OK) { 1617 break; 1618 } 1619 1620 typeRef.Detach(); 1621 typeRef.SetTo(addressType, true); 1622 ValueNode* valueNode = NULL; 1623 if (TypeHandlerRoster::Default()->CreateValueNode( 1624 node->NodeChild(), addressType, valueNode) != B_OK) { 1625 break; 1626 } 1627 1628 typeRef.Detach(); 1629 node->NodeChild()->SetNode(valueNode); 1630 node->SetCastedType(addressType); 1631 fVariableTableModel->NotifyNodeChanged(node); 1632 break; 1633 } 1634 case MSG_SHOW_CONTAINER_RANGE_PROMPT: 1635 { 1636 ModelNode* node = (ModelNode*)fVariableTable 1637 ->SelectionModel()->NodeAt(0); 1638 int32 lowerBound, upperBound; 1639 ValueNode* valueNode = node->NodeChild()->Node(); 1640 if (!valueNode->IsRangedContainer()) { 1641 valueNode = node->ChildAt(0)->NodeChild()->Node(); 1642 if (!valueNode->IsRangedContainer()) 1643 break; 1644 } 1645 1646 bool fixedRange = valueNode->IsContainerRangeFixed(); 1647 if (valueNode->SupportedChildRange(lowerBound, upperBound) 1648 != B_OK) { 1649 break; 1650 } 1651 1652 BMessage* promptMessage = new(std::nothrow) BMessage( 1653 MSG_SET_CONTAINER_RANGE); 1654 if (promptMessage == NULL) 1655 break; 1656 1657 ObjectDeleter<BMessage> messageDeleter(promptMessage); 1658 promptMessage->AddPointer("node", node); 1659 promptMessage->AddBool("fixedRange", fixedRange); 1660 BString infoText; 1661 if (fixedRange) { 1662 infoText.SetToFormat("Allowed range: %" B_PRId32 1663 "-%" B_PRId32 ".", lowerBound, upperBound); 1664 } else { 1665 infoText.SetToFormat("Current range: %" B_PRId32 1666 "-%" B_PRId32 ".", lowerBound, upperBound); 1667 } 1668 1669 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 1670 "Set Range", "Range: ", infoText.String(), BMessenger(this), 1671 promptMessage); 1672 if (promptWindow == NULL) 1673 return; 1674 1675 messageDeleter.Detach(); 1676 promptWindow->CenterOnScreen(); 1677 promptWindow->Show(); 1678 break; 1679 } 1680 case MSG_SET_CONTAINER_RANGE: 1681 { 1682 ModelNode* node = (ModelNode*)fVariableTable 1683 ->SelectionModel()->NodeAt(0); 1684 int32 lowerBound, upperBound; 1685 ValueNode* valueNode = node->NodeChild()->Node(); 1686 if (!valueNode->IsRangedContainer()) 1687 valueNode = node->ChildAt(0)->NodeChild()->Node(); 1688 if (valueNode->SupportedChildRange(lowerBound, upperBound) != B_OK) 1689 break; 1690 1691 bool fixedRange = message->FindBool("fixedRange"); 1692 1693 BString rangeExpression = message->FindString("text"); 1694 if (rangeExpression.Length() == 0) 1695 break; 1696 1697 RangeList ranges; 1698 status_t result = UiUtils::ParseRangeExpression( 1699 rangeExpression, lowerBound, upperBound, fixedRange, ranges); 1700 if (result != B_OK) 1701 break; 1702 1703 valueNode->ClearChildren(); 1704 for (int32 i = 0; i < ranges.CountRanges(); i++) { 1705 const Range* range = ranges.RangeAt(i); 1706 result = valueNode->CreateChildrenInRange( 1707 range->lowerBound, range->upperBound); 1708 if (result != B_OK) 1709 break; 1710 } 1711 break; 1712 } 1713 case MSG_SHOW_WATCH_VARIABLE_PROMPT: 1714 { 1715 ModelNode* node = reinterpret_cast<ModelNode*>( 1716 fVariableTable->SelectionModel()->NodeAt(0)); 1717 ValueLocation* location = node->NodeChild()->Location(); 1718 ValuePieceLocation piece = location->PieceAt(0); 1719 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 1720 break; 1721 1722 BMessage looperMessage(*message); 1723 looperMessage.AddUInt64("address", piece.address); 1724 looperMessage.AddInt32("length", piece.size); 1725 looperMessage.AddUInt32("type", B_DATA_READ_WRITE_WATCHPOINT); 1726 Looper()->PostMessage(&looperMessage); 1727 break; 1728 } 1729 case MSG_VALUE_NODE_CHANGED: 1730 { 1731 ValueNodeChild* nodeChild; 1732 ValueNode* oldNode; 1733 ValueNode* newNode; 1734 if (message->FindPointer("nodeChild", (void**)&nodeChild) == B_OK 1735 && message->FindPointer("oldNode", (void**)&oldNode) == B_OK 1736 && message->FindPointer("newNode", (void**)&newNode) == B_OK) { 1737 BReference<ValueNodeChild> nodeChildReference(nodeChild, true); 1738 BReference<ValueNode> oldNodeReference(oldNode, true); 1739 BReference<ValueNode> newNodeReference(newNode, true); 1740 1741 fVariableTableModel->ValueNodeChanged(nodeChild, oldNode, 1742 newNode); 1743 } 1744 1745 break; 1746 } 1747 case MSG_VALUE_NODE_CHILDREN_CREATED: 1748 { 1749 ValueNode* node; 1750 if (message->FindPointer("node", (void**)&node) == B_OK) { 1751 BReference<ValueNode> newNodeReference(node, true); 1752 fVariableTableModel->ValueNodeChildrenCreated(node); 1753 } 1754 1755 break; 1756 } 1757 case MSG_VALUE_NODE_CHILDREN_DELETED: 1758 { 1759 ValueNode* node; 1760 if (message->FindPointer("node", (void**)&node) == B_OK) { 1761 BReference<ValueNode> newNodeReference(node, true); 1762 fVariableTableModel->ValueNodeChildrenDeleted(node); 1763 } 1764 1765 break; 1766 } 1767 case MSG_VALUE_NODE_VALUE_CHANGED: 1768 { 1769 ValueNode* node; 1770 if (message->FindPointer("node", (void**)&node) == B_OK) { 1771 BReference<ValueNode> newNodeReference(node, true); 1772 fVariableTableModel->ValueNodeValueChanged(node); 1773 } 1774 1775 break; 1776 } 1777 case MSG_RESTORE_PARTIAL_VIEW_STATE: 1778 { 1779 ModelNode* node; 1780 if (message->FindPointer("node", (void**)&node) == B_OK) { 1781 TreeTablePath path; 1782 if (fVariableTableModel->GetTreePath(node, path)) { 1783 FunctionID* functionID = fStackFrame->Function() 1784 ->GetFunctionID(); 1785 if (functionID == NULL) 1786 return; 1787 BReference<FunctionID> functionIDReference(functionID, 1788 true); 1789 VariablesViewState* viewState = fViewStateHistory 1790 ->GetState(fThread->ID(), functionID); 1791 if (viewState != NULL) { 1792 _ApplyViewStateDescendentNodeInfos(viewState, node, 1793 path); 1794 } 1795 } 1796 } 1797 break; 1798 } 1799 case MSG_VALUE_NODE_NEEDS_VALUE: 1800 case MSG_MODEL_NODE_HIDDEN: 1801 { 1802 ModelNode* node; 1803 if (message->FindPointer("node", (void**)&node) == B_OK) { 1804 BReference<ModelNode> modelNodeReference(node, true); 1805 _RequestNodeValue(node); 1806 } 1807 1808 break; 1809 } 1810 case MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE: 1811 { 1812 _FinishContextMenu(false); 1813 break; 1814 } 1815 case MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED: 1816 { 1817 ModelNode* node; 1818 if (message->FindPointer("node", (void**)&node) != B_OK) 1819 break; 1820 BReference<ModelNode> nodeReference(node, true); 1821 1822 fVariableTableModel->NotifyNodeChanged(node); 1823 break; 1824 } 1825 default: 1826 BGroupView::MessageReceived(message); 1827 break; 1828 } 1829 } 1830 1831 1832 void 1833 VariablesView::DetachedFromWindow() 1834 { 1835 _FinishContextMenu(true); 1836 } 1837 1838 1839 void 1840 VariablesView::LoadSettings(const BMessage& settings) 1841 { 1842 BMessage tableSettings; 1843 if (settings.FindMessage("variableTable", &tableSettings) == B_OK) { 1844 GuiSettingsUtils::UnarchiveTableSettings(tableSettings, 1845 fVariableTable); 1846 } 1847 } 1848 1849 1850 status_t 1851 VariablesView::SaveSettings(BMessage& settings) 1852 { 1853 settings.MakeEmpty(); 1854 1855 BMessage tableSettings; 1856 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings, 1857 fVariableTable); 1858 if (result == B_OK) 1859 result = settings.AddMessage("variableTable", &tableSettings); 1860 1861 return result; 1862 } 1863 1864 1865 void 1866 VariablesView::SetStackFrameClearPending() 1867 { 1868 fFrameClearPending = true; 1869 } 1870 1871 1872 void 1873 VariablesView::TreeTableNodeExpandedChanged(TreeTable* table, 1874 const TreeTablePath& path, bool expanded) 1875 { 1876 if (fFrameClearPending) 1877 return; 1878 1879 if (expanded) { 1880 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 1881 if (node == NULL) 1882 return; 1883 1884 fVariableTableModel->NodeExpanded(node); 1885 1886 // request the values of all children that don't have any yet 1887 1888 // If the node only has a hidden child, directly load the child's 1889 // children's values. 1890 if (node->CountChildren() == 1) { 1891 ModelNode* child = node->ChildAt(0); 1892 if (child->IsHidden()) 1893 node = child; 1894 } 1895 1896 // request the values 1897 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) { 1898 if (child->IsPresentationNode()) 1899 continue; 1900 1901 _RequestNodeValue(child); 1902 } 1903 } 1904 } 1905 1906 1907 void 1908 VariablesView::TreeTableCellMouseDown(TreeTable* table, 1909 const TreeTablePath& path, int32 columnIndex, BPoint screenWhere, 1910 uint32 buttons) 1911 { 1912 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) 1913 return; 1914 1915 if (fFrameClearPending) 1916 return; 1917 1918 _FinishContextMenu(true); 1919 1920 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 1921 if (node == NULL) 1922 return; 1923 1924 Settings* settings = NULL; 1925 SettingsMenu* settingsMenu = NULL; 1926 BReference<SettingsMenu> settingsMenuReference; 1927 status_t error = B_OK; 1928 TableCellValueRenderer* cellRenderer = node->TableCellRenderer(); 1929 if (cellRenderer != NULL) { 1930 settings = cellRenderer->GetSettings(); 1931 if (settings != NULL) { 1932 error = node->GetValueHandler() 1933 ->CreateTableCellValueSettingsMenu(node->GetValue(), settings, 1934 settingsMenu); 1935 settingsMenuReference.SetTo(settingsMenu, true); 1936 if (error != B_OK) 1937 return; 1938 } 1939 } 1940 1941 TableCellContextMenuTracker* tracker = new(std::nothrow) 1942 TableCellContextMenuTracker(node, Looper(), this); 1943 BReference<TableCellContextMenuTracker> trackerReference(tracker); 1944 1945 ContextActionList* preActionList = new(std::nothrow) ContextActionList; 1946 if (preActionList == NULL) 1947 return; 1948 1949 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter( 1950 preActionList); 1951 1952 error = _GetContextActionsForNode(node, preActionList); 1953 if (error != B_OK) 1954 return; 1955 1956 if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList) != B_OK) 1957 return; 1958 1959 fTableCellContextMenuTracker = trackerReference.Detach(); 1960 fTableCellContextMenuTracker->ShowMenu(screenWhere); 1961 } 1962 1963 1964 void 1965 VariablesView::_Init() 1966 { 1967 fVariableTable = new TreeTable("variable list", 0, B_FANCY_BORDER); 1968 AddChild(fVariableTable->ToView()); 1969 fVariableTable->SetSortingEnabled(false); 1970 1971 // columns 1972 fVariableTable->AddColumn(new StringTableColumn(0, "Variable", 80, 40, 1000, 1973 B_TRUNCATE_END, B_ALIGN_LEFT)); 1974 fVariableTable->AddColumn(new VariableValueColumn(1, "Value", 80, 40, 1000, 1975 B_TRUNCATE_END, B_ALIGN_RIGHT)); 1976 fVariableTable->AddColumn(new StringTableColumn(2, "Type", 80, 40, 1000, 1977 B_TRUNCATE_END, B_ALIGN_LEFT)); 1978 1979 fVariableTableModel = new VariableTableModel; 1980 if (fVariableTableModel->Init() != B_OK) 1981 throw std::bad_alloc(); 1982 fVariableTable->SetTreeTableModel(fVariableTableModel); 1983 fVariableTable->SetToolTipProvider(fVariableTableModel); 1984 1985 fContainerListener = new ContainerListener(this); 1986 fVariableTableModel->SetContainerListener(fContainerListener); 1987 1988 fVariableTable->AddTreeTableListener(this); 1989 1990 fViewStateHistory = new VariablesViewStateHistory; 1991 if (fViewStateHistory->Init() != B_OK) 1992 throw std::bad_alloc(); 1993 } 1994 1995 1996 void 1997 VariablesView::_RequestNodeValue(ModelNode* node) 1998 { 1999 // get the node child and its container 2000 ValueNodeChild* nodeChild = node->NodeChild(); 2001 ValueNodeContainer* container = nodeChild->Container(); 2002 2003 BReference<ValueNodeContainer> containerReference(container); 2004 AutoLocker<ValueNodeContainer> containerLocker(container); 2005 2006 if (container == NULL || nodeChild->Container() != container) 2007 return; 2008 2009 // get the value node and check whether its value has not yet been resolved 2010 ValueNode* valueNode = nodeChild->Node(); 2011 if (valueNode == NULL) { 2012 ModelNode* parent = node->Parent(); 2013 if (parent != NULL) { 2014 TreeTablePath path; 2015 if (!fVariableTableModel->GetTreePath(parent, path)) 2016 return; 2017 2018 // if the parent node was already expanded when the child was 2019 // added, we may not yet have added a value node. 2020 // Notify the table model that this needs to be done. 2021 if (fVariableTable->IsNodeExpanded(path)) 2022 fVariableTableModel->NodeExpanded(parent); 2023 } 2024 } 2025 2026 if (valueNode == NULL || valueNode->LocationAndValueResolutionState() 2027 != VALUE_NODE_UNRESOLVED) { 2028 return; 2029 } 2030 2031 BReference<ValueNode> valueNodeReference(valueNode); 2032 containerLocker.Unlock(); 2033 2034 // request resolution of the value 2035 fListener->ValueNodeValueRequested(fStackFrame->GetCpuState(), container, 2036 valueNode); 2037 } 2038 2039 2040 status_t 2041 VariablesView::_GetContextActionsForNode(ModelNode* node, 2042 ContextActionList* actions) 2043 { 2044 ValueLocation* location = node->NodeChild()->Location(); 2045 status_t result = B_OK; 2046 BMessage* message = NULL; 2047 2048 // only show the Inspect option if the value is in fact located 2049 // in memory. 2050 if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) { 2051 result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW, 2052 actions, message); 2053 if (result != B_OK) 2054 return result; 2055 message->AddUInt64("address", location->PieceAt(0).address); 2056 } 2057 2058 ValueNode* valueNode = node->NodeChild()->Node(); 2059 2060 if (valueNode != NULL) { 2061 AddressType* type = dynamic_cast<AddressType*>(valueNode->GetType()); 2062 if (type != NULL && type->BaseType() != NULL) { 2063 result = _AddContextAction("Cast to array", MSG_TYPECAST_TO_ARRAY, 2064 actions, message); 2065 if (result != B_OK) 2066 return result; 2067 message->AddPointer("node", node); 2068 } 2069 } 2070 2071 result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS, 2072 MSG_SHOW_TYPECAST_NODE_PROMPT, actions, message); 2073 if (result != B_OK) 2074 return result; 2075 2076 result = _AddContextAction("Watch" B_UTF8_ELLIPSIS, 2077 MSG_SHOW_WATCH_VARIABLE_PROMPT, actions, message); 2078 if (result != B_OK) 2079 return result; 2080 2081 if (valueNode == NULL) 2082 return B_OK; 2083 2084 if (!valueNode->IsRangedContainer()) { 2085 if (node->CountChildren() == 1 && node->ChildAt(0)->IsHidden()) { 2086 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2087 if (valueNode == NULL || !valueNode->IsRangedContainer()) 2088 return B_OK; 2089 } else 2090 return B_OK; 2091 } 2092 2093 result = _AddContextAction("Set visible range" B_UTF8_ELLIPSIS, 2094 MSG_SHOW_CONTAINER_RANGE_PROMPT, actions, message); 2095 if (result != B_OK) 2096 return result; 2097 2098 return B_OK; 2099 } 2100 2101 2102 status_t 2103 VariablesView::_AddContextAction(const char* action, uint32 what, 2104 ContextActionList* actions, BMessage*& _message) 2105 { 2106 _message = new BMessage(what); 2107 if (_message == NULL) 2108 return B_NO_MEMORY; 2109 2110 ObjectDeleter<BMessage> messageDeleter(_message); 2111 2112 ActionMenuItem* item = new(std::nothrow) ActionMenuItem(action, 2113 _message); 2114 if (item == NULL) 2115 return B_NO_MEMORY; 2116 2117 messageDeleter.Detach(); 2118 ObjectDeleter<ActionMenuItem> actionDeleter(item); 2119 if (!actions->AddItem(item)) 2120 return B_NO_MEMORY; 2121 2122 actionDeleter.Detach(); 2123 2124 return B_OK; 2125 } 2126 2127 2128 void 2129 VariablesView::_FinishContextMenu(bool force) 2130 { 2131 if (fTableCellContextMenuTracker != NULL) { 2132 if (!fTableCellContextMenuTracker->FinishMenu(force) || force) { 2133 fTableCellContextMenuTracker->ReleaseReference(); 2134 fTableCellContextMenuTracker = NULL; 2135 } 2136 } 2137 } 2138 2139 2140 2141 void 2142 VariablesView::_SaveViewState() const 2143 { 2144 if (fThread == NULL || fStackFrame == NULL 2145 || fStackFrame->Function() == NULL) { 2146 return; 2147 } 2148 2149 // get the function ID 2150 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2151 if (functionID == NULL) 2152 return; 2153 BReference<FunctionID> functionIDReference(functionID, true); 2154 2155 // create an empty view state 2156 VariablesViewState* viewState = new(std::nothrow) VariablesViewState; 2157 if (viewState == NULL) 2158 return; 2159 BReference<VariablesViewState> viewStateReference(viewState, true); 2160 2161 if (viewState->Init() != B_OK) 2162 return; 2163 2164 // populate it 2165 TreeTablePath path; 2166 if (_AddViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), 2167 path) != B_OK) { 2168 return; 2169 } 2170 // TODO: Add values! 2171 2172 // add the view state to the history 2173 fViewStateHistory->SetState(fThread->ID(), functionID, viewState); 2174 } 2175 2176 2177 void 2178 VariablesView::_RestoreViewState() 2179 { 2180 if (fPreviousViewState != NULL) { 2181 fPreviousViewState->ReleaseReference(); 2182 fPreviousViewState = NULL; 2183 } 2184 2185 if (fThread == NULL || fStackFrame == NULL 2186 || fStackFrame->Function() == NULL) { 2187 return; 2188 } 2189 2190 // get the function ID 2191 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2192 if (functionID == NULL) 2193 return; 2194 BReference<FunctionID> functionIDReference(functionID, true); 2195 2196 // get the previous view state 2197 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), 2198 functionID); 2199 if (viewState == NULL) 2200 return; 2201 2202 // apply the view state 2203 TreeTablePath path; 2204 _ApplyViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), 2205 path); 2206 } 2207 2208 2209 status_t 2210 VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState, 2211 void* parent, TreeTablePath& path) const 2212 { 2213 int32 childCount = fVariableTableModel->CountChildren(parent); 2214 for (int32 i = 0; i < childCount; i++) { 2215 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i); 2216 if (!path.AddComponent(i)) 2217 return B_NO_MEMORY; 2218 2219 // add the node's info 2220 VariablesViewNodeInfo nodeInfo; 2221 nodeInfo.SetNodeExpanded(fVariableTable->IsNodeExpanded(path)); 2222 nodeInfo.SetCastedType(node->GetCastedType()); 2223 TableCellValueRenderer* renderer = node->TableCellRenderer(); 2224 if (renderer != NULL) { 2225 Settings* settings = renderer->GetSettings(); 2226 if (settings != NULL) 2227 nodeInfo.SetRendererSettings(settings->Message()); 2228 } 2229 2230 status_t error = viewState->SetNodeInfo(node->GetVariable()->ID(), 2231 node->GetPath(), nodeInfo); 2232 if (error != B_OK) 2233 return error; 2234 2235 // recurse 2236 error = _AddViewStateDescendentNodeInfos(viewState, node, path); 2237 if (error != B_OK) 2238 return error; 2239 2240 path.RemoveLastComponent(); 2241 } 2242 2243 return B_OK; 2244 } 2245 2246 2247 status_t 2248 VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState, 2249 void* parent, TreeTablePath& path) 2250 { 2251 int32 childCount = fVariableTableModel->CountChildren(parent); 2252 for (int32 i = 0; i < childCount; i++) { 2253 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i); 2254 if (!path.AddComponent(i)) 2255 return B_NO_MEMORY; 2256 2257 // apply the node's info, if any 2258 const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( 2259 node->GetVariable()->ID(), node->GetPath()); 2260 if (nodeInfo != NULL) { 2261 // NB: if the node info indicates that the node in question 2262 // was being cast to a different type, this *must* be applied 2263 // before any other view state restoration, since it potentially 2264 // changes the child hierarchy under that node. 2265 Type* type = nodeInfo->GetCastedType(); 2266 if (type != NULL) { 2267 ValueNode* valueNode = NULL; 2268 if (TypeHandlerRoster::Default()->CreateValueNode( 2269 node->NodeChild(), type, valueNode) == B_OK) { 2270 node->NodeChild()->SetNode(valueNode); 2271 node->SetCastedType(type); 2272 } 2273 } 2274 2275 // we don't have a renderer yet so we can't apply the settings 2276 // at this stage. Store them on the model node so we can lazily 2277 // apply them once the value is retrieved. 2278 node->SetLastRendererSettings(nodeInfo->GetRendererSettings()); 2279 2280 fVariableTable->SetNodeExpanded(path, nodeInfo->IsNodeExpanded()); 2281 2282 // recurse 2283 status_t error = _ApplyViewStateDescendentNodeInfos(viewState, node, 2284 path); 2285 if (error != B_OK) 2286 return error; 2287 } 2288 2289 path.RemoveLastComponent(); 2290 } 2291 2292 return B_OK; 2293 } 2294 2295 2296 // #pragma mark - Listener 2297 2298 2299 VariablesView::Listener::~Listener() 2300 { 2301 } 2302