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