1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2011-2018, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "VariablesView.h" 9 10 #include <new> 11 12 #include <debugger.h> 13 14 #include <Alert.h> 15 #include <Clipboard.h> 16 #include <ControlLook.h> 17 #include <Looper.h> 18 #include <PopUpMenu.h> 19 #include <ToolTip.h> 20 21 #include <AutoDeleter.h> 22 #include <AutoLocker.h> 23 #include <PromptWindow.h> 24 25 #include "table/TableColumns.h" 26 27 #include "ActionMenuItem.h" 28 #include "AppMessageCodes.h" 29 #include "Architecture.h" 30 #include "ExpressionInfo.h" 31 #include "ExpressionValues.h" 32 #include "FileSourceCode.h" 33 #include "Function.h" 34 #include "FunctionID.h" 35 #include "FunctionInstance.h" 36 #include "GuiSettingsUtils.h" 37 #include "MessageCodes.h" 38 #include "RangeList.h" 39 #include "Register.h" 40 #include "SettingsMenu.h" 41 #include "SourceLanguage.h" 42 #include "StackTrace.h" 43 #include "StackFrame.h" 44 #include "StackFrameValues.h" 45 #include "StringValue.h" 46 #include "SyntheticPrimitiveType.h" 47 #include "TableCellValueEditor.h" 48 #include "TableCellValueRenderer.h" 49 #include "Team.h" 50 #include "TeamDebugInfo.h" 51 #include "Thread.h" 52 #include "Tracing.h" 53 #include "TypeComponentPath.h" 54 #include "TypeHandler.h" 55 #include "TypeHandlerMenuItem.h" 56 #include "TypeHandlerRoster.h" 57 #include "TypeLookupConstraints.h" 58 #include "UiUtils.h" 59 #include "Value.h" 60 #include "ValueHandler.h" 61 #include "ValueHandlerRoster.h" 62 #include "ValueLocation.h" 63 #include "ValueNode.h" 64 #include "ValueNodeManager.h" 65 #include "Variable.h" 66 #include "VariableEditWindow.h" 67 #include "VariableValueNodeChild.h" 68 #include "VariablesViewState.h" 69 #include "VariablesViewStateHistory.h" 70 71 72 enum { 73 VALUE_NODE_TYPE = 'valn' 74 }; 75 76 77 enum { 78 MSG_MODEL_NODE_HIDDEN = 'monh', 79 MSG_VALUE_NODE_NEEDS_VALUE = 'mvnv', 80 MSG_RESTORE_PARTIAL_VIEW_STATE = 'mpvs', 81 MSG_ADD_WATCH_EXPRESSION = 'awex', 82 MSG_REMOVE_WATCH_EXPRESSION = 'rwex', 83 MSG_USE_AUTOMATIC_HANDLER = 'uaha', 84 MSG_USE_EXPLICIT_HANDLER = 'ueha' 85 }; 86 87 88 // maximum number of array elements to show by default 89 static const uint64 kMaxArrayElementCount = 10; 90 91 92 // #pragma mark - FunctionKey 93 94 95 struct VariablesView::FunctionKey { 96 FunctionID* function; 97 98 FunctionKey(FunctionID* function) 99 : 100 function(function) 101 { 102 } 103 104 uint32 HashValue() const 105 { 106 return function->HashValue(); 107 } 108 109 bool operator==(const FunctionKey& other) const 110 { 111 return *function == *other.function; 112 } 113 }; 114 115 116 // #pragma mark - ExpressionInfoEntry 117 118 119 struct VariablesView::ExpressionInfoEntry : FunctionKey, ExpressionInfoList { 120 ExpressionInfoEntry* next; 121 122 ExpressionInfoEntry(FunctionID* function) 123 : 124 FunctionKey(function), 125 ExpressionInfoList(10, false) 126 { 127 function->AcquireReference(); 128 } 129 130 ~ExpressionInfoEntry() 131 { 132 _Cleanup(); 133 } 134 135 void SetInfo(const ExpressionInfoList& infoList) 136 { 137 _Cleanup(); 138 139 for (int32 i = 0; i < infoList.CountItems(); i++) { 140 ExpressionInfo* info = infoList.ItemAt(i); 141 if (!AddItem(info)) 142 break; 143 144 info->AcquireReference(); 145 } 146 } 147 148 private: 149 void _Cleanup() 150 { 151 for (int32 i = 0; i < CountItems(); i++) 152 ItemAt(i)->ReleaseReference(); 153 154 MakeEmpty(); 155 } 156 }; 157 158 159 // #pragma mark - ExpressionInfoEntryHashDefinition 160 161 162 struct VariablesView::ExpressionInfoEntryHashDefinition { 163 typedef FunctionKey KeyType; 164 typedef ExpressionInfoEntry ValueType; 165 166 size_t HashKey(const FunctionKey& key) const 167 { 168 return key.HashValue(); 169 } 170 171 size_t Hash(const ExpressionInfoEntry* value) const 172 { 173 return value->HashValue(); 174 } 175 176 bool Compare(const FunctionKey& key, 177 const ExpressionInfoEntry* value) const 178 { 179 return key == *value; 180 } 181 182 ExpressionInfoEntry*& GetLink(ExpressionInfoEntry* value) const 183 { 184 return value->next; 185 } 186 }; 187 188 189 // #pragma mark - ContainerListener 190 191 192 class VariablesView::ContainerListener : public ValueNodeContainer::Listener { 193 public: 194 ContainerListener(BHandler* indirectTarget); 195 196 void SetModel(VariableTableModel* model); 197 198 virtual void ValueNodeChanged(ValueNodeChild* nodeChild, 199 ValueNode* oldNode, ValueNode* newNode); 200 virtual void ValueNodeChildrenCreated(ValueNode* node); 201 virtual void ValueNodeChildrenDeleted(ValueNode* node); 202 virtual void ValueNodeValueChanged(ValueNode* node); 203 204 virtual void ModelNodeHidden(ModelNode* node); 205 206 virtual void ModelNodeValueRequested(ModelNode* node); 207 208 virtual void ModelNodeRestoreViewStateRequested(ModelNode* node); 209 210 private: 211 BHandler* fIndirectTarget; 212 VariableTableModel* fModel; 213 }; 214 215 216 // #pragma mark - ExpressionVariableID 217 218 219 class VariablesView::ExpressionVariableID : public ObjectID { 220 public: 221 ExpressionVariableID(ExpressionInfo* info) 222 : 223 fInfo(info) 224 { 225 fInfo->AcquireReference(); 226 } 227 228 virtual ~ExpressionVariableID() 229 { 230 fInfo->ReleaseReference(); 231 } 232 233 virtual bool operator==(const ObjectID& other) const 234 { 235 const ExpressionVariableID* otherID 236 = dynamic_cast<const ExpressionVariableID*>(&other); 237 if (otherID == NULL) 238 return false; 239 240 return fInfo == otherID->fInfo; 241 } 242 243 protected: 244 virtual uint32 ComputeHashValue() const 245 { 246 uint32 hash = reinterpret_cast<addr_t>(fInfo); 247 hash = hash * 19 + fInfo->Expression().HashValue(); 248 249 return hash; 250 } 251 252 private: 253 ExpressionInfo* fInfo; 254 }; 255 256 257 // #pragma mark - ModelNode 258 259 260 class VariablesView::ModelNode : public BReferenceable { 261 public: 262 ModelNode(ModelNode* parent, Variable* variable, ValueNodeChild* nodeChild, 263 bool isPresentationNode) 264 : 265 fParent(parent), 266 fNodeChild(nodeChild), 267 fVariable(variable), 268 fValue(NULL), 269 fPreviousValue(), 270 fValueHandler(NULL), 271 fTableCellRenderer(NULL), 272 fLastRendererSettings(), 273 fCastedType(NULL), 274 fTypeHandler(NULL), 275 fComponentPath(NULL), 276 fIsPresentationNode(isPresentationNode), 277 fHidden(false), 278 fValueChanged(false), 279 fPresentationName() 280 { 281 fVariable->AcquireReference(); 282 fNodeChild->AcquireReference(); 283 } 284 285 ~ModelNode() 286 { 287 SetTableCellRenderer(NULL); 288 SetValueHandler(NULL); 289 SetValue(NULL); 290 291 for (int32 i = 0; ModelNode* child = fChildren.ItemAt(i); i++) 292 child->ReleaseReference(); 293 294 fNodeChild->ReleaseReference(); 295 fVariable->ReleaseReference(); 296 297 if (fComponentPath != NULL) 298 fComponentPath->ReleaseReference(); 299 300 if (fCastedType != NULL) 301 fCastedType->ReleaseReference(); 302 303 if (fTypeHandler != NULL) 304 fTypeHandler->ReleaseReference(); 305 } 306 307 status_t Init() 308 { 309 fComponentPath = new(std::nothrow) TypeComponentPath(); 310 if (fComponentPath == NULL) 311 return B_NO_MEMORY; 312 313 if (fParent != NULL) 314 *fComponentPath = *fParent->GetPath(); 315 316 TypeComponent component; 317 // TODO: this should actually discriminate between different 318 // classes of type component kinds 319 component.SetToBaseType(fNodeChild->GetType()->Kind(), 320 0, fNodeChild->Name()); 321 322 fComponentPath->AddComponent(component); 323 324 return B_OK; 325 } 326 327 ModelNode* Parent() const 328 { 329 return fParent; 330 } 331 332 ValueNodeChild* NodeChild() const 333 { 334 return fNodeChild; 335 } 336 337 const BString& Name() const 338 { 339 return fPresentationName.IsEmpty() 340 ? fNodeChild->Name() : fPresentationName; 341 } 342 343 void SetPresentationName(const BString& name) 344 { 345 fPresentationName = name; 346 } 347 348 Type* GetType() const 349 { 350 if (fCastedType != NULL) 351 return fCastedType; 352 353 return fNodeChild->GetType(); 354 } 355 356 Variable* GetVariable() const 357 { 358 return fVariable; 359 } 360 361 Value* GetValue() const 362 { 363 return fValue; 364 } 365 366 void SetValue(Value* value) 367 { 368 if (value == fValue) 369 return; 370 371 if (fValue != NULL) 372 fValue->ReleaseReference(); 373 374 fValue = value; 375 376 if (fValue != NULL) 377 fValue->AcquireReference(); 378 379 _CompareValues(); 380 } 381 382 const BVariant& PreviousValue() const 383 { 384 return fPreviousValue; 385 } 386 387 void SetPreviousValue(const BVariant& value) 388 { 389 fPreviousValue = value; 390 } 391 392 Type* GetCastedType() const 393 { 394 return fCastedType; 395 } 396 397 void SetCastedType(Type* type) 398 { 399 if (fCastedType != NULL) 400 fCastedType->ReleaseReference(); 401 402 fCastedType = type; 403 if (type != NULL) 404 fCastedType->AcquireReference(); 405 } 406 407 TypeHandler* GetTypeHandler() const 408 { 409 return fTypeHandler; 410 } 411 412 void SetTypeHandler(TypeHandler* handler) 413 { 414 if (fTypeHandler != NULL) 415 fTypeHandler->ReleaseReference(); 416 417 fTypeHandler = handler; 418 if (fTypeHandler != NULL) 419 fTypeHandler->AcquireReference(); 420 } 421 422 423 const BMessage& GetLastRendererSettings() const 424 { 425 return fLastRendererSettings; 426 } 427 428 void SetLastRendererSettings(const BMessage& settings) 429 { 430 fLastRendererSettings = settings; 431 } 432 433 TypeComponentPath* GetPath() const 434 { 435 return fComponentPath; 436 } 437 438 ValueHandler* GetValueHandler() const 439 { 440 return fValueHandler; 441 } 442 443 void SetValueHandler(ValueHandler* handler) 444 { 445 if (handler == fValueHandler) 446 return; 447 448 if (fValueHandler != NULL) 449 fValueHandler->ReleaseReference(); 450 451 fValueHandler = handler; 452 453 if (fValueHandler != NULL) 454 fValueHandler->AcquireReference(); 455 } 456 457 458 TableCellValueRenderer* TableCellRenderer() const 459 { 460 return fTableCellRenderer; 461 } 462 463 void SetTableCellRenderer(TableCellValueRenderer* renderer) 464 { 465 if (renderer == fTableCellRenderer) 466 return; 467 468 if (fTableCellRenderer != NULL) 469 fTableCellRenderer->ReleaseReference(); 470 471 fTableCellRenderer = renderer; 472 473 if (fTableCellRenderer != NULL) 474 fTableCellRenderer->AcquireReference(); 475 } 476 477 bool IsPresentationNode() const 478 { 479 return fIsPresentationNode; 480 } 481 482 bool IsHidden() const 483 { 484 return fHidden; 485 } 486 487 void SetHidden(bool hidden) 488 { 489 fHidden = hidden; 490 } 491 492 bool ValueChanged() const 493 { 494 return fValueChanged; 495 } 496 497 int32 CountChildren() const 498 { 499 return fChildren.CountItems(); 500 } 501 502 ModelNode* ChildAt(int32 index) const 503 { 504 return fChildren.ItemAt(index); 505 } 506 507 int32 IndexOf(ModelNode* child) const 508 { 509 return fChildren.IndexOf(child); 510 } 511 512 bool AddChild(ModelNode* child) 513 { 514 if (!fChildren.AddItem(child)) 515 return false; 516 517 child->AcquireReference(); 518 return true; 519 } 520 521 bool RemoveChild(ModelNode* child) 522 { 523 if (!fChildren.RemoveItem(child)) 524 return false; 525 526 child->ReleaseReference(); 527 return true; 528 } 529 530 bool RemoveAllChildren() 531 { 532 for (int32 i = 0; i < fChildren.CountItems(); i++) 533 RemoveChild(fChildren.ItemAt(i)); 534 535 return true; 536 } 537 538 private: 539 typedef BObjectList<ModelNode> ChildList; 540 541 private: 542 void _CompareValues() 543 { 544 fValueChanged = false; 545 if (fValue != NULL) { 546 if (fPreviousValue.Type() != 0) { 547 BVariant newValue; 548 fValue->ToVariant(newValue); 549 fValueChanged = (fPreviousValue != newValue); 550 } else { 551 // for expression variables, always consider the initial 552 // value as changed, since their evaluation has just been 553 // requested, and thus their initial value is by definition 554 // new/of interest 555 fValueChanged = dynamic_cast<ExpressionVariableID*>( 556 fVariable->ID()) != NULL; 557 } 558 } 559 } 560 561 private: 562 ModelNode* fParent; 563 ValueNodeChild* fNodeChild; 564 Variable* fVariable; 565 Value* fValue; 566 BVariant fPreviousValue; 567 ValueHandler* fValueHandler; 568 TableCellValueRenderer* fTableCellRenderer; 569 BMessage fLastRendererSettings; 570 Type* fCastedType; 571 TypeHandler* fTypeHandler; 572 ChildList fChildren; 573 TypeComponentPath* fComponentPath; 574 bool fIsPresentationNode; 575 bool fHidden; 576 bool fValueChanged; 577 BString fPresentationName; 578 579 public: 580 ModelNode* fNext; 581 }; 582 583 584 // #pragma mark - VariablesExpressionInfo 585 586 587 class VariablesView::VariablesExpressionInfo : public ExpressionInfo { 588 public: 589 VariablesExpressionInfo(const BString& expression, ModelNode* node) 590 : 591 ExpressionInfo(expression), 592 fTargetNode(node) 593 { 594 fTargetNode->AcquireReference(); 595 } 596 597 virtual ~VariablesExpressionInfo() 598 { 599 fTargetNode->ReleaseReference(); 600 } 601 602 inline ModelNode* TargetNode() const 603 { 604 return fTargetNode; 605 } 606 607 private: 608 ModelNode* fTargetNode; 609 }; 610 611 612 // #pragma mark - VariableValueColumn 613 614 615 class VariablesView::VariableValueColumn : public StringTableColumn { 616 public: 617 VariableValueColumn(int32 modelIndex, const char* title, float width, 618 float minWidth, float maxWidth, uint32 truncate = B_TRUNCATE_MIDDLE, 619 alignment align = B_ALIGN_RIGHT) 620 : 621 StringTableColumn(modelIndex, title, width, minWidth, maxWidth, 622 truncate, align) 623 { 624 } 625 626 protected: 627 void DrawValue(const BVariant& value, BRect rect, BView* targetView) 628 { 629 // draw the node's value with the designated renderer 630 if (value.Type() == VALUE_NODE_TYPE) { 631 ModelNode* node = dynamic_cast<ModelNode*>(value.ToReferenceable()); 632 if (node != NULL && node->GetValue() != NULL 633 && node->TableCellRenderer() != NULL) { 634 node->TableCellRenderer()->RenderValue(node->GetValue(), 635 node->ValueChanged(), rect, targetView); 636 return; 637 } 638 } else if (value.Type() == B_STRING_TYPE) { 639 fField.SetString(value.ToString()); 640 } else { 641 // fall back to drawing an empty string 642 fField.SetString(""); 643 } 644 fField.SetWidth(Width()); 645 fColumn.DrawField(&fField, rect, targetView); 646 } 647 648 float GetPreferredWidth(const BVariant& value, BView* targetView) const 649 { 650 // get the preferred width from the node's designated renderer 651 if (value.Type() == VALUE_NODE_TYPE) { 652 ModelNode* node = dynamic_cast<ModelNode*>(value.ToReferenceable()); 653 if (node != NULL && node->GetValue() != NULL 654 && node->TableCellRenderer() != NULL) { 655 return node->TableCellRenderer()->PreferredValueWidth( 656 node->GetValue(), targetView); 657 } 658 } 659 660 return fColumn.BTitledColumn::GetPreferredWidth(NULL, targetView); 661 } 662 663 virtual BField* PrepareField(const BVariant& _value) const 664 { 665 return NULL; 666 } 667 }; 668 669 670 // #pragma mark - VariableTableModel 671 672 673 class VariablesView::VariableTableModel : public TreeTableModel, 674 public TreeTableToolTipProvider { 675 public: 676 VariableTableModel(ValueNodeManager* manager); 677 ~VariableTableModel(); 678 679 status_t Init(); 680 681 void SetContainerListener( 682 ContainerListener* listener); 683 684 void SetStackFrame(::Thread* thread, 685 StackFrame* stackFrame); 686 687 void ValueNodeChanged(ValueNodeChild* nodeChild, 688 ValueNode* oldNode, ValueNode* newNode); 689 void ValueNodeChildrenCreated(ValueNode* node); 690 void ValueNodeChildrenDeleted(ValueNode* node); 691 void ValueNodeValueChanged(ValueNode* node); 692 693 virtual int32 CountColumns() const; 694 virtual void* Root() const; 695 virtual int32 CountChildren(void* parent) const; 696 virtual void* ChildAt(void* parent, int32 index) const; 697 virtual bool GetValueAt(void* object, int32 columnIndex, 698 BVariant& _value); 699 700 bool GetTreePath(ModelNode* node, 701 TreeTablePath& _path) const; 702 703 void NodeExpanded(ModelNode* node); 704 705 void NotifyNodeChanged(ModelNode* node); 706 void NotifyNodeHidden(ModelNode* node); 707 708 virtual bool GetToolTipForTablePath( 709 const TreeTablePath& path, 710 int32 columnIndex, BToolTip** _tip); 711 712 status_t AddSyntheticNode(Variable* variable, 713 ValueNodeChild*& _child, 714 const char* presentationName = NULL); 715 void RemoveSyntheticNode(ModelNode* node); 716 717 private: 718 struct NodeHashDefinition { 719 typedef ValueNodeChild* KeyType; 720 typedef ModelNode ValueType; 721 722 size_t HashKey(const ValueNodeChild* key) const 723 { 724 return (size_t)key; 725 } 726 727 size_t Hash(const ModelNode* value) const 728 { 729 return HashKey(value->NodeChild()); 730 } 731 732 bool Compare(const ValueNodeChild* key, 733 const ModelNode* value) const 734 { 735 return value->NodeChild() == key; 736 } 737 738 ModelNode*& GetLink(ModelNode* value) const 739 { 740 return value->fNext; 741 } 742 }; 743 744 typedef BObjectList<ModelNode> NodeList; 745 typedef BOpenHashTable<NodeHashDefinition> NodeTable; 746 747 private: 748 // container must be locked 749 750 status_t _AddNode(Variable* variable, ModelNode* parent, 751 ValueNodeChild* nodeChild, 752 bool isPresentationNode = false, 753 bool isOnlyChild = false); 754 755 private: 756 ::Thread* fThread; 757 ValueNodeManager* fNodeManager; 758 ContainerListener* fContainerListener; 759 NodeList fNodes; 760 NodeTable fNodeTable; 761 }; 762 763 764 class VariablesView::ContextMenu : public BPopUpMenu { 765 public: 766 ContextMenu(const BMessenger& parent, const char* name) 767 : BPopUpMenu(name, false, false), 768 fParent(parent) 769 { 770 } 771 772 virtual void Hide() 773 { 774 BPopUpMenu::Hide(); 775 776 BMessage message(MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE); 777 message.AddPointer("menu", this); 778 fParent.SendMessage(&message); 779 } 780 781 private: 782 BMessenger fParent; 783 }; 784 785 786 // #pragma mark - TableCellContextMenuTracker 787 788 789 class VariablesView::TableCellContextMenuTracker : public BReferenceable, 790 Settings::Listener { 791 public: 792 TableCellContextMenuTracker(ModelNode* node, BLooper* parentLooper, 793 const BMessenger& parent) 794 : 795 fNode(node), 796 fParentLooper(parentLooper), 797 fParent(parent), 798 fRendererSettings(NULL), 799 fRendererSettingsMenu(NULL), 800 fRendererMenuAdded(false), 801 fMenuPreparedToShow(false) 802 { 803 fNode->AcquireReference(); 804 } 805 806 ~TableCellContextMenuTracker() 807 { 808 FinishMenu(true); 809 810 if (fRendererSettingsMenu != NULL) 811 fRendererSettingsMenu->ReleaseReference(); 812 813 if (fRendererSettings != NULL) 814 fRendererSettings->ReleaseReference(); 815 816 fNode->ReleaseReference(); 817 } 818 819 status_t Init(Settings* rendererSettings, 820 SettingsMenu* rendererSettingsMenu, 821 ContextActionList* preSettingsActions = NULL, 822 ContextActionList* postSettingsActions = NULL) 823 { 824 if (rendererSettings == NULL && preSettingsActions == NULL 825 && postSettingsActions == NULL) { 826 return B_BAD_VALUE; 827 } 828 829 if (rendererSettings != NULL) { 830 fRendererSettings = rendererSettings; 831 fRendererSettings->AcquireReference(); 832 833 834 fRendererSettingsMenu = rendererSettingsMenu; 835 fRendererSettingsMenu->AcquireReference(); 836 } 837 838 fContextMenu = new(std::nothrow) ContextMenu(fParent, 839 "table cell settings popup"); 840 if (fContextMenu == NULL) 841 return B_NO_MEMORY; 842 843 status_t error = B_OK; 844 if (preSettingsActions != NULL 845 && preSettingsActions->CountItems() > 0) { 846 error = _AddActionItems(preSettingsActions); 847 if (error != B_OK) 848 return error; 849 850 if (fRendererSettingsMenu != NULL || postSettingsActions != NULL) 851 fContextMenu->AddSeparatorItem(); 852 } 853 854 if (fRendererSettingsMenu != NULL) { 855 error = fRendererSettingsMenu->AddToMenu(fContextMenu, 856 fContextMenu->CountItems()); 857 if (error != B_OK) 858 return error; 859 860 if (postSettingsActions != NULL) 861 fContextMenu->AddSeparatorItem(); 862 } 863 864 if (postSettingsActions != NULL) { 865 error = _AddActionItems(postSettingsActions); 866 if (error != B_OK) 867 return error; 868 869 } 870 871 if (fRendererSettings != NULL) { 872 AutoLocker<Settings> settingsLocker(fRendererSettings); 873 fRendererSettings->AddListener(this); 874 fRendererMenuAdded = true; 875 } 876 877 return B_OK; 878 } 879 880 void ShowMenu(BPoint screenWhere) 881 { 882 if (fRendererMenuAdded) 883 fRendererSettingsMenu->PrepareToShow(fParentLooper); 884 885 for (int32 i = 0; i < fContextMenu->CountItems(); i++) { 886 ActionMenuItem* item = dynamic_cast<ActionMenuItem*>( 887 fContextMenu->ItemAt(i)); 888 if (item != NULL) 889 item->PrepareToShow(fParentLooper, fParent.Target(NULL)); 890 } 891 892 fMenuPreparedToShow = true; 893 894 BRect mouseRect(screenWhere, screenWhere); 895 mouseRect.InsetBy(-4.0, -4.0); 896 fContextMenu->Go(screenWhere, true, false, mouseRect, true); 897 } 898 899 bool FinishMenu(bool force) 900 { 901 bool stillActive = false; 902 903 if (fMenuPreparedToShow) { 904 if (fRendererMenuAdded) 905 stillActive = fRendererSettingsMenu->Finish(fParentLooper, 906 force); 907 for (int32 i = 0; i < fContextMenu->CountItems(); i++) { 908 ActionMenuItem* item = dynamic_cast<ActionMenuItem*>( 909 fContextMenu->ItemAt(i)); 910 if (item != NULL) { 911 stillActive |= item->Finish(fParentLooper, 912 fParent.Target(NULL), force); 913 } 914 } 915 916 fMenuPreparedToShow = stillActive; 917 } 918 919 if (fRendererMenuAdded) { 920 fRendererSettingsMenu->RemoveFromMenu(); 921 fRendererSettings->RemoveListener(this); 922 fRendererMenuAdded = false; 923 } 924 925 if (fContextMenu != NULL) { 926 delete fContextMenu; 927 fContextMenu = NULL; 928 } 929 930 return stillActive; 931 } 932 933 private: 934 // Settings::Listener 935 936 virtual void SettingValueChanged(Setting* setting) 937 { 938 BMessage message(MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED); 939 fNode->AcquireReference(); 940 if (message.AddPointer("node", fNode) != B_OK 941 || fParent.SendMessage(&message) != B_OK) { 942 fNode->ReleaseReference(); 943 } 944 } 945 946 status_t _AddActionItems(ContextActionList* actions) 947 { 948 if (fContextMenu == NULL) 949 return B_BAD_VALUE; 950 951 int32 index = fContextMenu->CountItems(); 952 for (int32 i = 0; ActionMenuItem* item = actions->ItemAt(i); i++) { 953 if (!fContextMenu->AddItem(item, index + i)) { 954 for (i--; i >= 0; i--) 955 fContextMenu->RemoveItem(fContextMenu->ItemAt(index + i)); 956 957 return B_NO_MEMORY; 958 } 959 } 960 961 return B_OK; 962 } 963 964 private: 965 ModelNode* fNode; 966 BLooper* fParentLooper; 967 BMessenger fParent; 968 ContextMenu* fContextMenu; 969 Settings* fRendererSettings; 970 SettingsMenu* fRendererSettingsMenu; 971 bool fRendererMenuAdded; 972 bool fMenuPreparedToShow; 973 }; 974 975 976 // #pragma mark - ContainerListener 977 978 979 VariablesView::ContainerListener::ContainerListener(BHandler* indirectTarget) 980 : 981 fIndirectTarget(indirectTarget), 982 fModel(NULL) 983 { 984 } 985 986 987 void 988 VariablesView::ContainerListener::SetModel(VariableTableModel* model) 989 { 990 fModel = model; 991 } 992 993 994 void 995 VariablesView::ContainerListener::ValueNodeChanged(ValueNodeChild* nodeChild, 996 ValueNode* oldNode, ValueNode* newNode) 997 { 998 // If the looper is already locked, invoke the model's hook synchronously. 999 if (fIndirectTarget->Looper()->IsLocked()) { 1000 fModel->ValueNodeChanged(nodeChild, oldNode, newNode); 1001 return; 1002 } 1003 1004 // looper not locked yet -- call asynchronously to avoid reverse locking 1005 // order 1006 BReference<ValueNodeChild> nodeChildReference(nodeChild); 1007 BReference<ValueNode> oldNodeReference(oldNode); 1008 BReference<ValueNode> newNodeReference(newNode); 1009 1010 BMessage message(MSG_VALUE_NODE_CHANGED); 1011 if (message.AddPointer("nodeChild", nodeChild) == B_OK 1012 && message.AddPointer("oldNode", oldNode) == B_OK 1013 && message.AddPointer("newNode", newNode) == B_OK 1014 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 1015 == B_OK) { 1016 nodeChildReference.Detach(); 1017 oldNodeReference.Detach(); 1018 newNodeReference.Detach(); 1019 } 1020 } 1021 1022 1023 void 1024 VariablesView::ContainerListener::ValueNodeChildrenCreated(ValueNode* node) 1025 { 1026 // If the looper is already locked, invoke the model's hook synchronously. 1027 if (fIndirectTarget->Looper()->IsLocked()) { 1028 fModel->ValueNodeChildrenCreated(node); 1029 return; 1030 } 1031 1032 // looper not locked yet -- call asynchronously to avoid reverse locking 1033 // order 1034 BReference<ValueNode> nodeReference(node); 1035 1036 BMessage message(MSG_VALUE_NODE_CHILDREN_CREATED); 1037 if (message.AddPointer("node", node) == B_OK 1038 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 1039 == B_OK) { 1040 nodeReference.Detach(); 1041 } 1042 } 1043 1044 1045 void 1046 VariablesView::ContainerListener::ValueNodeChildrenDeleted(ValueNode* node) 1047 { 1048 // If the looper is already locked, invoke the model's hook synchronously. 1049 if (fIndirectTarget->Looper()->IsLocked()) { 1050 fModel->ValueNodeChildrenDeleted(node); 1051 return; 1052 } 1053 1054 // looper not locked yet -- call asynchronously to avoid reverse locking 1055 // order 1056 BReference<ValueNode> nodeReference(node); 1057 1058 BMessage message(MSG_VALUE_NODE_CHILDREN_DELETED); 1059 if (message.AddPointer("node", node) == B_OK 1060 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 1061 == B_OK) { 1062 nodeReference.Detach(); 1063 } 1064 } 1065 1066 1067 void 1068 VariablesView::ContainerListener::ValueNodeValueChanged(ValueNode* node) 1069 { 1070 // If the looper is already locked, invoke the model's hook synchronously. 1071 if (fIndirectTarget->Looper()->IsLocked()) { 1072 fModel->ValueNodeValueChanged(node); 1073 return; 1074 } 1075 1076 // looper not locked yet -- call asynchronously to avoid reverse locking 1077 // order 1078 BReference<ValueNode> nodeReference(node); 1079 1080 BMessage message(MSG_VALUE_NODE_VALUE_CHANGED); 1081 if (message.AddPointer("node", node) == B_OK 1082 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 1083 == B_OK) { 1084 nodeReference.Detach(); 1085 } 1086 } 1087 1088 1089 void 1090 VariablesView::ContainerListener::ModelNodeHidden(ModelNode* node) 1091 { 1092 BReference<ModelNode> nodeReference(node); 1093 1094 BMessage message(MSG_MODEL_NODE_HIDDEN); 1095 if (message.AddPointer("node", node) == B_OK 1096 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 1097 == B_OK) { 1098 nodeReference.Detach(); 1099 } 1100 } 1101 1102 1103 void 1104 VariablesView::ContainerListener::ModelNodeValueRequested(ModelNode* node) 1105 { 1106 BReference<ModelNode> nodeReference(node); 1107 1108 BMessage message(MSG_VALUE_NODE_NEEDS_VALUE); 1109 if (message.AddPointer("node", node) == B_OK 1110 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 1111 == B_OK) { 1112 nodeReference.Detach(); 1113 } 1114 } 1115 1116 1117 void 1118 VariablesView::ContainerListener::ModelNodeRestoreViewStateRequested( 1119 ModelNode* node) 1120 { 1121 BReference<ModelNode> nodeReference(node); 1122 1123 BMessage message(MSG_RESTORE_PARTIAL_VIEW_STATE); 1124 if (message.AddPointer("node", node) == B_OK 1125 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 1126 == B_OK) { 1127 nodeReference.Detach(); 1128 } 1129 } 1130 1131 1132 // #pragma mark - VariableTableModel 1133 1134 1135 VariablesView::VariableTableModel::VariableTableModel( 1136 ValueNodeManager* manager) 1137 : 1138 fThread(NULL), 1139 fNodeManager(manager), 1140 fContainerListener(NULL), 1141 fNodeTable() 1142 { 1143 fNodeManager->AcquireReference(); 1144 } 1145 1146 1147 VariablesView::VariableTableModel::~VariableTableModel() 1148 { 1149 fNodeManager->ReleaseReference(); 1150 } 1151 1152 1153 status_t 1154 VariablesView::VariableTableModel::Init() 1155 { 1156 return fNodeTable.Init(); 1157 } 1158 1159 1160 void 1161 VariablesView::VariableTableModel::SetContainerListener( 1162 ContainerListener* listener) 1163 { 1164 if (listener == fContainerListener) 1165 return; 1166 1167 if (fContainerListener != NULL) { 1168 if (fNodeManager != NULL) 1169 fNodeManager->RemoveListener(fContainerListener); 1170 1171 fContainerListener->SetModel(NULL); 1172 } 1173 1174 fContainerListener = listener; 1175 1176 if (fContainerListener != NULL) { 1177 fContainerListener->SetModel(this); 1178 1179 if (fNodeManager != NULL) 1180 fNodeManager->AddListener(fContainerListener); 1181 } 1182 } 1183 1184 1185 void 1186 VariablesView::VariableTableModel::SetStackFrame(::Thread* thread, 1187 StackFrame* stackFrame) 1188 { 1189 fThread = thread; 1190 1191 fNodeManager->SetStackFrame(thread, stackFrame); 1192 1193 int32 count = fNodes.CountItems(); 1194 fNodeTable.Clear(true); 1195 1196 if (!fNodes.IsEmpty()) { 1197 for (int32 i = 0; i < count; i++) 1198 fNodes.ItemAt(i)->ReleaseReference(); 1199 fNodes.MakeEmpty(); 1200 } 1201 1202 NotifyNodesRemoved(TreeTablePath(), 0, count); 1203 1204 if (stackFrame == NULL) 1205 return; 1206 1207 ValueNodeContainer* container = fNodeManager->GetContainer(); 1208 AutoLocker<ValueNodeContainer> containerLocker(container); 1209 1210 for (int32 i = 0; i < container->CountChildren(); i++) { 1211 VariableValueNodeChild* child = dynamic_cast<VariableValueNodeChild *>( 1212 container->ChildAt(i)); 1213 _AddNode(child->GetVariable(), NULL, child); 1214 // top level nodes get their children added immediately 1215 // so those won't invoke our callback hook. Add them directly here. 1216 ValueNodeChildrenCreated(child->Node()); 1217 } 1218 } 1219 1220 1221 void 1222 VariablesView::VariableTableModel::ValueNodeChanged(ValueNodeChild* nodeChild, 1223 ValueNode* oldNode, ValueNode* newNode) 1224 { 1225 AutoLocker<ValueNodeContainer> containerLocker( 1226 fNodeManager->GetContainer()); 1227 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1228 if (modelNode == NULL) 1229 return; 1230 1231 if (oldNode != NULL) { 1232 ValueNodeChildrenDeleted(oldNode); 1233 NotifyNodeChanged(modelNode); 1234 } 1235 } 1236 1237 1238 void 1239 VariablesView::VariableTableModel::ValueNodeChildrenCreated( 1240 ValueNode* valueNode) 1241 { 1242 AutoLocker<ValueNodeContainer> containerLocker( 1243 fNodeManager->GetContainer()); 1244 1245 // check whether we know the node 1246 ValueNodeChild* nodeChild = valueNode->NodeChild(); 1247 if (nodeChild == NULL) 1248 return; 1249 1250 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1251 if (modelNode == NULL) 1252 return; 1253 1254 // Iterate through the children and create model nodes for the ones we 1255 // don't know yet. 1256 int32 childCount = valueNode->CountChildren(); 1257 for (int32 i = 0; i < childCount; i++) { 1258 ValueNodeChild* child = valueNode->ChildAt(i); 1259 if (fNodeTable.Lookup(child) == NULL) { 1260 _AddNode(modelNode->GetVariable(), modelNode, child, 1261 child->IsInternal(), childCount == 1); 1262 } 1263 1264 ModelNode* childNode = fNodeTable.Lookup(child); 1265 if (childNode != NULL) 1266 fContainerListener->ModelNodeValueRequested(childNode); 1267 } 1268 1269 if (valueNode->ChildCreationNeedsValue()) 1270 fContainerListener->ModelNodeRestoreViewStateRequested(modelNode); 1271 } 1272 1273 1274 void 1275 VariablesView::VariableTableModel::ValueNodeChildrenDeleted(ValueNode* node) 1276 { 1277 AutoLocker<ValueNodeContainer> containerLocker( 1278 fNodeManager->GetContainer()); 1279 1280 // check whether we know the node 1281 ValueNodeChild* nodeChild = node->NodeChild(); 1282 if (nodeChild == NULL) 1283 return; 1284 1285 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1286 if (modelNode == NULL) 1287 return; 1288 1289 // in the case of an address node with a hidden child, 1290 // we want to send removal notifications for the children 1291 // instead. 1292 BReference<ModelNode> hiddenChild; 1293 if (modelNode->CountChildren() == 1 1294 && modelNode->ChildAt(0)->IsHidden()) { 1295 hiddenChild.SetTo(modelNode->ChildAt(0)); 1296 modelNode->RemoveChild(hiddenChild); 1297 modelNode = hiddenChild; 1298 fNodeTable.Remove(hiddenChild); 1299 } 1300 1301 for (int32 i = modelNode->CountChildren() - 1; i >= 0 ; i--) { 1302 BReference<ModelNode> childNode = modelNode->ChildAt(i); 1303 // recursively remove the current node's child hierarchy. 1304 if (childNode->CountChildren() != 0) 1305 ValueNodeChildrenDeleted(childNode->NodeChild()->Node()); 1306 1307 TreeTablePath treePath; 1308 if (GetTreePath(childNode, treePath)) { 1309 int32 index = treePath.RemoveLastComponent(); 1310 NotifyNodesRemoved(treePath, index, 1); 1311 } 1312 modelNode->RemoveChild(childNode); 1313 fNodeTable.Remove(childNode); 1314 } 1315 } 1316 1317 1318 void 1319 VariablesView::VariableTableModel::ValueNodeValueChanged(ValueNode* valueNode) 1320 { 1321 AutoLocker<ValueNodeContainer> containerLocker( 1322 fNodeManager->GetContainer()); 1323 1324 // check whether we know the node 1325 ValueNodeChild* nodeChild = valueNode->NodeChild(); 1326 if (nodeChild == NULL) 1327 return; 1328 1329 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1330 if (modelNode == NULL) 1331 return; 1332 1333 // check whether the value actually changed 1334 Value* value = valueNode->GetValue(); 1335 if (value == modelNode->GetValue()) 1336 return; 1337 1338 // get a value handler 1339 ValueHandler* valueHandler; 1340 status_t error = ValueHandlerRoster::Default()->FindValueHandler(value, 1341 valueHandler); 1342 if (error != B_OK) 1343 return; 1344 BReference<ValueHandler> handlerReference(valueHandler, true); 1345 1346 // create a table cell renderer for the value 1347 TableCellValueRenderer* renderer = NULL; 1348 error = valueHandler->GetTableCellValueRenderer(value, renderer); 1349 if (error != B_OK) 1350 return; 1351 1352 BReference<TableCellValueRenderer> rendererReference(renderer, true); 1353 // set value/handler/renderer 1354 modelNode->SetValue(value); 1355 modelNode->SetValueHandler(valueHandler); 1356 modelNode->SetTableCellRenderer(renderer); 1357 1358 // we have to restore renderer settings here since until this point 1359 // we don't yet know what renderer is in use. 1360 if (renderer != NULL) { 1361 Settings* settings = renderer->GetSettings(); 1362 if (settings != NULL) 1363 settings->RestoreValues(modelNode->GetLastRendererSettings()); 1364 } 1365 1366 // notify table model listeners 1367 NotifyNodeChanged(modelNode); 1368 } 1369 1370 1371 int32 1372 VariablesView::VariableTableModel::CountColumns() const 1373 { 1374 return 3; 1375 } 1376 1377 1378 void* 1379 VariablesView::VariableTableModel::Root() const 1380 { 1381 return (void*)this; 1382 } 1383 1384 1385 int32 1386 VariablesView::VariableTableModel::CountChildren(void* parent) const 1387 { 1388 if (parent == this) 1389 return fNodes.CountItems(); 1390 1391 // If the node only has a hidden child, pretend the node directly has the 1392 // child's children. 1393 ModelNode* modelNode = (ModelNode*)parent; 1394 int32 childCount = modelNode->CountChildren(); 1395 if (childCount == 1) { 1396 ModelNode* child = modelNode->ChildAt(0); 1397 if (child->IsHidden()) 1398 return child->CountChildren(); 1399 } 1400 1401 return childCount; 1402 } 1403 1404 1405 void* 1406 VariablesView::VariableTableModel::ChildAt(void* parent, int32 index) const 1407 { 1408 if (parent == this) 1409 return fNodes.ItemAt(index); 1410 1411 // If the node only has a hidden child, pretend the node directly has the 1412 // child's children. 1413 ModelNode* modelNode = (ModelNode*)parent; 1414 int32 childCount = modelNode->CountChildren(); 1415 if (childCount == 1) { 1416 ModelNode* child = modelNode->ChildAt(0); 1417 if (child->IsHidden()) 1418 return child->ChildAt(index); 1419 } 1420 1421 return modelNode->ChildAt(index); 1422 } 1423 1424 1425 bool 1426 VariablesView::VariableTableModel::GetValueAt(void* object, int32 columnIndex, 1427 BVariant& _value) 1428 { 1429 ModelNode* node = (ModelNode*)object; 1430 1431 switch (columnIndex) { 1432 case 0: 1433 _value.SetTo(node->Name(), B_VARIANT_DONT_COPY_DATA); 1434 return true; 1435 case 1: 1436 if (node->GetValue() == NULL) { 1437 ValueLocation* location = node->NodeChild()->Location(); 1438 if (location == NULL) 1439 return false; 1440 1441 ValueNode* childNode = node->NodeChild()->Node(); 1442 if (childNode == NULL) 1443 return false; 1444 1445 Type* nodeChildRawType = childNode->GetType()->ResolveRawType( 1446 false); 1447 if (nodeChildRawType->Kind() == TYPE_COMPOUND) 1448 { 1449 if (location->CountPieces() > 1) 1450 return false; 1451 1452 BString data; 1453 ValuePieceLocation piece = location->PieceAt(0); 1454 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 1455 return false; 1456 1457 data.SetToFormat("[@ %#" B_PRIx64 "]", piece.address); 1458 _value.SetTo(data); 1459 return true; 1460 } 1461 return false; 1462 } 1463 1464 _value.SetTo(node, VALUE_NODE_TYPE); 1465 return true; 1466 case 2: 1467 { 1468 // use the type of the underlying value node, as it may 1469 // be different from the initially assigned top level type 1470 // due to casting 1471 ValueNode* childNode = node->NodeChild()->Node(); 1472 if (childNode == NULL) 1473 return false; 1474 1475 Type* type = childNode->GetType(); 1476 if (type == NULL) 1477 return false; 1478 1479 _value.SetTo(type->Name(), B_VARIANT_DONT_COPY_DATA); 1480 return true; 1481 } 1482 default: 1483 return false; 1484 } 1485 } 1486 1487 1488 void 1489 VariablesView::VariableTableModel::NodeExpanded(ModelNode* node) 1490 { 1491 AutoLocker<ValueNodeContainer> containerLocker( 1492 fNodeManager->GetContainer()); 1493 // add children of all children 1494 1495 // If the node only has a hidden child, add the child's children instead. 1496 if (node->CountChildren() == 1) { 1497 ModelNode* child = node->ChildAt(0); 1498 if (child->IsHidden()) 1499 node = child; 1500 } 1501 1502 // add the children 1503 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) 1504 fNodeManager->AddChildNodes(child->NodeChild()); 1505 } 1506 1507 1508 void 1509 VariablesView::VariableTableModel::NotifyNodeChanged(ModelNode* node) 1510 { 1511 if (!node->IsHidden()) { 1512 TreeTablePath treePath; 1513 if (GetTreePath(node, treePath)) { 1514 int32 index = treePath.RemoveLastComponent(); 1515 NotifyNodesChanged(treePath, index, 1); 1516 } 1517 } 1518 } 1519 1520 1521 void 1522 VariablesView::VariableTableModel::NotifyNodeHidden(ModelNode* node) 1523 { 1524 fContainerListener->ModelNodeHidden(node); 1525 } 1526 1527 1528 bool 1529 VariablesView::VariableTableModel::GetToolTipForTablePath( 1530 const TreeTablePath& path, int32 columnIndex, BToolTip** _tip) 1531 { 1532 ModelNode* node = (ModelNode*)NodeForPath(path); 1533 if (node == NULL) 1534 return false; 1535 1536 BString tipData; 1537 ValueNodeChild* child = node->NodeChild(); 1538 status_t error = child->LocationResolutionState(); 1539 if (error != B_OK) 1540 tipData.SetToFormat("Unable to resolve location: %s", strerror(error)); 1541 else { 1542 ValueNode* valueNode = child->Node(); 1543 if (valueNode == NULL) 1544 return false; 1545 error = valueNode->LocationAndValueResolutionState(); 1546 if (error != B_OK) { 1547 tipData.SetToFormat("Unable to resolve value: %s\n\n", 1548 strerror(error)); 1549 } 1550 1551 switch (columnIndex) { 1552 case 0: 1553 { 1554 ValueLocation* location = child->Location(); 1555 for (int32 i = 0; i < location->CountPieces(); i++) { 1556 ValuePieceLocation piece = location->PieceAt(i); 1557 BString pieceData; 1558 switch (piece.type) { 1559 case VALUE_PIECE_LOCATION_MEMORY: 1560 pieceData.SetToFormat("(%" B_PRId32 "): Address: " 1561 "%#" B_PRIx64 ", Size: %" B_PRId64 " bytes\n", 1562 i, piece.address, piece.size); 1563 break; 1564 case VALUE_PIECE_LOCATION_REGISTER: 1565 { 1566 Architecture* architecture = fThread->GetTeam() 1567 ->GetArchitecture(); 1568 pieceData.SetToFormat("(%" B_PRId32 "): Register " 1569 "(%s)\n", i, 1570 architecture->Registers()[piece.reg].Name()); 1571 break; 1572 } 1573 default: 1574 break; 1575 } 1576 1577 tipData += pieceData; 1578 } 1579 tipData += "Editable: "; 1580 tipData += error == B_OK && location->IsWritable() 1581 ? "Yes" : "No"; 1582 break; 1583 } 1584 case 1: 1585 { 1586 Value* value = node->GetValue(); 1587 if (value != NULL) 1588 value->ToString(tipData); 1589 1590 break; 1591 } 1592 default: 1593 break; 1594 } 1595 } 1596 1597 if (tipData.IsEmpty()) 1598 return false; 1599 1600 *_tip = new(std::nothrow) BTextToolTip(tipData); 1601 if (*_tip == NULL) 1602 return false; 1603 1604 return true; 1605 } 1606 1607 1608 status_t 1609 VariablesView::VariableTableModel::AddSyntheticNode(Variable* variable, 1610 ValueNodeChild*& _child, const char* presentationName) 1611 { 1612 ValueNodeContainer* container = fNodeManager->GetContainer(); 1613 AutoLocker<ValueNodeContainer> containerLocker(container); 1614 1615 status_t error; 1616 if (_child == NULL) { 1617 _child = new(std::nothrow) VariableValueNodeChild(variable); 1618 if (_child == NULL) 1619 return B_NO_MEMORY; 1620 1621 BReference<ValueNodeChild> childReference(_child, true); 1622 ValueNode* valueNode; 1623 if (_child->IsInternal()) 1624 error = _child->CreateInternalNode(valueNode); 1625 else { 1626 error = TypeHandlerRoster::Default()->CreateValueNode(_child, 1627 _child->GetType(), NULL, valueNode); 1628 } 1629 1630 if (error != B_OK) 1631 return error; 1632 1633 _child->SetNode(valueNode); 1634 valueNode->ReleaseReference(); 1635 } 1636 1637 container->AddChild(_child); 1638 1639 error = _AddNode(variable, NULL, _child); 1640 if (error != B_OK) { 1641 container->RemoveChild(_child); 1642 return error; 1643 } 1644 1645 // since we're injecting these nodes synthetically, 1646 // we have to manually ask the node manager to create any 1647 // applicable children; this would normally be done implicitly 1648 // for top level nodes, as they're added from the parameters/locals, 1649 // but not here. 1650 fNodeManager->AddChildNodes(_child); 1651 1652 ModelNode* childNode = fNodeTable.Lookup(_child); 1653 if (childNode != NULL) { 1654 if (presentationName != NULL) 1655 childNode->SetPresentationName(presentationName); 1656 1657 ValueNode* valueNode = _child->Node(); 1658 if (valueNode->LocationAndValueResolutionState() 1659 == VALUE_NODE_UNRESOLVED) { 1660 fContainerListener->ModelNodeValueRequested(childNode); 1661 } else 1662 ValueNodeValueChanged(valueNode); 1663 } 1664 ValueNodeChildrenCreated(_child->Node()); 1665 1666 return B_OK; 1667 } 1668 1669 1670 void 1671 VariablesView::VariableTableModel::RemoveSyntheticNode(ModelNode* node) 1672 { 1673 int32 index = fNodes.IndexOf(node); 1674 if (index < 0) 1675 return; 1676 1677 fNodeTable.Remove(node); 1678 1679 fNodes.RemoveItemAt(index); 1680 1681 NotifyNodesRemoved(TreeTablePath(), index, 1); 1682 1683 node->ReleaseReference(); 1684 } 1685 1686 1687 status_t 1688 VariablesView::VariableTableModel::_AddNode(Variable* variable, 1689 ModelNode* parent, ValueNodeChild* nodeChild, bool isPresentationNode, 1690 bool isOnlyChild) 1691 { 1692 // Don't create nodes for unspecified types -- we can't get/show their 1693 // value anyway. 1694 Type* nodeChildRawType = nodeChild->GetType()->ResolveRawType(false); 1695 if (nodeChildRawType->Kind() == TYPE_UNSPECIFIED) 1696 return B_OK; 1697 1698 ModelNode* node = new(std::nothrow) ModelNode(parent, variable, nodeChild, 1699 isPresentationNode); 1700 BReference<ModelNode> nodeReference(node, true); 1701 if (node == NULL || node->Init() != B_OK) 1702 return B_NO_MEMORY; 1703 1704 int32 childIndex; 1705 1706 if (parent != NULL) { 1707 childIndex = parent->CountChildren(); 1708 1709 if (!parent->AddChild(node)) 1710 return B_NO_MEMORY; 1711 // the parent has a reference, now 1712 } else { 1713 childIndex = fNodes.CountItems(); 1714 1715 if (!fNodes.AddItem(node)) 1716 return B_NO_MEMORY; 1717 nodeReference.Detach(); 1718 // the fNodes list has a reference, now 1719 } 1720 1721 fNodeTable.Insert(node); 1722 1723 // if an address type node has only a single child, and that child 1724 // is a compound type, mark it hidden 1725 if (isOnlyChild && parent != NULL) { 1726 ValueNode* parentValueNode = parent->NodeChild()->Node(); 1727 if (parentValueNode != NULL) { 1728 if (parentValueNode->GetType()->ResolveRawType(false)->Kind() 1729 == TYPE_ADDRESS) { 1730 type_kind childKind = nodeChildRawType->Kind(); 1731 if (childKind == TYPE_COMPOUND || childKind == TYPE_ARRAY) { 1732 node->SetHidden(true); 1733 1734 // we need to tell the listener about nodes like this so 1735 // any necessary actions can be taken for them (i.e. value 1736 // resolution), since they're otherwise invisible to 1737 // outsiders. 1738 NotifyNodeHidden(node); 1739 } 1740 } 1741 } 1742 } 1743 1744 // notify table model listeners 1745 if (!node->IsHidden()) { 1746 TreeTablePath path; 1747 if (parent == NULL || GetTreePath(parent, path)) 1748 NotifyNodesAdded(path, childIndex, 1); 1749 } 1750 1751 // if the node is hidden, add its children 1752 if (node->IsHidden()) 1753 fNodeManager->AddChildNodes(nodeChild); 1754 1755 return B_OK; 1756 } 1757 1758 1759 bool 1760 VariablesView::VariableTableModel::GetTreePath(ModelNode* node, 1761 TreeTablePath& _path) const 1762 { 1763 // recurse, if the node has a parent 1764 if (ModelNode* parent = node->Parent()) { 1765 if (!GetTreePath(parent, _path)) 1766 return false; 1767 1768 if (node->IsHidden()) 1769 return true; 1770 1771 return _path.AddComponent(parent->IndexOf(node)); 1772 } 1773 1774 // no parent -- get the index and start the path 1775 int32 index = fNodes.IndexOf(node); 1776 _path.Clear(); 1777 return index >= 0 && _path.AddComponent(index); 1778 } 1779 1780 1781 // #pragma mark - VariablesView 1782 1783 1784 VariablesView::VariablesView(Listener* listener) 1785 : 1786 BGroupView(B_VERTICAL), 1787 fThread(NULL), 1788 fStackFrame(NULL), 1789 fVariableTable(NULL), 1790 fVariableTableModel(NULL), 1791 fContainerListener(NULL), 1792 fPreviousViewState(NULL), 1793 fViewStateHistory(NULL), 1794 fExpressions(NULL), 1795 fExpressionChildren(10, false), 1796 fTableCellContextMenuTracker(NULL), 1797 fPendingTypecastInfo(NULL), 1798 fTemporaryExpression(NULL), 1799 fFrameClearPending(false), 1800 fEditWindow(NULL), 1801 fListener(listener) 1802 { 1803 SetName("Variables"); 1804 } 1805 1806 1807 VariablesView::~VariablesView() 1808 { 1809 if (fEditWindow != NULL) 1810 BMessenger(fEditWindow).SendMessage(B_QUIT_REQUESTED); 1811 1812 SetStackFrame(NULL, NULL); 1813 fVariableTable->SetTreeTableModel(NULL); 1814 1815 if (fPreviousViewState != NULL) 1816 fPreviousViewState->ReleaseReference(); 1817 delete fViewStateHistory; 1818 1819 if (fVariableTableModel != NULL) { 1820 fVariableTableModel->SetContainerListener(NULL); 1821 delete fVariableTableModel; 1822 } 1823 1824 delete fContainerListener; 1825 if (fPendingTypecastInfo != NULL) 1826 fPendingTypecastInfo->ReleaseReference(); 1827 1828 if (fTemporaryExpression != NULL) 1829 fTemporaryExpression->ReleaseReference(); 1830 1831 if (fExpressions != NULL) { 1832 ExpressionInfoEntry* entry = fExpressions->Clear(); 1833 while (entry != NULL) { 1834 ExpressionInfoEntry* next = entry->next; 1835 delete entry; 1836 entry = next; 1837 } 1838 } 1839 1840 delete fExpressions; 1841 } 1842 1843 1844 /*static*/ VariablesView* 1845 VariablesView::Create(Listener* listener, ValueNodeManager* manager) 1846 { 1847 VariablesView* self = new VariablesView(listener); 1848 1849 try { 1850 self->_Init(manager); 1851 } catch (...) { 1852 delete self; 1853 throw; 1854 } 1855 1856 return self; 1857 } 1858 1859 1860 void 1861 VariablesView::SetStackFrame(::Thread* thread, StackFrame* stackFrame) 1862 { 1863 bool updateValues = fFrameClearPending; 1864 // We only want to save previous values if we've continued 1865 // execution (i.e. thread/frame are being cleared). 1866 // Otherwise, we'll overwrite our previous values simply 1867 // by switching frames within the same stack trace, which isn't 1868 // desired behavior. 1869 1870 fFrameClearPending = false; 1871 1872 if (thread == fThread && stackFrame == fStackFrame) 1873 return; 1874 1875 _SaveViewState(updateValues); 1876 1877 _FinishContextMenu(true); 1878 1879 for (int32 i = 0; i < fExpressionChildren.CountItems(); i++) 1880 fExpressionChildren.ItemAt(i)->ReleaseReference(); 1881 fExpressionChildren.MakeEmpty(); 1882 1883 if (fThread != NULL) 1884 fThread->ReleaseReference(); 1885 if (fStackFrame != NULL) 1886 fStackFrame->ReleaseReference(); 1887 1888 fThread = thread; 1889 fStackFrame = stackFrame; 1890 1891 if (fThread != NULL) 1892 fThread->AcquireReference(); 1893 if (fStackFrame != NULL) 1894 fStackFrame->AcquireReference(); 1895 1896 fVariableTableModel->SetStackFrame(fThread, fStackFrame); 1897 1898 // request loading the parameter and variable values 1899 if (fThread != NULL && fStackFrame != NULL) { 1900 AutoLocker<Team> locker(fThread->GetTeam()); 1901 1902 void* root = fVariableTableModel->Root(); 1903 int32 count = fVariableTableModel->CountChildren(root); 1904 for (int32 i = 0; i < count; i++) { 1905 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i); 1906 _RequestNodeValue(node); 1907 } 1908 1909 _RestoreExpressionNodes(); 1910 } 1911 1912 _RestoreViewState(); 1913 } 1914 1915 1916 void 1917 VariablesView::MessageReceived(BMessage* message) 1918 { 1919 switch (message->what) { 1920 case MSG_SHOW_INSPECTOR_WINDOW: 1921 { 1922 // TODO: it'd probably be more ideal to extend the context 1923 // action mechanism to allow one to specify an explicit 1924 // target for each action rather than them all defaulting 1925 // to targetting here. 1926 Looper()->PostMessage(message); 1927 break; 1928 } 1929 case MSG_SHOW_VARIABLE_EDIT_WINDOW: 1930 { 1931 if (fEditWindow != NULL) 1932 fEditWindow->Activate(); 1933 else { 1934 ModelNode* node = NULL; 1935 if (message->FindPointer("node", reinterpret_cast<void**>( 1936 &node)) != B_OK) { 1937 break; 1938 } 1939 1940 Value* value = NULL; 1941 if (message->FindPointer("value", reinterpret_cast<void**>( 1942 &value)) != B_OK) { 1943 break; 1944 } 1945 1946 _HandleEditVariableRequest(node, value); 1947 } 1948 break; 1949 } 1950 case MSG_VARIABLE_EDIT_WINDOW_CLOSED: 1951 { 1952 fEditWindow = NULL; 1953 break; 1954 } 1955 case MSG_WRITE_VARIABLE_VALUE: 1956 { 1957 Value* value = NULL; 1958 if (message->FindPointer("value", reinterpret_cast<void**>( 1959 &value)) != B_OK) { 1960 break; 1961 } 1962 1963 BReference<Value> valueReference(value, true); 1964 1965 ValueNode* node = NULL; 1966 if (message->FindPointer("node", reinterpret_cast<void**>( 1967 &node)) != B_OK) { 1968 break; 1969 } 1970 1971 fListener->ValueNodeWriteRequested(node, 1972 fStackFrame->GetCpuState(), value); 1973 break; 1974 } 1975 case MSG_SHOW_TYPECAST_NODE_PROMPT: 1976 { 1977 BMessage* promptMessage = new(std::nothrow) BMessage( 1978 MSG_TYPECAST_NODE); 1979 1980 if (promptMessage == NULL) 1981 return; 1982 1983 ObjectDeleter<BMessage> messageDeleter(promptMessage); 1984 promptMessage->AddPointer("node", fVariableTable 1985 ->SelectionModel()->NodeAt(0)); 1986 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 1987 "Specify Type", "Type: ", NULL, BMessenger(this), 1988 promptMessage); 1989 if (promptWindow == NULL) 1990 return; 1991 1992 messageDeleter.Detach(); 1993 promptWindow->CenterOnScreen(); 1994 promptWindow->Show(); 1995 break; 1996 } 1997 case MSG_TYPECAST_NODE: 1998 { 1999 ModelNode* node = NULL; 2000 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 2001 != B_OK) { 2002 break; 2003 } 2004 2005 BString typeExpression; 2006 if (message->FindString("text", &typeExpression) == B_OK) { 2007 if (typeExpression.IsEmpty()) 2008 break; 2009 2010 if (fPendingTypecastInfo != NULL) 2011 fPendingTypecastInfo->ReleaseReference(); 2012 2013 fPendingTypecastInfo = new(std::nothrow) 2014 VariablesExpressionInfo(typeExpression, node); 2015 if (fPendingTypecastInfo == NULL) { 2016 // TODO: notify user 2017 break; 2018 } 2019 2020 fPendingTypecastInfo->AddListener(this); 2021 fListener->ExpressionEvaluationRequested(fPendingTypecastInfo, 2022 fStackFrame, fThread); 2023 } 2024 break; 2025 } 2026 case MSG_TYPECAST_TO_ARRAY: 2027 { 2028 ModelNode* node = NULL; 2029 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 2030 != B_OK) { 2031 break; 2032 } 2033 2034 Type* baseType = dynamic_cast<AddressType*>(node->NodeChild() 2035 ->Node()->GetType())->BaseType(); 2036 ArrayType* arrayType = NULL; 2037 if (baseType->CreateDerivedArrayType(0, kMaxArrayElementCount, 2038 false, arrayType) != B_OK) { 2039 break; 2040 } 2041 2042 AddressType* addressType = NULL; 2043 BReference<Type> typeRef(arrayType, true); 2044 if (arrayType->CreateDerivedAddressType(DERIVED_TYPE_POINTER, 2045 addressType) != B_OK) { 2046 break; 2047 } 2048 2049 typeRef.Detach(); 2050 typeRef.SetTo(addressType, true); 2051 ValueNode* valueNode = NULL; 2052 if (TypeHandlerRoster::Default()->CreateValueNode( 2053 node->NodeChild(), addressType, NULL, valueNode) != B_OK) { 2054 break; 2055 } 2056 2057 typeRef.Detach(); 2058 node->NodeChild()->SetNode(valueNode); 2059 node->SetCastedType(addressType); 2060 fVariableTableModel->NotifyNodeChanged(node); 2061 break; 2062 } 2063 case MSG_SHOW_CONTAINER_RANGE_PROMPT: 2064 { 2065 ModelNode* node = (ModelNode*)fVariableTable 2066 ->SelectionModel()->NodeAt(0); 2067 int32 lowerBound, upperBound; 2068 ValueNode* valueNode = node->NodeChild()->Node(); 2069 if (!valueNode->IsRangedContainer()) { 2070 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2071 if (!valueNode->IsRangedContainer()) 2072 break; 2073 } 2074 2075 bool fixedRange = valueNode->IsContainerRangeFixed(); 2076 if (valueNode->SupportedChildRange(lowerBound, upperBound) 2077 != B_OK) { 2078 break; 2079 } 2080 2081 BMessage* promptMessage = new(std::nothrow) BMessage( 2082 MSG_SET_CONTAINER_RANGE); 2083 if (promptMessage == NULL) 2084 break; 2085 2086 ObjectDeleter<BMessage> messageDeleter(promptMessage); 2087 promptMessage->AddPointer("node", node); 2088 promptMessage->AddBool("fixedRange", fixedRange); 2089 BString infoText; 2090 if (fixedRange) { 2091 infoText.SetToFormat("Allowed range: %" B_PRId32 2092 "-%" B_PRId32 ".", lowerBound, upperBound); 2093 } else { 2094 infoText.SetToFormat("Current range: %" B_PRId32 2095 "-%" B_PRId32 ".", lowerBound, upperBound); 2096 } 2097 2098 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 2099 "Set Range", "Range: ", infoText.String(), BMessenger(this), 2100 promptMessage); 2101 if (promptWindow == NULL) 2102 return; 2103 2104 messageDeleter.Detach(); 2105 promptWindow->CenterOnScreen(); 2106 promptWindow->Show(); 2107 break; 2108 } 2109 case MSG_SET_CONTAINER_RANGE: 2110 { 2111 ModelNode* node = (ModelNode*)fVariableTable 2112 ->SelectionModel()->NodeAt(0); 2113 int32 lowerBound, upperBound; 2114 ValueNode* valueNode = node->NodeChild()->Node(); 2115 if (!valueNode->IsRangedContainer()) 2116 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2117 if (valueNode->SupportedChildRange(lowerBound, upperBound) != B_OK) 2118 break; 2119 2120 bool fixedRange = message->FindBool("fixedRange"); 2121 2122 BString rangeExpression = message->FindString("text"); 2123 if (rangeExpression.Length() == 0) 2124 break; 2125 2126 RangeList ranges; 2127 status_t result = UiUtils::ParseRangeExpression( 2128 rangeExpression, lowerBound, upperBound, fixedRange, ranges); 2129 if (result != B_OK) 2130 break; 2131 2132 valueNode->ClearChildren(); 2133 for (int32 i = 0; i < ranges.CountRanges(); i++) { 2134 const Range* range = ranges.RangeAt(i); 2135 result = valueNode->CreateChildrenInRange( 2136 fThread->GetTeam()->GetTeamTypeInformation(), 2137 range->lowerBound, range->upperBound); 2138 if (result != B_OK) 2139 break; 2140 } 2141 break; 2142 } 2143 case MSG_SHOW_WATCH_VARIABLE_PROMPT: 2144 { 2145 ModelNode* node = reinterpret_cast<ModelNode*>( 2146 fVariableTable->SelectionModel()->NodeAt(0)); 2147 ValueLocation* location = node->NodeChild()->Location(); 2148 ValuePieceLocation piece = location->PieceAt(0); 2149 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 2150 break; 2151 2152 BMessage looperMessage(*message); 2153 looperMessage.AddUInt64("address", piece.address); 2154 looperMessage.AddInt32("length", piece.size); 2155 looperMessage.AddUInt32("type", B_DATA_READ_WRITE_WATCHPOINT); 2156 Looper()->PostMessage(&looperMessage); 2157 break; 2158 } 2159 case MSG_ADD_WATCH_EXPRESSION: 2160 { 2161 BMessage looperMessage(MSG_SHOW_EXPRESSION_PROMPT_WINDOW); 2162 looperMessage.AddPointer("target", this); 2163 Looper()->PostMessage(&looperMessage); 2164 break; 2165 } 2166 case MSG_REMOVE_WATCH_EXPRESSION: 2167 { 2168 ModelNode* node; 2169 if (message->FindPointer("node", reinterpret_cast<void**>(&node)) 2170 != B_OK) { 2171 break; 2172 } 2173 2174 _RemoveExpression(node); 2175 break; 2176 } 2177 case MSG_ADD_NEW_EXPRESSION: 2178 { 2179 const char* expression; 2180 if (message->FindString("expression", &expression) != B_OK) 2181 break; 2182 2183 bool persistentExpression = message->FindBool("persistent"); 2184 2185 ExpressionInfo* info; 2186 status_t error = _AddExpression(expression, persistentExpression, 2187 info); 2188 if (error != B_OK) { 2189 // TODO: notify user of failure 2190 break; 2191 } 2192 2193 fListener->ExpressionEvaluationRequested(info, fStackFrame, 2194 fThread); 2195 break; 2196 } 2197 case MSG_EXPRESSION_EVALUATED: 2198 { 2199 ExpressionInfo* info; 2200 status_t result; 2201 ExpressionResult* value = NULL; 2202 if (message->FindPointer("info", 2203 reinterpret_cast<void**>(&info)) != B_OK 2204 || message->FindInt32("result", &result) != B_OK) { 2205 break; 2206 } 2207 2208 BReference<ExpressionResult> valueReference; 2209 if (message->FindPointer("value", reinterpret_cast<void**>(&value)) 2210 == B_OK) { 2211 valueReference.SetTo(value, true); 2212 } 2213 2214 VariablesExpressionInfo* variableInfo 2215 = dynamic_cast<VariablesExpressionInfo*>(info); 2216 if (variableInfo != NULL) { 2217 if (fPendingTypecastInfo == variableInfo) { 2218 _HandleTypecastResult(result, value); 2219 fPendingTypecastInfo->ReleaseReference(); 2220 fPendingTypecastInfo = NULL; 2221 } 2222 } else { 2223 _AddExpressionNode(info, result, value); 2224 if (info == fTemporaryExpression) { 2225 info->ReleaseReference(); 2226 fTemporaryExpression = NULL; 2227 } 2228 } 2229 2230 break; 2231 } 2232 case MSG_USE_AUTOMATIC_HANDLER: 2233 case MSG_USE_EXPLICIT_HANDLER: 2234 { 2235 TypeHandler* handler = NULL; 2236 ModelNode* node = NULL; 2237 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 2238 != B_OK) { 2239 break; 2240 } 2241 2242 if (message->what == MSG_USE_EXPLICIT_HANDLER 2243 && message->FindPointer("handler", reinterpret_cast<void**>( 2244 &handler)) != B_OK) { 2245 break; 2246 } 2247 2248 ValueNode* newNode; 2249 ValueNodeChild* child = node->NodeChild(); 2250 if (TypeHandlerRoster::Default()->CreateValueNode(child, 2251 child->GetType(), handler, newNode) != B_OK) { 2252 return; 2253 } 2254 2255 node->SetTypeHandler(handler); 2256 child->SetNode(newNode); 2257 _RequestNodeValue(node); 2258 break; 2259 } 2260 case MSG_VALUE_NODE_CHANGED: 2261 { 2262 ValueNodeChild* nodeChild; 2263 ValueNode* oldNode; 2264 ValueNode* newNode; 2265 if (message->FindPointer("nodeChild", (void**)&nodeChild) == B_OK 2266 && message->FindPointer("oldNode", (void**)&oldNode) == B_OK 2267 && message->FindPointer("newNode", (void**)&newNode) == B_OK) { 2268 BReference<ValueNodeChild> nodeChildReference(nodeChild, true); 2269 BReference<ValueNode> oldNodeReference(oldNode, true); 2270 BReference<ValueNode> newNodeReference(newNode, true); 2271 2272 fVariableTableModel->ValueNodeChanged(nodeChild, oldNode, 2273 newNode); 2274 } 2275 2276 break; 2277 } 2278 case MSG_VALUE_NODE_CHILDREN_CREATED: 2279 { 2280 ValueNode* node; 2281 if (message->FindPointer("node", (void**)&node) == B_OK) { 2282 BReference<ValueNode> newNodeReference(node, true); 2283 fVariableTableModel->ValueNodeChildrenCreated(node); 2284 } 2285 2286 break; 2287 } 2288 case MSG_VALUE_NODE_CHILDREN_DELETED: 2289 { 2290 ValueNode* node; 2291 if (message->FindPointer("node", (void**)&node) == B_OK) { 2292 BReference<ValueNode> newNodeReference(node, true); 2293 fVariableTableModel->ValueNodeChildrenDeleted(node); 2294 } 2295 2296 break; 2297 } 2298 case MSG_VALUE_NODE_VALUE_CHANGED: 2299 { 2300 ValueNode* node; 2301 if (message->FindPointer("node", (void**)&node) == B_OK) { 2302 BReference<ValueNode> newNodeReference(node, true); 2303 fVariableTableModel->ValueNodeValueChanged(node); 2304 } 2305 2306 break; 2307 } 2308 case MSG_RESTORE_PARTIAL_VIEW_STATE: 2309 { 2310 ModelNode* node; 2311 if (message->FindPointer("node", (void**)&node) == B_OK) { 2312 BReference<ModelNode> nodeReference(node, true); 2313 TreeTablePath path; 2314 if (fVariableTableModel->GetTreePath(node, path)) { 2315 FunctionID* functionID = fStackFrame->Function() 2316 ->GetFunctionID(); 2317 if (functionID == NULL) 2318 return; 2319 BReference<FunctionID> functionIDReference(functionID, 2320 true); 2321 VariablesViewState* viewState = fViewStateHistory 2322 ->GetState(fThread->ID(), functionID); 2323 if (viewState != NULL) { 2324 _ApplyViewStateDescendentNodeInfos(viewState, node, 2325 path); 2326 } 2327 } 2328 } 2329 break; 2330 } 2331 case MSG_VALUE_NODE_NEEDS_VALUE: 2332 case MSG_MODEL_NODE_HIDDEN: 2333 { 2334 ModelNode* node; 2335 if (message->FindPointer("node", (void**)&node) == B_OK) { 2336 BReference<ModelNode> modelNodeReference(node, true); 2337 _RequestNodeValue(node); 2338 } 2339 2340 break; 2341 } 2342 case MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE: 2343 { 2344 _FinishContextMenu(false); 2345 break; 2346 } 2347 case MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED: 2348 { 2349 ModelNode* node; 2350 if (message->FindPointer("node", (void**)&node) != B_OK) 2351 break; 2352 BReference<ModelNode> nodeReference(node, true); 2353 2354 fVariableTableModel->NotifyNodeChanged(node); 2355 break; 2356 } 2357 case B_COPY: 2358 { 2359 _CopyVariableValueToClipboard(); 2360 break; 2361 } 2362 default: 2363 BGroupView::MessageReceived(message); 2364 break; 2365 } 2366 } 2367 2368 2369 void 2370 VariablesView::DetachedFromWindow() 2371 { 2372 _FinishContextMenu(true); 2373 } 2374 2375 2376 void 2377 VariablesView::LoadSettings(const BMessage& settings) 2378 { 2379 BMessage tableSettings; 2380 if (settings.FindMessage("variableTable", &tableSettings) == B_OK) { 2381 GuiSettingsUtils::UnarchiveTableSettings(tableSettings, 2382 fVariableTable); 2383 } 2384 } 2385 2386 2387 status_t 2388 VariablesView::SaveSettings(BMessage& settings) 2389 { 2390 settings.MakeEmpty(); 2391 2392 BMessage tableSettings; 2393 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings, 2394 fVariableTable); 2395 if (result == B_OK) 2396 result = settings.AddMessage("variableTable", &tableSettings); 2397 2398 return result; 2399 } 2400 2401 2402 void 2403 VariablesView::SetStackFrameClearPending() 2404 { 2405 fFrameClearPending = true; 2406 } 2407 2408 2409 void 2410 VariablesView::TreeTableNodeExpandedChanged(TreeTable* table, 2411 const TreeTablePath& path, bool expanded) 2412 { 2413 if (fFrameClearPending) 2414 return; 2415 2416 if (expanded) { 2417 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2418 if (node == NULL) 2419 return; 2420 2421 fVariableTableModel->NodeExpanded(node); 2422 2423 // request the values of all children that don't have any yet 2424 2425 // If the node only has a hidden child, directly load the child's 2426 // children's values. 2427 if (node->CountChildren() == 1) { 2428 ModelNode* child = node->ChildAt(0); 2429 if (child->IsHidden()) 2430 node = child; 2431 } 2432 2433 // request the values 2434 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) { 2435 if (child->IsPresentationNode()) 2436 continue; 2437 2438 _RequestNodeValue(child); 2439 } 2440 } 2441 } 2442 2443 2444 void 2445 VariablesView::TreeTableNodeInvoked(TreeTable* table, 2446 const TreeTablePath& path) 2447 { 2448 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2449 if (node == NULL) 2450 return; 2451 2452 ValueNodeChild* child = node->NodeChild(); 2453 2454 if (child->LocationResolutionState() != B_OK) 2455 return; 2456 2457 ValueLocation* location = child->Location(); 2458 if (!location->IsWritable()) 2459 return; 2460 2461 Value* value = node->GetValue(); 2462 if (value == NULL) 2463 return; 2464 2465 BMessage message(MSG_SHOW_VARIABLE_EDIT_WINDOW); 2466 message.AddPointer("node", node); 2467 message.AddPointer("value", value); 2468 2469 BMessenger(this).SendMessage(&message); 2470 } 2471 2472 2473 void 2474 VariablesView::TreeTableCellMouseDown(TreeTable* table, 2475 const TreeTablePath& path, int32 columnIndex, BPoint screenWhere, 2476 uint32 buttons) 2477 { 2478 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) 2479 return; 2480 2481 if (fFrameClearPending) 2482 return; 2483 2484 _FinishContextMenu(true); 2485 2486 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2487 if (node == NULL) 2488 return; 2489 2490 Settings* settings = NULL; 2491 SettingsMenu* settingsMenu = NULL; 2492 BReference<SettingsMenu> settingsMenuReference; 2493 status_t error = B_OK; 2494 TableCellValueRenderer* cellRenderer = node->TableCellRenderer(); 2495 if (cellRenderer != NULL) { 2496 settings = cellRenderer->GetSettings(); 2497 if (settings != NULL) { 2498 error = node->GetValueHandler() 2499 ->CreateTableCellValueSettingsMenu(node->GetValue(), settings, 2500 settingsMenu); 2501 settingsMenuReference.SetTo(settingsMenu, true); 2502 if (error != B_OK) 2503 return; 2504 } 2505 } 2506 2507 TableCellContextMenuTracker* tracker = new(std::nothrow) 2508 TableCellContextMenuTracker(node, Looper(), this); 2509 BReference<TableCellContextMenuTracker> trackerReference(tracker); 2510 2511 ContextActionList* preActionList; 2512 ContextActionList* postActionList; 2513 2514 error = _GetContextActionsForNode(node, preActionList, postActionList); 2515 if (error != B_OK) 2516 return; 2517 2518 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter( 2519 preActionList); 2520 2521 BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter( 2522 postActionList); 2523 2524 if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList, 2525 postActionList) != B_OK) { 2526 return; 2527 } 2528 2529 fTableCellContextMenuTracker = trackerReference.Detach(); 2530 fTableCellContextMenuTracker->ShowMenu(screenWhere); 2531 } 2532 2533 2534 void 2535 VariablesView::ExpressionEvaluated(ExpressionInfo* info, status_t result, 2536 ExpressionResult* value) 2537 { 2538 BMessage message(MSG_EXPRESSION_EVALUATED); 2539 message.AddPointer("info", info); 2540 message.AddInt32("result", result); 2541 BReference<ExpressionResult> valueReference; 2542 2543 if (value != NULL) { 2544 valueReference.SetTo(value); 2545 message.AddPointer("value", value); 2546 } 2547 2548 if (BMessenger(this).SendMessage(&message) == B_OK) 2549 valueReference.Detach(); 2550 } 2551 2552 2553 void 2554 VariablesView::_Init(ValueNodeManager* manager) 2555 { 2556 fVariableTable = new TreeTable("variable list", 0, B_FANCY_BORDER); 2557 fVariableTable->SetFont(B_FONT_ROW, be_fixed_font); 2558 AddChild(fVariableTable->ToView()); 2559 fVariableTable->SetSortingEnabled(false); 2560 2561 // columns 2562 const float padding = be_control_look->DefaultLabelSpacing(); 2563 fVariableTable->AddColumn(new StringTableColumn(0, "Variable", 2564 be_fixed_font->StringWidth("VariableName") + padding, 40, 1000, 2565 B_TRUNCATE_END, B_ALIGN_LEFT)); 2566 fVariableTable->AddColumn(new VariableValueColumn(1, "Value", 2567 be_fixed_font->StringWidth("0xffffffff00000000") + padding, 40, 1000, 2568 B_TRUNCATE_END, B_ALIGN_RIGHT)); 2569 fVariableTable->AddColumn(new StringTableColumn(2, "Type", 2570 be_fixed_font->StringWidth("std::vector<int32_t>") + padding, 40, 1000, 2571 B_TRUNCATE_END, B_ALIGN_LEFT)); 2572 2573 fVariableTableModel = new VariableTableModel(manager); 2574 if (fVariableTableModel->Init() != B_OK) 2575 throw std::bad_alloc(); 2576 fVariableTable->SetTreeTableModel(fVariableTableModel); 2577 fVariableTable->SetToolTipProvider(fVariableTableModel); 2578 2579 fContainerListener = new ContainerListener(this); 2580 fVariableTableModel->SetContainerListener(fContainerListener); 2581 2582 fVariableTable->AddTreeTableListener(this); 2583 2584 fViewStateHistory = new VariablesViewStateHistory; 2585 if (fViewStateHistory->Init() != B_OK) 2586 throw std::bad_alloc(); 2587 2588 fExpressions = new ExpressionInfoTable(); 2589 if (fExpressions->Init() != B_OK) 2590 throw std::bad_alloc(); 2591 } 2592 2593 2594 void 2595 VariablesView::_RequestNodeValue(ModelNode* node) 2596 { 2597 // get the node child and its container 2598 ValueNodeChild* nodeChild = node->NodeChild(); 2599 ValueNodeContainer* container = nodeChild->Container(); 2600 2601 BReference<ValueNodeContainer> containerReference(container); 2602 AutoLocker<ValueNodeContainer> containerLocker(container); 2603 2604 if (container == NULL || nodeChild->Container() != container) 2605 return; 2606 2607 // get the value node and check whether its value has not yet been resolved 2608 ValueNode* valueNode = nodeChild->Node(); 2609 if (valueNode == NULL) { 2610 ModelNode* parent = node->Parent(); 2611 if (parent != NULL) { 2612 TreeTablePath path; 2613 if (!fVariableTableModel->GetTreePath(parent, path)) 2614 return; 2615 2616 // if the parent node was already expanded when the child was 2617 // added, we may not yet have added a value node. 2618 // Notify the table model that this needs to be done. 2619 if (fVariableTable->IsNodeExpanded(path)) 2620 fVariableTableModel->NodeExpanded(parent); 2621 } 2622 } 2623 2624 if (valueNode == NULL || valueNode->LocationAndValueResolutionState() 2625 != VALUE_NODE_UNRESOLVED) { 2626 return; 2627 } 2628 2629 BReference<ValueNode> valueNodeReference(valueNode); 2630 containerLocker.Unlock(); 2631 2632 // request resolution of the value 2633 fListener->ValueNodeValueRequested(fStackFrame != NULL 2634 ? fStackFrame->GetCpuState() : NULL, container, valueNode); 2635 } 2636 2637 2638 status_t 2639 VariablesView::_GetContextActionsForNode(ModelNode* node, 2640 ContextActionList*& _preActions, ContextActionList*& _postActions) 2641 { 2642 _preActions = NULL; 2643 _postActions = NULL; 2644 2645 ValueLocation* location = node->NodeChild()->Location(); 2646 2647 _preActions = new(std::nothrow) ContextActionList; 2648 if (_preActions == NULL) 2649 return B_NO_MEMORY; 2650 2651 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter( 2652 _preActions); 2653 2654 status_t result = B_OK; 2655 BMessage* message = NULL; 2656 2657 // only show the Inspect option if the value is in fact located 2658 // in memory. 2659 if (location != NULL) { 2660 if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) { 2661 result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW, 2662 _preActions, message); 2663 if (result != B_OK) 2664 return result; 2665 message->AddUInt64("address", location->PieceAt(0).address); 2666 } 2667 2668 ValueNodeChild* child = node->NodeChild(); 2669 ValueNode* valueNode = child->Node(); 2670 2671 result = _AddTypeHandlerMenuIfNeeded(node, _preActions); 2672 if (result != B_OK) 2673 return result; 2674 2675 if (valueNode != NULL) { 2676 Value* value = valueNode->GetValue(); 2677 if (location->IsWritable() && value != NULL) { 2678 result = _AddContextAction("Edit" B_UTF8_ELLIPSIS, 2679 MSG_SHOW_VARIABLE_EDIT_WINDOW, _preActions, message); 2680 if (result != B_OK) 2681 return result; 2682 message->AddPointer("node", node); 2683 message->AddPointer("value", value); 2684 } 2685 AddressType* type = dynamic_cast<AddressType*>(valueNode->GetType()); 2686 if (type != NULL && type->BaseType() != NULL) { 2687 result = _AddContextAction("Cast to array", MSG_TYPECAST_TO_ARRAY, 2688 _preActions, message); 2689 if (result != B_OK) 2690 return result; 2691 message->AddPointer("node", node); 2692 } 2693 } 2694 2695 result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS, 2696 MSG_SHOW_TYPECAST_NODE_PROMPT, _preActions, message); 2697 if (result != B_OK) 2698 return result; 2699 2700 result = _AddContextAction("Watch" B_UTF8_ELLIPSIS, 2701 MSG_SHOW_WATCH_VARIABLE_PROMPT, _preActions, message); 2702 if (result != B_OK) 2703 return result; 2704 2705 if (valueNode == NULL) 2706 return B_OK; 2707 2708 if (valueNode->LocationAndValueResolutionState() == B_OK) { 2709 result = _AddContextAction("Copy Value", B_COPY, _preActions, message); 2710 if (result != B_OK) 2711 return result; 2712 } 2713 2714 bool addRangedContainerItem = false; 2715 // if the current node isn't itself a ranged container, check if it 2716 // contains a hidden node which is, since in the latter case we 2717 // want to present the range selection as well. 2718 if (valueNode->IsRangedContainer()) 2719 addRangedContainerItem = true; 2720 else if (node->CountChildren() == 1 && node->ChildAt(0)->IsHidden()) { 2721 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2722 if (valueNode != NULL && valueNode->IsRangedContainer()) 2723 addRangedContainerItem = true; 2724 } 2725 2726 if (addRangedContainerItem) { 2727 result = _AddContextAction("Set visible range" B_UTF8_ELLIPSIS, 2728 MSG_SHOW_CONTAINER_RANGE_PROMPT, _preActions, message); 2729 if (result != B_OK) 2730 return result; 2731 } 2732 } 2733 2734 _postActions = new(std::nothrow) ContextActionList; 2735 if (_postActions == NULL) 2736 return B_NO_MEMORY; 2737 2738 BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter( 2739 _postActions); 2740 2741 result = _AddContextAction("Add watch expression" B_UTF8_ELLIPSIS, 2742 MSG_ADD_WATCH_EXPRESSION, _postActions, message); 2743 if (result != B_OK) 2744 return result; 2745 2746 if (fExpressionChildren.HasItem(node->NodeChild())) { 2747 result = _AddContextAction("Remove watch expression", 2748 MSG_REMOVE_WATCH_EXPRESSION, _postActions, message); 2749 if (result != B_OK) 2750 return result; 2751 message->AddPointer("node", node); 2752 } 2753 2754 preActionListDeleter.Detach(); 2755 postActionListDeleter.Detach(); 2756 2757 return B_OK; 2758 } 2759 2760 2761 status_t 2762 VariablesView::_AddContextAction(const char* action, uint32 what, 2763 ContextActionList* actions, BMessage*& _message) 2764 { 2765 ActionMenuItem* item = NULL; 2766 status_t result = _CreateContextAction(action, what, item); 2767 if (result != B_OK) 2768 return result; 2769 2770 ObjectDeleter<ActionMenuItem> actionDeleter(item); 2771 if (!actions->AddItem(item)) 2772 return B_NO_MEMORY; 2773 2774 actionDeleter.Detach(); 2775 _message = item->Message(); 2776 2777 return B_OK; 2778 } 2779 2780 2781 status_t 2782 VariablesView::_CreateContextAction(const char* action, uint32 what, 2783 ActionMenuItem*& _item) 2784 { 2785 BMessage* message = new(std::nothrow) BMessage(what); 2786 if (message == NULL) 2787 return B_NO_MEMORY; 2788 2789 ObjectDeleter<BMessage> messageDeleter(message); 2790 2791 _item = new(std::nothrow) ActionMenuItem(action, 2792 message); 2793 if (_item == NULL) 2794 return B_NO_MEMORY; 2795 2796 messageDeleter.Detach(); 2797 2798 return B_OK; 2799 } 2800 2801 2802 status_t 2803 VariablesView::_AddTypeHandlerMenuIfNeeded(ModelNode* node, 2804 ContextActionList* actions) 2805 { 2806 ValueNodeChild* child = node->NodeChild(); 2807 2808 if (node->CountChildren() == 1 && node->ChildAt(0)->IsHidden()) { 2809 node = node->ChildAt(0); 2810 child = node->NodeChild(); 2811 } 2812 2813 int32 handlerCount = TypeHandlerRoster::Default()->CountTypeHandlers( 2814 child->GetType()); 2815 if (handlerCount > 1) { 2816 TypeHandler* lastHandler = node->GetTypeHandler(); 2817 BMenu* handlerMenu = new(std::nothrow) BMenu("Show as"); 2818 if (handlerMenu == NULL) 2819 return B_NO_MEMORY; 2820 2821 ObjectDeleter<BMenu> menuDeleter(handlerMenu); 2822 ActionMenuItem* menuItem = new(std::nothrow) ActionMenuItem( 2823 handlerMenu); 2824 if (menuItem == NULL) 2825 return B_NO_MEMORY; 2826 ObjectDeleter<ActionMenuItem> menuItemDeleter(menuItem); 2827 menuDeleter.Detach(); 2828 2829 ActionMenuItem* item = NULL; 2830 status_t result = _CreateContextAction("Automatic", 2831 MSG_USE_AUTOMATIC_HANDLER, item); 2832 if (item == NULL) 2833 return B_NO_MEMORY; 2834 item->Message()->AddPointer("node", node); 2835 2836 ObjectDeleter<ActionMenuItem> itemDeleter(item); 2837 if (!handlerMenu->AddItem(item) || !handlerMenu->AddSeparatorItem()) 2838 return B_NO_MEMORY; 2839 2840 itemDeleter.Detach(); 2841 if (lastHandler == NULL) 2842 item->SetMarked(true); 2843 2844 TypeHandlerList* handlers = NULL; 2845 result = TypeHandlerRoster::Default()->FindTypeHandlers(child, 2846 child->GetType(), handlers); 2847 if (result != B_OK) 2848 return result; 2849 2850 ObjectDeleter<TypeHandlerList> listDeleter(handlers); 2851 while (handlers->CountItems() > 0) { 2852 TypeHandler* handler = handlers->ItemAt(0); 2853 BMessage* message = new(std::nothrow) BMessage( 2854 MSG_USE_EXPLICIT_HANDLER); 2855 if (message == NULL) { 2856 result = B_NO_MEMORY; 2857 break; 2858 } 2859 message->AddPointer("node", node); 2860 2861 TypeHandlerMenuItem* typeItem 2862 = new(std::nothrow) TypeHandlerMenuItem(handler->Name(), 2863 message); 2864 if (typeItem == NULL) { 2865 result = B_NO_MEMORY; 2866 break; 2867 } 2868 ObjectDeleter<TypeHandlerMenuItem> typeItemDeleter(typeItem); 2869 2870 result = typeItem->SetTypeHandler(handler); 2871 if (result != B_OK) 2872 break; 2873 handlers->RemoveItemAt(0); 2874 if (!handlerMenu->AddItem(typeItem)) { 2875 result = B_NO_MEMORY; 2876 break; 2877 } 2878 2879 typeItemDeleter.Detach(); 2880 if (handler == lastHandler) 2881 typeItem->SetMarked(true); 2882 } 2883 2884 if (result != B_OK) { 2885 for (int32 i = 0; TypeHandler* handler = handlers->ItemAt(i); 2886 i++) { 2887 handler->ReleaseReference(); 2888 } 2889 2890 return result; 2891 } 2892 2893 if (!actions->AddItem(menuItem)) 2894 return B_NO_MEMORY; 2895 2896 handlerMenu->SetTargetForItems(this); 2897 menuItemDeleter.Detach(); 2898 } 2899 2900 return B_OK; 2901 } 2902 2903 2904 void 2905 VariablesView::_FinishContextMenu(bool force) 2906 { 2907 if (fTableCellContextMenuTracker != NULL) { 2908 if (!fTableCellContextMenuTracker->FinishMenu(force) || force) { 2909 fTableCellContextMenuTracker->ReleaseReference(); 2910 fTableCellContextMenuTracker = NULL; 2911 } 2912 } 2913 } 2914 2915 2916 2917 void 2918 VariablesView::_SaveViewState(bool updateValues) const 2919 { 2920 if (fThread == NULL || fStackFrame == NULL 2921 || fStackFrame->Function() == NULL) { 2922 return; 2923 } 2924 2925 // get the function ID 2926 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2927 if (functionID == NULL) 2928 return; 2929 BReference<FunctionID> functionIDReference(functionID, true); 2930 2931 StackFrameValues* values = NULL; 2932 ExpressionValues* expressionValues = NULL; 2933 BReference<StackFrameValues> valuesReference; 2934 BReference<ExpressionValues> expressionsReference; 2935 2936 if (!updateValues) { 2937 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), 2938 functionID); 2939 if (viewState != NULL) { 2940 values = viewState->Values(); 2941 valuesReference.SetTo(values); 2942 2943 expressionValues = viewState->GetExpressionValues(); 2944 expressionsReference.SetTo(expressionValues); 2945 } 2946 } 2947 2948 if (values == NULL) { 2949 values = new(std::nothrow) StackFrameValues; 2950 if (values == NULL) 2951 return; 2952 valuesReference.SetTo(values, true); 2953 2954 if (values->Init() != B_OK) 2955 return; 2956 2957 expressionValues = new(std::nothrow) ExpressionValues; 2958 if (expressionValues == NULL) 2959 return; 2960 expressionsReference.SetTo(expressionValues, true); 2961 2962 if (expressionValues->Init() != B_OK) 2963 return; 2964 } 2965 2966 // create an empty view state 2967 VariablesViewState* viewState = new(std::nothrow) VariablesViewState; 2968 if (viewState == NULL) 2969 return; 2970 BReference<VariablesViewState> viewStateReference(viewState, true); 2971 2972 if (viewState->Init() != B_OK) 2973 return; 2974 2975 viewState->SetValues(values); 2976 viewState->SetExpressionValues(expressionValues); 2977 2978 // populate it 2979 TreeTablePath path; 2980 if (_AddViewStateDescendentNodeInfos(viewState, 2981 fVariableTableModel->Root(), path, updateValues) != B_OK) { 2982 return; 2983 } 2984 2985 // add the view state to the history 2986 fViewStateHistory->SetState(fThread->ID(), functionID, viewState); 2987 } 2988 2989 2990 void 2991 VariablesView::_RestoreViewState() 2992 { 2993 if (fPreviousViewState != NULL) { 2994 fPreviousViewState->ReleaseReference(); 2995 fPreviousViewState = NULL; 2996 } 2997 2998 if (fThread == NULL || fStackFrame == NULL 2999 || fStackFrame->Function() == NULL) { 3000 return; 3001 } 3002 3003 // get the function ID 3004 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 3005 if (functionID == NULL) 3006 return; 3007 BReference<FunctionID> functionIDReference(functionID, true); 3008 3009 // get the previous view state 3010 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), 3011 functionID); 3012 if (viewState == NULL) 3013 return; 3014 3015 // apply the view state 3016 TreeTablePath path; 3017 _ApplyViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), 3018 path); 3019 } 3020 3021 3022 status_t 3023 VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState, 3024 void* parent, TreeTablePath& path, bool updateValues) const 3025 { 3026 bool isRoot = parent == fVariableTableModel->Root(); 3027 int32 childCount = isRoot ? fVariableTableModel->CountChildren(parent) 3028 : ((ModelNode*)parent)->CountChildren(); 3029 for (int32 i = 0; i < childCount; i++) { 3030 ModelNode* node = (ModelNode*)(isRoot ? fVariableTableModel->ChildAt( 3031 parent, i) 3032 : ((ModelNode*)parent)->ChildAt(i)); 3033 3034 if (!path.AddComponent(i)) 3035 return B_NO_MEMORY; 3036 3037 // add the node's info 3038 VariablesViewNodeInfo nodeInfo; 3039 nodeInfo.SetNodeExpanded(fVariableTable->IsNodeExpanded(path)); 3040 nodeInfo.SetCastedType(node->GetCastedType()); 3041 nodeInfo.SetTypeHandler(node->GetTypeHandler()); 3042 TableCellValueRenderer* renderer = node->TableCellRenderer(); 3043 if (renderer != NULL) { 3044 Settings* settings = renderer->GetSettings(); 3045 if (settings != NULL) 3046 nodeInfo.SetRendererSettings(settings->Message()); 3047 } 3048 3049 Value* value = node->GetValue(); 3050 Variable* variable = node->GetVariable(); 3051 TypeComponentPath* componentPath = node->GetPath(); 3052 ObjectID* id = variable->ID(); 3053 3054 status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo); 3055 if (error != B_OK) 3056 return error; 3057 3058 if (value != NULL && updateValues) { 3059 BVariant variableValueData; 3060 if (value->ToVariant(variableValueData)) 3061 error = viewState->Values()->SetValue(id, componentPath, 3062 variableValueData); 3063 if (error != B_OK) 3064 return error; 3065 } 3066 3067 // recurse 3068 error = _AddViewStateDescendentNodeInfos(viewState, node, path, 3069 updateValues); 3070 if (error != B_OK) 3071 return error; 3072 3073 path.RemoveLastComponent(); 3074 } 3075 3076 return B_OK; 3077 } 3078 3079 3080 status_t 3081 VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState, 3082 void* parent, TreeTablePath& path) 3083 { 3084 bool isRoot = parent == fVariableTableModel->Root(); 3085 int32 childCount = isRoot ? fVariableTableModel->CountChildren(parent) 3086 : ((ModelNode*)parent)->CountChildren(); 3087 for (int32 i = 0; i < childCount; i++) { 3088 ModelNode* node = (ModelNode*)(isRoot ? fVariableTableModel->ChildAt( 3089 parent, i) 3090 : ((ModelNode*)parent)->ChildAt(i)); 3091 if (!path.AddComponent(i)) 3092 return B_NO_MEMORY; 3093 3094 // apply the node's info, if any 3095 ObjectID* objectID = node->GetVariable()->ID(); 3096 TypeComponentPath* componentPath = node->GetPath(); 3097 const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( 3098 objectID, componentPath); 3099 if (nodeInfo != NULL) { 3100 // NB: if the node info indicates that the node in question 3101 // was being cast to a different type, this *must* be applied 3102 // before any other view state restoration, since it 3103 // potentially changes the child hierarchy under that node. 3104 Type* type = nodeInfo->GetCastedType(); 3105 TypeHandler* handler = nodeInfo->GetTypeHandler(); 3106 node->SetCastedType(type); 3107 node->SetTypeHandler(handler); 3108 if (type != NULL || handler != NULL) { 3109 if (type == NULL) 3110 type = node->GetType(); 3111 ValueNode* valueNode = NULL; 3112 if (TypeHandlerRoster::Default()->CreateValueNode( 3113 node->NodeChild(), type, handler, valueNode) == B_OK) { 3114 node->NodeChild()->SetNode(valueNode); 3115 } 3116 } 3117 3118 // we don't have a renderer yet so we can't apply the settings 3119 // at this stage. Store them on the model node so we can lazily 3120 // apply them once the value is retrieved. 3121 node->SetLastRendererSettings(nodeInfo->GetRendererSettings()); 3122 3123 fVariableTable->SetNodeExpanded(path, 3124 nodeInfo->IsNodeExpanded()); 3125 3126 BVariant previousValue; 3127 if (viewState->Values()->GetValue(objectID, componentPath, 3128 previousValue)) { 3129 node->SetPreviousValue(previousValue); 3130 } 3131 } 3132 3133 // recurse 3134 status_t error = _ApplyViewStateDescendentNodeInfos(viewState, node, 3135 path); 3136 if (error != B_OK) 3137 return error; 3138 3139 path.RemoveLastComponent(); 3140 } 3141 3142 return B_OK; 3143 } 3144 3145 3146 void 3147 VariablesView::_CopyVariableValueToClipboard() 3148 { 3149 ModelNode* node = reinterpret_cast<ModelNode*>( 3150 fVariableTable->SelectionModel()->NodeAt(0)); 3151 3152 Value* value = node->GetValue(); 3153 BString valueData; 3154 if (value != NULL && value->ToString(valueData)) { 3155 be_clipboard->Lock(); 3156 be_clipboard->Data()->RemoveData("text/plain"); 3157 be_clipboard->Data()->AddData ("text/plain", 3158 B_MIME_TYPE, valueData.String(), 3159 valueData.Length()); 3160 be_clipboard->Commit(); 3161 be_clipboard->Unlock(); 3162 } 3163 } 3164 3165 3166 status_t 3167 VariablesView::_AddExpression(const char* expression, 3168 bool persistentExpression, ExpressionInfo*& _info) 3169 { 3170 ExpressionInfoEntry* entry = NULL; 3171 if (persistentExpression) { 3172 // if our stack frame doesn't have an associated function, 3173 // we can't add an expression 3174 FunctionInstance* function = fStackFrame->Function(); 3175 if (function == NULL) 3176 return B_NOT_ALLOWED; 3177 3178 FunctionID* id = function->GetFunctionID(); 3179 if (id == NULL) 3180 return B_NO_MEMORY; 3181 3182 BReference<FunctionID> idReference(id, true); 3183 3184 entry = fExpressions->Lookup(FunctionKey(id)); 3185 if (entry == NULL) { 3186 entry = new(std::nothrow) ExpressionInfoEntry(id); 3187 if (entry == NULL) 3188 return B_NO_MEMORY; 3189 status_t error = fExpressions->Insert(entry); 3190 if (error != B_OK) { 3191 delete entry; 3192 return error; 3193 } 3194 } 3195 } 3196 3197 ExpressionInfo* info = new(std::nothrow) ExpressionInfo(expression); 3198 3199 if (info == NULL) 3200 return B_NO_MEMORY; 3201 3202 BReference<ExpressionInfo> infoReference(info, true); 3203 3204 if (persistentExpression) { 3205 if (!entry->AddItem(info)) 3206 return B_NO_MEMORY; 3207 } else 3208 fTemporaryExpression = info; 3209 3210 info->AddListener(this); 3211 infoReference.Detach(); 3212 _info = info; 3213 return B_OK; 3214 } 3215 3216 3217 void 3218 VariablesView::_RemoveExpression(ModelNode* node) 3219 { 3220 if (!fExpressionChildren.HasItem(node->NodeChild())) 3221 return; 3222 3223 FunctionID* id = fStackFrame->Function()->GetFunctionID(); 3224 BReference<FunctionID> idReference(id, true); 3225 3226 ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); 3227 if (entry == NULL) 3228 return; 3229 3230 for (int32 i = 0; i < entry->CountItems(); i++) { 3231 ExpressionInfo* info = entry->ItemAt(i); 3232 if (info->Expression() == node->Name()) { 3233 entry->RemoveItemAt(i); 3234 info->RemoveListener(this); 3235 info->ReleaseReference(); 3236 break; 3237 } 3238 } 3239 3240 fVariableTableModel->RemoveSyntheticNode(node); 3241 } 3242 3243 3244 void 3245 VariablesView::_RestoreExpressionNodes() 3246 { 3247 FunctionInstance* instance = fStackFrame->Function(); 3248 if (instance == NULL) 3249 return; 3250 3251 FunctionID* id = instance->GetFunctionID(); 3252 if (id == NULL) 3253 return; 3254 3255 BReference<FunctionID> idReference(id, true); 3256 3257 ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); 3258 if (entry == NULL) 3259 return; 3260 3261 for (int32 i = 0; i < entry->CountItems(); i++) { 3262 ExpressionInfo* info = entry->ItemAt(i); 3263 fListener->ExpressionEvaluationRequested(info, fStackFrame, fThread); 3264 } 3265 } 3266 3267 3268 void 3269 VariablesView::_AddExpressionNode(ExpressionInfo* info, status_t result, 3270 ExpressionResult* value) 3271 { 3272 bool temporaryExpression = (info == fTemporaryExpression); 3273 Variable* variable = NULL; 3274 BReference<Variable> variableReference; 3275 BVariant valueData; 3276 3277 ExpressionVariableID* id 3278 = new(std::nothrow) ExpressionVariableID(info); 3279 if (id == NULL) 3280 return; 3281 BReference<ObjectID> idReference(id, true); 3282 3283 Type* type = NULL; 3284 ValueLocation* location = NULL; 3285 ValueNodeChild* child = NULL; 3286 BReference<Type> typeReference; 3287 BReference<ValueLocation> locationReference; 3288 if (value->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) { 3289 value->PrimitiveValue()->ToVariant(valueData); 3290 if (_GetTypeForTypeCode(valueData.Type(), type) != B_OK) 3291 return; 3292 typeReference.SetTo(type, true); 3293 3294 location = new(std::nothrow) ValueLocation(); 3295 if (location == NULL) 3296 return; 3297 locationReference.SetTo(location, true); 3298 3299 if (valueData.IsNumber()) { 3300 3301 ValuePieceLocation piece; 3302 if (!piece.SetToValue(valueData.Bytes(), valueData.Size()) 3303 || !location->AddPiece(piece)) { 3304 return; 3305 } 3306 } 3307 } else if (value->Kind() == EXPRESSION_RESULT_KIND_VALUE_NODE) { 3308 child = value->ValueNodeValue(); 3309 type = child->GetType(); 3310 typeReference.SetTo(type); 3311 location = child->Location(); 3312 locationReference.SetTo(location); 3313 } 3314 3315 variable = new(std::nothrow) Variable(id, 3316 info->Expression(), type, location); 3317 if (variable == NULL) 3318 return; 3319 variableReference.SetTo(variable, true); 3320 3321 status_t error = fVariableTableModel->AddSyntheticNode(variable, child, 3322 info->Expression()); 3323 if (error != B_OK) 3324 return; 3325 3326 // In the case of either an evaluation error, or an unsupported result 3327 // type, set an explanatory string for the result directly. 3328 if (result != B_OK || valueData.Type() == B_STRING_TYPE) { 3329 StringValue* explicitValue = new(std::nothrow) StringValue( 3330 valueData.ToString()); 3331 if (explicitValue == NULL) 3332 return; 3333 3334 child->Node()->SetLocationAndValue(NULL, explicitValue, B_OK); 3335 } 3336 3337 if (temporaryExpression || fExpressionChildren.AddItem(child)) { 3338 child->AcquireReference(); 3339 3340 if (temporaryExpression) 3341 return; 3342 3343 // attempt to restore our newly added node's view state, 3344 // if applicable. 3345 FunctionID* functionID = fStackFrame->Function() 3346 ->GetFunctionID(); 3347 if (functionID == NULL) 3348 return; 3349 BReference<FunctionID> functionIDReference(functionID, 3350 true); 3351 VariablesViewState* viewState = fViewStateHistory 3352 ->GetState(fThread->ID(), functionID); 3353 if (viewState != NULL) { 3354 TreeTablePath path; 3355 _ApplyViewStateDescendentNodeInfos(viewState, 3356 fVariableTableModel->Root(), path); 3357 } 3358 } 3359 } 3360 3361 3362 void 3363 VariablesView::_HandleTypecastResult(status_t result, ExpressionResult* value) 3364 { 3365 BString errorMessage; 3366 if (value == NULL) { 3367 errorMessage.SetToFormat("Failed to evaluate expression \"%s\": %s (%" 3368 B_PRId32 ")", fPendingTypecastInfo->Expression().String(), 3369 strerror(result), result); 3370 } else if (result != B_OK) { 3371 BVariant valueData; 3372 value->PrimitiveValue()->ToVariant(valueData); 3373 3374 // usually, the evaluation can give us back an error message to 3375 // specifically indicate why it failed. If it did, simply use 3376 // the message directly, otherwise fall back to generating an error 3377 // message based on the error code 3378 if (valueData.Type() == B_STRING_TYPE) 3379 errorMessage = valueData.ToString(); 3380 else { 3381 errorMessage.SetToFormat("Failed to evaluate expression \"%s\":" 3382 " %s (%" B_PRId32 ")", 3383 fPendingTypecastInfo->Expression().String(), strerror(result), 3384 result); 3385 } 3386 3387 } else if (value->Kind() != EXPRESSION_RESULT_KIND_TYPE) { 3388 errorMessage.SetToFormat("Expression \"%s\" does not evaluate to a" 3389 " type.", fPendingTypecastInfo->Expression().String()); 3390 } 3391 3392 if (!errorMessage.IsEmpty()) { 3393 BAlert* alert = new(std::nothrow) BAlert("Typecast error", 3394 errorMessage, "Close"); 3395 if (alert != NULL) 3396 alert->Go(); 3397 3398 return; 3399 } 3400 3401 Type* type = value->GetType(); 3402 BReference<Type> typeRef(type); 3403 ValueNode* valueNode = NULL; 3404 ModelNode* node = fPendingTypecastInfo->TargetNode(); 3405 if (TypeHandlerRoster::Default()->CreateValueNode(node->NodeChild(), type, 3406 NULL, valueNode) != B_OK) { 3407 return; 3408 } 3409 3410 node->NodeChild()->SetNode(valueNode); 3411 node->SetCastedType(type); 3412 fVariableTableModel->NotifyNodeChanged(node); 3413 } 3414 3415 3416 void 3417 VariablesView::_HandleEditVariableRequest(ModelNode* node, Value* value) 3418 { 3419 // get a value handler 3420 ValueHandler* valueHandler; 3421 status_t error = ValueHandlerRoster::Default()->FindValueHandler(value, 3422 valueHandler); 3423 if (error != B_OK) 3424 return; 3425 3426 ValueNode* valueNode = node->NodeChild()->Node(); 3427 3428 BReference<ValueHandler> handlerReference(valueHandler, true); 3429 TableCellValueRenderer* renderer = node->TableCellRenderer(); 3430 TableCellValueEditor* editor = NULL; 3431 error = valueHandler->GetTableCellValueEditor(value, 3432 renderer != NULL ? renderer->GetSettings() : NULL, editor); 3433 if (error != B_OK || editor == NULL) 3434 return; 3435 3436 BReference<TableCellValueEditor> editorReference(editor, true); 3437 3438 try { 3439 fEditWindow = VariableEditWindow::Create(value, valueNode, editor, 3440 this); 3441 } catch (...) { 3442 fEditWindow = NULL; 3443 return; 3444 } 3445 3446 fEditWindow->Show(); 3447 } 3448 3449 3450 status_t 3451 VariablesView::_GetTypeForTypeCode(int32 type, Type*& _resultType) const 3452 { 3453 if (BVariant::TypeIsNumber(type) || type == B_STRING_TYPE) { 3454 _resultType = new(std::nothrow) SyntheticPrimitiveType(type); 3455 if (_resultType == NULL) 3456 return B_NO_MEMORY; 3457 3458 return B_OK; 3459 } 3460 3461 return B_NOT_SUPPORTED; 3462 } 3463 3464 3465 // #pragma mark - Listener 3466 3467 3468 VariablesView::Listener::~Listener() 3469 { 3470 } 3471