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