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