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