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