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