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(ValueNodeManager* manager); 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 ValueNodeManager* manager) 1111 : 1112 fThread(NULL), 1113 fNodeManager(manager), 1114 fContainerListener(NULL), 1115 fNodeTable() 1116 { 1117 fNodeManager->AcquireReference(); 1118 } 1119 1120 1121 VariablesView::VariableTableModel::~VariableTableModel() 1122 { 1123 fNodeManager->ReleaseReference(); 1124 } 1125 1126 1127 status_t 1128 VariablesView::VariableTableModel::Init() 1129 { 1130 return fNodeTable.Init(); 1131 } 1132 1133 1134 void 1135 VariablesView::VariableTableModel::SetContainerListener( 1136 ContainerListener* listener) 1137 { 1138 if (listener == fContainerListener) 1139 return; 1140 1141 if (fContainerListener != NULL) { 1142 if (fNodeManager != NULL) 1143 fNodeManager->RemoveListener(fContainerListener); 1144 1145 fContainerListener->SetModel(NULL); 1146 } 1147 1148 fContainerListener = listener; 1149 1150 if (fContainerListener != NULL) { 1151 fContainerListener->SetModel(this); 1152 1153 if (fNodeManager != NULL) 1154 fNodeManager->AddListener(fContainerListener); 1155 } 1156 } 1157 1158 1159 void 1160 VariablesView::VariableTableModel::SetStackFrame(Thread* thread, 1161 StackFrame* stackFrame) 1162 { 1163 fThread = thread; 1164 1165 fNodeManager->SetStackFrame(thread, stackFrame); 1166 1167 int32 count = fNodes.CountItems(); 1168 fNodeTable.Clear(true); 1169 1170 if (!fNodes.IsEmpty()) { 1171 for (int32 i = 0; i < count; i++) 1172 fNodes.ItemAt(i)->ReleaseReference(); 1173 fNodes.MakeEmpty(); 1174 } 1175 1176 NotifyNodesRemoved(TreeTablePath(), 0, count); 1177 1178 if (stackFrame == NULL) 1179 return; 1180 1181 ValueNodeContainer* container = fNodeManager->GetContainer(); 1182 AutoLocker<ValueNodeContainer> containerLocker(container); 1183 1184 for (int32 i = 0; i < container->CountChildren(); i++) { 1185 VariableValueNodeChild* child = dynamic_cast<VariableValueNodeChild *>( 1186 container->ChildAt(i)); 1187 _AddNode(child->GetVariable(), NULL, child); 1188 // top level nodes get their children added immediately 1189 // so those won't invoke our callback hook. Add them directly here. 1190 ValueNodeChildrenCreated(child->Node()); 1191 } 1192 } 1193 1194 1195 void 1196 VariablesView::VariableTableModel::ValueNodeChanged(ValueNodeChild* nodeChild, 1197 ValueNode* oldNode, ValueNode* newNode) 1198 { 1199 AutoLocker<ValueNodeContainer> containerLocker( 1200 fNodeManager->GetContainer()); 1201 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1202 if (modelNode == NULL) 1203 return; 1204 1205 if (oldNode != NULL) { 1206 ValueNodeChildrenDeleted(oldNode); 1207 NotifyNodeChanged(modelNode); 1208 } 1209 } 1210 1211 1212 void 1213 VariablesView::VariableTableModel::ValueNodeChildrenCreated( 1214 ValueNode* valueNode) 1215 { 1216 AutoLocker<ValueNodeContainer> containerLocker( 1217 fNodeManager->GetContainer()); 1218 1219 // check whether we know the node 1220 ValueNodeChild* nodeChild = valueNode->NodeChild(); 1221 if (nodeChild == NULL) 1222 return; 1223 1224 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1225 if (modelNode == NULL) 1226 return; 1227 1228 // Iterate through the children and create model nodes for the ones we 1229 // don't know yet. 1230 int32 childCount = valueNode->CountChildren(); 1231 for (int32 i = 0; i < childCount; i++) { 1232 ValueNodeChild* child = valueNode->ChildAt(i); 1233 if (fNodeTable.Lookup(child) == NULL) { 1234 _AddNode(modelNode->GetVariable(), modelNode, child, 1235 child->IsInternal(), childCount == 1); 1236 } 1237 1238 ModelNode* childNode = fNodeTable.Lookup(child); 1239 if (childNode != NULL) 1240 fContainerListener->ModelNodeValueRequested(childNode); 1241 } 1242 1243 if (valueNode->ChildCreationNeedsValue()) 1244 fContainerListener->ModelNodeRestoreViewStateRequested(modelNode); 1245 } 1246 1247 1248 void 1249 VariablesView::VariableTableModel::ValueNodeChildrenDeleted(ValueNode* node) 1250 { 1251 AutoLocker<ValueNodeContainer> containerLocker( 1252 fNodeManager->GetContainer()); 1253 1254 // check whether we know the node 1255 ValueNodeChild* nodeChild = node->NodeChild(); 1256 if (nodeChild == NULL) 1257 return; 1258 1259 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1260 if (modelNode == NULL) 1261 return; 1262 1263 // in the case of an address node with a hidden child, 1264 // we want to send removal notifications for the children 1265 // instead. 1266 BReference<ModelNode> hiddenChild; 1267 if (modelNode->CountChildren() == 1 1268 && modelNode->ChildAt(0)->IsHidden()) { 1269 hiddenChild.SetTo(modelNode->ChildAt(0)); 1270 modelNode->RemoveChild(hiddenChild); 1271 modelNode = hiddenChild; 1272 fNodeTable.Remove(hiddenChild); 1273 } 1274 1275 for (int32 i = modelNode->CountChildren() - 1; i >= 0 ; i--) { 1276 BReference<ModelNode> childNode = modelNode->ChildAt(i); 1277 // recursively remove the current node's child hierarchy. 1278 if (childNode->CountChildren() != 0) 1279 ValueNodeChildrenDeleted(childNode->NodeChild()->Node()); 1280 1281 TreeTablePath treePath; 1282 if (GetTreePath(childNode, treePath)) { 1283 int32 index = treePath.RemoveLastComponent(); 1284 NotifyNodesRemoved(treePath, index, 1); 1285 } 1286 modelNode->RemoveChild(childNode); 1287 fNodeTable.Remove(childNode); 1288 } 1289 } 1290 1291 1292 void 1293 VariablesView::VariableTableModel::ValueNodeValueChanged(ValueNode* valueNode) 1294 { 1295 AutoLocker<ValueNodeContainer> containerLocker( 1296 fNodeManager->GetContainer()); 1297 1298 // check whether we know the node 1299 ValueNodeChild* nodeChild = valueNode->NodeChild(); 1300 if (nodeChild == NULL) 1301 return; 1302 1303 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1304 if (modelNode == NULL) 1305 return; 1306 1307 // check whether the value actually changed 1308 Value* value = valueNode->GetValue(); 1309 if (value == modelNode->GetValue()) 1310 return; 1311 1312 // get a value handler 1313 ValueHandler* valueHandler; 1314 status_t error = ValueHandlerRoster::Default()->FindValueHandler(value, 1315 valueHandler); 1316 if (error != B_OK) 1317 return; 1318 BReference<ValueHandler> handlerReference(valueHandler, true); 1319 1320 // create a table cell renderer for the value 1321 TableCellValueRenderer* renderer = NULL; 1322 error = valueHandler->GetTableCellValueRenderer(value, renderer); 1323 if (error != B_OK) 1324 return; 1325 1326 // set value/handler/renderer 1327 modelNode->SetValue(value); 1328 modelNode->SetValueHandler(valueHandler); 1329 modelNode->SetTableCellRenderer(renderer); 1330 1331 // we have to restore renderer settings here since until this point 1332 // we don't yet know what renderer is in use. 1333 if (renderer != NULL) { 1334 Settings* settings = renderer->GetSettings(); 1335 if (settings != NULL) 1336 settings->RestoreValues(modelNode->GetLastRendererSettings()); 1337 } 1338 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 1807 1808 /*static*/ VariablesView* 1809 VariablesView::Create(Listener* listener, ValueNodeManager* manager) 1810 { 1811 VariablesView* self = new VariablesView(listener); 1812 1813 try { 1814 self->_Init(manager); 1815 } catch (...) { 1816 delete self; 1817 throw; 1818 } 1819 1820 return self; 1821 } 1822 1823 1824 void 1825 VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame) 1826 { 1827 bool updateValues = fFrameClearPending; 1828 // We only want to save previous values if we've continued 1829 // execution (i.e. thread/frame are being cleared). 1830 // Otherwise, we'll overwrite our previous values simply 1831 // by switching frames within the same stack trace, which isn't 1832 // desired behavior. 1833 1834 fFrameClearPending = false; 1835 1836 if (thread == fThread && stackFrame == fStackFrame) 1837 return; 1838 1839 _SaveViewState(updateValues); 1840 1841 _FinishContextMenu(true); 1842 1843 for (int32 i = 0; i < fExpressionChildren.CountItems(); i++) 1844 fExpressionChildren.ItemAt(i)->ReleaseReference(); 1845 fExpressionChildren.MakeEmpty(); 1846 1847 if (fThread != NULL) 1848 fThread->ReleaseReference(); 1849 if (fStackFrame != NULL) 1850 fStackFrame->ReleaseReference(); 1851 1852 fThread = thread; 1853 fStackFrame = stackFrame; 1854 1855 if (fThread != NULL) 1856 fThread->AcquireReference(); 1857 if (fStackFrame != NULL) 1858 fStackFrame->AcquireReference(); 1859 1860 fVariableTableModel->SetStackFrame(fThread, fStackFrame); 1861 1862 // request loading the parameter and variable values 1863 if (fThread != NULL && fStackFrame != NULL) { 1864 AutoLocker<Team> locker(fThread->GetTeam()); 1865 1866 void* root = fVariableTableModel->Root(); 1867 int32 count = fVariableTableModel->CountChildren(root); 1868 for (int32 i = 0; i < count; i++) { 1869 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i); 1870 _RequestNodeValue(node); 1871 } 1872 1873 _RestoreExpressionNodes(); 1874 } 1875 1876 _RestoreViewState(); 1877 } 1878 1879 1880 void 1881 VariablesView::MessageReceived(BMessage* message) 1882 { 1883 switch (message->what) { 1884 case MSG_SHOW_INSPECTOR_WINDOW: 1885 { 1886 // TODO: it'd probably be more ideal to extend the context 1887 // action mechanism to allow one to specify an explicit 1888 // target for each action rather than them all defaulting 1889 // to targetting here. 1890 Looper()->PostMessage(message); 1891 break; 1892 } 1893 case MSG_SHOW_VARIABLE_EDIT_WINDOW: 1894 { 1895 if (fEditWindow != NULL) 1896 fEditWindow->Activate(); 1897 else { 1898 ModelNode* node = NULL; 1899 if (message->FindPointer("node", reinterpret_cast<void**>( 1900 &node)) != B_OK) { 1901 break; 1902 } 1903 1904 Value* value = NULL; 1905 if (message->FindPointer("value", reinterpret_cast<void**>( 1906 &value)) != B_OK) { 1907 break; 1908 } 1909 1910 _HandleEditVariableRequest(node, value); 1911 } 1912 break; 1913 } 1914 case MSG_VARIABLE_EDIT_WINDOW_CLOSED: 1915 { 1916 fEditWindow = NULL; 1917 break; 1918 } 1919 case MSG_WRITE_VARIABLE_VALUE: 1920 { 1921 Value* value = NULL; 1922 if (message->FindPointer("value", reinterpret_cast<void**>( 1923 &value)) != B_OK) { 1924 break; 1925 } 1926 1927 BReference<Value> valueReference(value, true); 1928 1929 ValueNode* node = NULL; 1930 if (message->FindPointer("node", reinterpret_cast<void**>( 1931 &node)) != B_OK) { 1932 break; 1933 } 1934 1935 fListener->ValueNodeWriteRequested(node, 1936 fStackFrame->GetCpuState(), value); 1937 break; 1938 } 1939 case MSG_SHOW_TYPECAST_NODE_PROMPT: 1940 { 1941 BMessage* promptMessage = new(std::nothrow) BMessage( 1942 MSG_TYPECAST_NODE); 1943 1944 if (promptMessage == NULL) 1945 return; 1946 1947 ObjectDeleter<BMessage> messageDeleter(promptMessage); 1948 promptMessage->AddPointer("node", fVariableTable 1949 ->SelectionModel()->NodeAt(0)); 1950 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 1951 "Specify Type", "Type: ", NULL, BMessenger(this), 1952 promptMessage); 1953 if (promptWindow == NULL) 1954 return; 1955 1956 messageDeleter.Detach(); 1957 promptWindow->CenterOnScreen(); 1958 promptWindow->Show(); 1959 break; 1960 } 1961 case MSG_TYPECAST_NODE: 1962 { 1963 ModelNode* node = NULL; 1964 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 1965 != B_OK) { 1966 break; 1967 } 1968 1969 BString typeExpression; 1970 if (message->FindString("text", &typeExpression) == B_OK) { 1971 if (typeExpression.IsEmpty()) 1972 break; 1973 1974 if (fPendingTypecastInfo != NULL) 1975 fPendingTypecastInfo->ReleaseReference(); 1976 1977 fPendingTypecastInfo = new(std::nothrow) 1978 VariablesExpressionInfo(typeExpression, node); 1979 if (fPendingTypecastInfo == NULL) { 1980 // TODO: notify user 1981 break; 1982 } 1983 1984 fPendingTypecastInfo->AddListener(this); 1985 fListener->ExpressionEvaluationRequested(fPendingTypecastInfo, 1986 fStackFrame, fThread); 1987 } 1988 break; 1989 } 1990 case MSG_TYPECAST_TO_ARRAY: 1991 { 1992 ModelNode* node = NULL; 1993 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 1994 != B_OK) { 1995 break; 1996 } 1997 1998 Type* baseType = dynamic_cast<AddressType*>(node->NodeChild() 1999 ->Node()->GetType())->BaseType(); 2000 ArrayType* arrayType = NULL; 2001 if (baseType->CreateDerivedArrayType(0, kMaxArrayElementCount, 2002 false, arrayType) != B_OK) { 2003 break; 2004 } 2005 2006 AddressType* addressType = NULL; 2007 BReference<Type> typeRef(arrayType, true); 2008 if (arrayType->CreateDerivedAddressType(DERIVED_TYPE_POINTER, 2009 addressType) != B_OK) { 2010 break; 2011 } 2012 2013 typeRef.Detach(); 2014 typeRef.SetTo(addressType, true); 2015 ValueNode* valueNode = NULL; 2016 if (TypeHandlerRoster::Default()->CreateValueNode( 2017 node->NodeChild(), addressType, valueNode) != B_OK) { 2018 break; 2019 } 2020 2021 typeRef.Detach(); 2022 node->NodeChild()->SetNode(valueNode); 2023 node->SetCastedType(addressType); 2024 fVariableTableModel->NotifyNodeChanged(node); 2025 break; 2026 } 2027 case MSG_SHOW_CONTAINER_RANGE_PROMPT: 2028 { 2029 ModelNode* node = (ModelNode*)fVariableTable 2030 ->SelectionModel()->NodeAt(0); 2031 int32 lowerBound, upperBound; 2032 ValueNode* valueNode = node->NodeChild()->Node(); 2033 if (!valueNode->IsRangedContainer()) { 2034 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2035 if (!valueNode->IsRangedContainer()) 2036 break; 2037 } 2038 2039 bool fixedRange = valueNode->IsContainerRangeFixed(); 2040 if (valueNode->SupportedChildRange(lowerBound, upperBound) 2041 != B_OK) { 2042 break; 2043 } 2044 2045 BMessage* promptMessage = new(std::nothrow) BMessage( 2046 MSG_SET_CONTAINER_RANGE); 2047 if (promptMessage == NULL) 2048 break; 2049 2050 ObjectDeleter<BMessage> messageDeleter(promptMessage); 2051 promptMessage->AddPointer("node", node); 2052 promptMessage->AddBool("fixedRange", fixedRange); 2053 BString infoText; 2054 if (fixedRange) { 2055 infoText.SetToFormat("Allowed range: %" B_PRId32 2056 "-%" B_PRId32 ".", lowerBound, upperBound); 2057 } else { 2058 infoText.SetToFormat("Current range: %" B_PRId32 2059 "-%" B_PRId32 ".", lowerBound, upperBound); 2060 } 2061 2062 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 2063 "Set Range", "Range: ", infoText.String(), BMessenger(this), 2064 promptMessage); 2065 if (promptWindow == NULL) 2066 return; 2067 2068 messageDeleter.Detach(); 2069 promptWindow->CenterOnScreen(); 2070 promptWindow->Show(); 2071 break; 2072 } 2073 case MSG_SET_CONTAINER_RANGE: 2074 { 2075 ModelNode* node = (ModelNode*)fVariableTable 2076 ->SelectionModel()->NodeAt(0); 2077 int32 lowerBound, upperBound; 2078 ValueNode* valueNode = node->NodeChild()->Node(); 2079 if (!valueNode->IsRangedContainer()) 2080 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2081 if (valueNode->SupportedChildRange(lowerBound, upperBound) != B_OK) 2082 break; 2083 2084 bool fixedRange = message->FindBool("fixedRange"); 2085 2086 BString rangeExpression = message->FindString("text"); 2087 if (rangeExpression.Length() == 0) 2088 break; 2089 2090 RangeList ranges; 2091 status_t result = UiUtils::ParseRangeExpression( 2092 rangeExpression, lowerBound, upperBound, fixedRange, ranges); 2093 if (result != B_OK) 2094 break; 2095 2096 valueNode->ClearChildren(); 2097 for (int32 i = 0; i < ranges.CountRanges(); i++) { 2098 const Range* range = ranges.RangeAt(i); 2099 result = valueNode->CreateChildrenInRange( 2100 fThread->GetTeam()->GetTeamTypeInformation(), 2101 range->lowerBound, range->upperBound); 2102 if (result != B_OK) 2103 break; 2104 } 2105 break; 2106 } 2107 case MSG_SHOW_WATCH_VARIABLE_PROMPT: 2108 { 2109 ModelNode* node = reinterpret_cast<ModelNode*>( 2110 fVariableTable->SelectionModel()->NodeAt(0)); 2111 ValueLocation* location = node->NodeChild()->Location(); 2112 ValuePieceLocation piece = location->PieceAt(0); 2113 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 2114 break; 2115 2116 BMessage looperMessage(*message); 2117 looperMessage.AddUInt64("address", piece.address); 2118 looperMessage.AddInt32("length", piece.size); 2119 looperMessage.AddUInt32("type", B_DATA_READ_WRITE_WATCHPOINT); 2120 Looper()->PostMessage(&looperMessage); 2121 break; 2122 } 2123 case MSG_ADD_WATCH_EXPRESSION: 2124 { 2125 BMessage looperMessage(MSG_SHOW_EXPRESSION_PROMPT_WINDOW); 2126 looperMessage.AddPointer("target", this); 2127 Looper()->PostMessage(&looperMessage); 2128 break; 2129 } 2130 case MSG_REMOVE_WATCH_EXPRESSION: 2131 { 2132 ModelNode* node; 2133 if (message->FindPointer("node", reinterpret_cast<void**>(&node)) 2134 != B_OK) { 2135 break; 2136 } 2137 2138 _RemoveExpression(node); 2139 break; 2140 } 2141 case MSG_ADD_NEW_EXPRESSION: 2142 { 2143 const char* expression; 2144 if (message->FindString("expression", &expression) != B_OK) 2145 break; 2146 2147 bool persistentExpression = message->FindBool("persistent"); 2148 2149 ExpressionInfo* info; 2150 status_t error = _AddExpression(expression, persistentExpression, 2151 info); 2152 if (error != B_OK) { 2153 // TODO: notify user of failure 2154 break; 2155 } 2156 2157 fListener->ExpressionEvaluationRequested(info, fStackFrame, 2158 fThread); 2159 break; 2160 } 2161 case MSG_EXPRESSION_EVALUATED: 2162 { 2163 ExpressionInfo* info; 2164 status_t result; 2165 ExpressionResult* value = NULL; 2166 if (message->FindPointer("info", 2167 reinterpret_cast<void**>(&info)) != B_OK 2168 || message->FindInt32("result", &result) != B_OK) { 2169 break; 2170 } 2171 2172 BReference<ExpressionResult> valueReference; 2173 if (message->FindPointer("value", reinterpret_cast<void**>(&value)) 2174 == B_OK) { 2175 valueReference.SetTo(value, true); 2176 } 2177 2178 VariablesExpressionInfo* variableInfo 2179 = dynamic_cast<VariablesExpressionInfo*>(info); 2180 if (variableInfo != NULL) { 2181 if (fPendingTypecastInfo == variableInfo) { 2182 _HandleTypecastResult(result, value); 2183 fPendingTypecastInfo->ReleaseReference(); 2184 fPendingTypecastInfo = NULL; 2185 } 2186 } else { 2187 _AddExpressionNode(info, result, value); 2188 if (info == fTemporaryExpression) { 2189 info->ReleaseReference(); 2190 fTemporaryExpression = NULL; 2191 } 2192 } 2193 2194 break; 2195 } 2196 case MSG_VALUE_NODE_CHANGED: 2197 { 2198 ValueNodeChild* nodeChild; 2199 ValueNode* oldNode; 2200 ValueNode* newNode; 2201 if (message->FindPointer("nodeChild", (void**)&nodeChild) == B_OK 2202 && message->FindPointer("oldNode", (void**)&oldNode) == B_OK 2203 && message->FindPointer("newNode", (void**)&newNode) == B_OK) { 2204 BReference<ValueNodeChild> nodeChildReference(nodeChild, true); 2205 BReference<ValueNode> oldNodeReference(oldNode, true); 2206 BReference<ValueNode> newNodeReference(newNode, true); 2207 2208 fVariableTableModel->ValueNodeChanged(nodeChild, oldNode, 2209 newNode); 2210 } 2211 2212 break; 2213 } 2214 case MSG_VALUE_NODE_CHILDREN_CREATED: 2215 { 2216 ValueNode* node; 2217 if (message->FindPointer("node", (void**)&node) == B_OK) { 2218 BReference<ValueNode> newNodeReference(node, true); 2219 fVariableTableModel->ValueNodeChildrenCreated(node); 2220 } 2221 2222 break; 2223 } 2224 case MSG_VALUE_NODE_CHILDREN_DELETED: 2225 { 2226 ValueNode* node; 2227 if (message->FindPointer("node", (void**)&node) == B_OK) { 2228 BReference<ValueNode> newNodeReference(node, true); 2229 fVariableTableModel->ValueNodeChildrenDeleted(node); 2230 } 2231 2232 break; 2233 } 2234 case MSG_VALUE_NODE_VALUE_CHANGED: 2235 { 2236 ValueNode* node; 2237 if (message->FindPointer("node", (void**)&node) == B_OK) { 2238 BReference<ValueNode> newNodeReference(node, true); 2239 fVariableTableModel->ValueNodeValueChanged(node); 2240 } 2241 2242 break; 2243 } 2244 case MSG_RESTORE_PARTIAL_VIEW_STATE: 2245 { 2246 ModelNode* node; 2247 if (message->FindPointer("node", (void**)&node) == B_OK) { 2248 TreeTablePath path; 2249 if (fVariableTableModel->GetTreePath(node, path)) { 2250 FunctionID* functionID = fStackFrame->Function() 2251 ->GetFunctionID(); 2252 if (functionID == NULL) 2253 return; 2254 BReference<FunctionID> functionIDReference(functionID, 2255 true); 2256 VariablesViewState* viewState = fViewStateHistory 2257 ->GetState(fThread->ID(), functionID); 2258 if (viewState != NULL) { 2259 _ApplyViewStateDescendentNodeInfos(viewState, node, 2260 path); 2261 } 2262 } 2263 } 2264 break; 2265 } 2266 case MSG_VALUE_NODE_NEEDS_VALUE: 2267 case MSG_MODEL_NODE_HIDDEN: 2268 { 2269 ModelNode* node; 2270 if (message->FindPointer("node", (void**)&node) == B_OK) { 2271 BReference<ModelNode> modelNodeReference(node, true); 2272 _RequestNodeValue(node); 2273 } 2274 2275 break; 2276 } 2277 case MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE: 2278 { 2279 _FinishContextMenu(false); 2280 break; 2281 } 2282 case MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED: 2283 { 2284 ModelNode* node; 2285 if (message->FindPointer("node", (void**)&node) != B_OK) 2286 break; 2287 BReference<ModelNode> nodeReference(node, true); 2288 2289 fVariableTableModel->NotifyNodeChanged(node); 2290 break; 2291 } 2292 case B_COPY: 2293 { 2294 _CopyVariableValueToClipboard(); 2295 break; 2296 } 2297 default: 2298 BGroupView::MessageReceived(message); 2299 break; 2300 } 2301 } 2302 2303 2304 void 2305 VariablesView::DetachedFromWindow() 2306 { 2307 _FinishContextMenu(true); 2308 } 2309 2310 2311 void 2312 VariablesView::LoadSettings(const BMessage& settings) 2313 { 2314 BMessage tableSettings; 2315 if (settings.FindMessage("variableTable", &tableSettings) == B_OK) { 2316 GuiSettingsUtils::UnarchiveTableSettings(tableSettings, 2317 fVariableTable); 2318 } 2319 } 2320 2321 2322 status_t 2323 VariablesView::SaveSettings(BMessage& settings) 2324 { 2325 settings.MakeEmpty(); 2326 2327 BMessage tableSettings; 2328 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings, 2329 fVariableTable); 2330 if (result == B_OK) 2331 result = settings.AddMessage("variableTable", &tableSettings); 2332 2333 return result; 2334 } 2335 2336 2337 void 2338 VariablesView::SetStackFrameClearPending() 2339 { 2340 fFrameClearPending = true; 2341 } 2342 2343 2344 void 2345 VariablesView::TreeTableNodeExpandedChanged(TreeTable* table, 2346 const TreeTablePath& path, bool expanded) 2347 { 2348 if (fFrameClearPending) 2349 return; 2350 2351 if (expanded) { 2352 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2353 if (node == NULL) 2354 return; 2355 2356 fVariableTableModel->NodeExpanded(node); 2357 2358 // request the values of all children that don't have any yet 2359 2360 // If the node only has a hidden child, directly load the child's 2361 // children's values. 2362 if (node->CountChildren() == 1) { 2363 ModelNode* child = node->ChildAt(0); 2364 if (child->IsHidden()) 2365 node = child; 2366 } 2367 2368 // request the values 2369 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) { 2370 if (child->IsPresentationNode()) 2371 continue; 2372 2373 _RequestNodeValue(child); 2374 } 2375 } 2376 } 2377 2378 2379 void 2380 VariablesView::TreeTableNodeInvoked(TreeTable* table, 2381 const TreeTablePath& path) 2382 { 2383 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2384 if (node == NULL) 2385 return; 2386 2387 ValueNodeChild* child = node->NodeChild(); 2388 2389 if (child->LocationResolutionState() != B_OK) 2390 return; 2391 2392 ValueLocation* location = child->Location(); 2393 if (!location->IsWritable()) 2394 return; 2395 2396 Value* value = node->GetValue(); 2397 if (value == NULL) 2398 return; 2399 2400 BMessage message(MSG_SHOW_VARIABLE_EDIT_WINDOW); 2401 message.AddPointer("node", node); 2402 message.AddPointer("value", value); 2403 2404 BMessenger(this).SendMessage(&message); 2405 } 2406 2407 2408 void 2409 VariablesView::TreeTableCellMouseDown(TreeTable* table, 2410 const TreeTablePath& path, int32 columnIndex, BPoint screenWhere, 2411 uint32 buttons) 2412 { 2413 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) 2414 return; 2415 2416 if (fFrameClearPending) 2417 return; 2418 2419 _FinishContextMenu(true); 2420 2421 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2422 if (node == NULL) 2423 return; 2424 2425 Settings* settings = NULL; 2426 SettingsMenu* settingsMenu = NULL; 2427 BReference<SettingsMenu> settingsMenuReference; 2428 status_t error = B_OK; 2429 TableCellValueRenderer* cellRenderer = node->TableCellRenderer(); 2430 if (cellRenderer != NULL) { 2431 settings = cellRenderer->GetSettings(); 2432 if (settings != NULL) { 2433 error = node->GetValueHandler() 2434 ->CreateTableCellValueSettingsMenu(node->GetValue(), settings, 2435 settingsMenu); 2436 settingsMenuReference.SetTo(settingsMenu, true); 2437 if (error != B_OK) 2438 return; 2439 } 2440 } 2441 2442 TableCellContextMenuTracker* tracker = new(std::nothrow) 2443 TableCellContextMenuTracker(node, Looper(), this); 2444 BReference<TableCellContextMenuTracker> trackerReference(tracker); 2445 2446 ContextActionList* preActionList; 2447 ContextActionList* postActionList; 2448 2449 error = _GetContextActionsForNode(node, preActionList, postActionList); 2450 if (error != B_OK) 2451 return; 2452 2453 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter( 2454 preActionList); 2455 2456 BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter( 2457 postActionList); 2458 2459 if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList, 2460 postActionList) != B_OK) { 2461 return; 2462 } 2463 2464 fTableCellContextMenuTracker = trackerReference.Detach(); 2465 fTableCellContextMenuTracker->ShowMenu(screenWhere); 2466 } 2467 2468 2469 void 2470 VariablesView::ExpressionEvaluated(ExpressionInfo* info, status_t result, 2471 ExpressionResult* value) 2472 { 2473 BMessage message(MSG_EXPRESSION_EVALUATED); 2474 message.AddPointer("info", info); 2475 message.AddInt32("result", result); 2476 BReference<ExpressionResult> valueReference; 2477 2478 if (value != NULL) { 2479 valueReference.SetTo(value); 2480 message.AddPointer("value", value); 2481 } 2482 2483 if (BMessenger(this).SendMessage(&message) == B_OK) 2484 valueReference.Detach(); 2485 } 2486 2487 2488 void 2489 VariablesView::_Init(ValueNodeManager* manager) 2490 { 2491 fVariableTable = new TreeTable("variable list", 0, B_FANCY_BORDER); 2492 AddChild(fVariableTable->ToView()); 2493 fVariableTable->SetSortingEnabled(false); 2494 2495 // columns 2496 fVariableTable->AddColumn(new StringTableColumn(0, "Variable", 80, 40, 1000, 2497 B_TRUNCATE_END, B_ALIGN_LEFT)); 2498 fVariableTable->AddColumn(new VariableValueColumn(1, "Value", 80, 40, 1000, 2499 B_TRUNCATE_END, B_ALIGN_RIGHT)); 2500 fVariableTable->AddColumn(new StringTableColumn(2, "Type", 80, 40, 1000, 2501 B_TRUNCATE_END, B_ALIGN_LEFT)); 2502 2503 fVariableTableModel = new VariableTableModel(manager); 2504 if (fVariableTableModel->Init() != B_OK) 2505 throw std::bad_alloc(); 2506 fVariableTable->SetTreeTableModel(fVariableTableModel); 2507 fVariableTable->SetToolTipProvider(fVariableTableModel); 2508 2509 fContainerListener = new ContainerListener(this); 2510 fVariableTableModel->SetContainerListener(fContainerListener); 2511 2512 fVariableTable->AddTreeTableListener(this); 2513 2514 fViewStateHistory = new VariablesViewStateHistory; 2515 if (fViewStateHistory->Init() != B_OK) 2516 throw std::bad_alloc(); 2517 2518 fExpressions = new ExpressionInfoTable(); 2519 if (fExpressions->Init() != B_OK) 2520 throw std::bad_alloc(); 2521 } 2522 2523 2524 void 2525 VariablesView::_RequestNodeValue(ModelNode* node) 2526 { 2527 // get the node child and its container 2528 ValueNodeChild* nodeChild = node->NodeChild(); 2529 ValueNodeContainer* container = nodeChild->Container(); 2530 2531 BReference<ValueNodeContainer> containerReference(container); 2532 AutoLocker<ValueNodeContainer> containerLocker(container); 2533 2534 if (container == NULL || nodeChild->Container() != container) 2535 return; 2536 2537 // get the value node and check whether its value has not yet been resolved 2538 ValueNode* valueNode = nodeChild->Node(); 2539 if (valueNode == NULL) { 2540 ModelNode* parent = node->Parent(); 2541 if (parent != NULL) { 2542 TreeTablePath path; 2543 if (!fVariableTableModel->GetTreePath(parent, path)) 2544 return; 2545 2546 // if the parent node was already expanded when the child was 2547 // added, we may not yet have added a value node. 2548 // Notify the table model that this needs to be done. 2549 if (fVariableTable->IsNodeExpanded(path)) 2550 fVariableTableModel->NodeExpanded(parent); 2551 } 2552 } 2553 2554 if (valueNode == NULL || valueNode->LocationAndValueResolutionState() 2555 != VALUE_NODE_UNRESOLVED) { 2556 return; 2557 } 2558 2559 BReference<ValueNode> valueNodeReference(valueNode); 2560 containerLocker.Unlock(); 2561 2562 // request resolution of the value 2563 fListener->ValueNodeValueRequested(fStackFrame != NULL 2564 ? fStackFrame->GetCpuState() : NULL, container, valueNode); 2565 } 2566 2567 2568 status_t 2569 VariablesView::_GetContextActionsForNode(ModelNode* node, 2570 ContextActionList*& _preActions, ContextActionList*& _postActions) 2571 { 2572 _preActions = NULL; 2573 _postActions = NULL; 2574 2575 ValueLocation* location = node->NodeChild()->Location(); 2576 2577 _preActions = new(std::nothrow) ContextActionList; 2578 if (_preActions == NULL) 2579 return B_NO_MEMORY; 2580 2581 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter( 2582 _preActions); 2583 2584 status_t result = B_OK; 2585 BMessage* message = NULL; 2586 2587 // only show the Inspect option if the value is in fact located 2588 // in memory. 2589 if (location != NULL) { 2590 if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) { 2591 result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW, 2592 _preActions, message); 2593 if (result != B_OK) 2594 return result; 2595 message->AddUInt64("address", location->PieceAt(0).address); 2596 } 2597 2598 ValueNode* valueNode = node->NodeChild()->Node(); 2599 2600 if (valueNode != NULL) { 2601 Value* value = valueNode->GetValue(); 2602 if (location->IsWritable() && value != NULL) { 2603 result = _AddContextAction("Edit" B_UTF8_ELLIPSIS, 2604 MSG_SHOW_VARIABLE_EDIT_WINDOW, _preActions, message); 2605 if (result != B_OK) 2606 return result; 2607 message->AddPointer("node", node); 2608 message->AddPointer("value", value); 2609 } 2610 AddressType* type = dynamic_cast<AddressType*>(valueNode->GetType()); 2611 if (type != NULL && type->BaseType() != NULL) { 2612 result = _AddContextAction("Cast to array", MSG_TYPECAST_TO_ARRAY, 2613 _preActions, message); 2614 if (result != B_OK) 2615 return result; 2616 message->AddPointer("node", node); 2617 } 2618 } 2619 2620 result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS, 2621 MSG_SHOW_TYPECAST_NODE_PROMPT, _preActions, message); 2622 if (result != B_OK) 2623 return result; 2624 2625 result = _AddContextAction("Watch" B_UTF8_ELLIPSIS, 2626 MSG_SHOW_WATCH_VARIABLE_PROMPT, _preActions, message); 2627 if (result != B_OK) 2628 return result; 2629 2630 if (valueNode == NULL) 2631 return B_OK; 2632 2633 if (valueNode->LocationAndValueResolutionState() == B_OK) { 2634 result = _AddContextAction("Copy Value", B_COPY, _preActions, message); 2635 if (result != B_OK) 2636 return result; 2637 } 2638 2639 bool addRangedContainerItem = false; 2640 // if the current node isn't itself a ranged container, check if it 2641 // contains a hidden node which is, since in the latter case we 2642 // want to present the range selection as well. 2643 if (valueNode->IsRangedContainer()) 2644 addRangedContainerItem = true; 2645 else if (node->CountChildren() == 1 && node->ChildAt(0)->IsHidden()) { 2646 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2647 if (valueNode != NULL && valueNode->IsRangedContainer()) 2648 addRangedContainerItem = true; 2649 } 2650 2651 if (addRangedContainerItem) { 2652 result = _AddContextAction("Set visible range" B_UTF8_ELLIPSIS, 2653 MSG_SHOW_CONTAINER_RANGE_PROMPT, _preActions, message); 2654 if (result != B_OK) 2655 return result; 2656 } 2657 } 2658 2659 _postActions = new(std::nothrow) ContextActionList; 2660 if (_postActions == NULL) 2661 return B_NO_MEMORY; 2662 2663 BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter( 2664 _postActions); 2665 2666 result = _AddContextAction("Add watch expression" B_UTF8_ELLIPSIS, 2667 MSG_ADD_WATCH_EXPRESSION, _postActions, message); 2668 if (result != B_OK) 2669 return result; 2670 2671 if (fExpressionChildren.HasItem(node->NodeChild())) { 2672 result = _AddContextAction("Remove watch expression", 2673 MSG_REMOVE_WATCH_EXPRESSION, _postActions, message); 2674 if (result != B_OK) 2675 return result; 2676 message->AddPointer("node", node); 2677 } 2678 2679 preActionListDeleter.Detach(); 2680 postActionListDeleter.Detach(); 2681 2682 return B_OK; 2683 } 2684 2685 2686 status_t 2687 VariablesView::_AddContextAction(const char* action, uint32 what, 2688 ContextActionList* actions, BMessage*& _message) 2689 { 2690 _message = new(std::nothrow) BMessage(what); 2691 if (_message == NULL) 2692 return B_NO_MEMORY; 2693 2694 ObjectDeleter<BMessage> messageDeleter(_message); 2695 2696 ActionMenuItem* item = new(std::nothrow) ActionMenuItem(action, 2697 _message); 2698 if (item == NULL) 2699 return B_NO_MEMORY; 2700 2701 messageDeleter.Detach(); 2702 ObjectDeleter<ActionMenuItem> actionDeleter(item); 2703 if (!actions->AddItem(item)) 2704 return B_NO_MEMORY; 2705 2706 actionDeleter.Detach(); 2707 2708 return B_OK; 2709 } 2710 2711 2712 void 2713 VariablesView::_FinishContextMenu(bool force) 2714 { 2715 if (fTableCellContextMenuTracker != NULL) { 2716 if (!fTableCellContextMenuTracker->FinishMenu(force) || force) { 2717 fTableCellContextMenuTracker->ReleaseReference(); 2718 fTableCellContextMenuTracker = NULL; 2719 } 2720 } 2721 } 2722 2723 2724 2725 void 2726 VariablesView::_SaveViewState(bool updateValues) const 2727 { 2728 if (fThread == NULL || fStackFrame == NULL 2729 || fStackFrame->Function() == NULL) { 2730 return; 2731 } 2732 2733 // get the function ID 2734 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2735 if (functionID == NULL) 2736 return; 2737 BReference<FunctionID> functionIDReference(functionID, true); 2738 2739 StackFrameValues* values = NULL; 2740 ExpressionValues* expressionValues = NULL; 2741 BReference<StackFrameValues> valuesReference; 2742 BReference<ExpressionValues> expressionsReference; 2743 2744 if (!updateValues) { 2745 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), 2746 functionID); 2747 if (viewState != NULL) { 2748 values = viewState->Values(); 2749 valuesReference.SetTo(values); 2750 2751 expressionValues = viewState->GetExpressionValues(); 2752 expressionsReference.SetTo(expressionValues); 2753 } 2754 } 2755 2756 if (values == NULL) { 2757 values = new(std::nothrow) StackFrameValues; 2758 if (values == NULL) 2759 return; 2760 valuesReference.SetTo(values, true); 2761 2762 if (values->Init() != B_OK) 2763 return; 2764 2765 expressionValues = new(std::nothrow) ExpressionValues; 2766 if (expressionValues == NULL) 2767 return; 2768 expressionsReference.SetTo(expressionValues, true); 2769 2770 if (expressionValues->Init() != B_OK) 2771 return; 2772 } 2773 2774 // create an empty view state 2775 VariablesViewState* viewState = new(std::nothrow) VariablesViewState; 2776 if (viewState == NULL) 2777 return; 2778 BReference<VariablesViewState> viewStateReference(viewState, true); 2779 2780 if (viewState->Init() != B_OK) 2781 return; 2782 2783 viewState->SetValues(values); 2784 viewState->SetExpressionValues(expressionValues); 2785 2786 // populate it 2787 TreeTablePath path; 2788 if (_AddViewStateDescendentNodeInfos(viewState, 2789 fVariableTableModel->Root(), path, updateValues) != B_OK) { 2790 return; 2791 } 2792 2793 // add the view state to the history 2794 fViewStateHistory->SetState(fThread->ID(), functionID, viewState); 2795 } 2796 2797 2798 void 2799 VariablesView::_RestoreViewState() 2800 { 2801 if (fPreviousViewState != NULL) { 2802 fPreviousViewState->ReleaseReference(); 2803 fPreviousViewState = NULL; 2804 } 2805 2806 if (fThread == NULL || fStackFrame == NULL 2807 || fStackFrame->Function() == NULL) { 2808 return; 2809 } 2810 2811 // get the function ID 2812 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2813 if (functionID == NULL) 2814 return; 2815 BReference<FunctionID> functionIDReference(functionID, true); 2816 2817 // get the previous view state 2818 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), 2819 functionID); 2820 if (viewState == NULL) 2821 return; 2822 2823 // apply the view state 2824 TreeTablePath path; 2825 _ApplyViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), 2826 path); 2827 } 2828 2829 2830 status_t 2831 VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState, 2832 void* parent, TreeTablePath& path, bool updateValues) const 2833 { 2834 int32 childCount = fVariableTableModel->CountChildren(parent); 2835 for (int32 i = 0; i < childCount; i++) { 2836 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i); 2837 if (!path.AddComponent(i)) 2838 return B_NO_MEMORY; 2839 2840 // add the node's info 2841 VariablesViewNodeInfo nodeInfo; 2842 nodeInfo.SetNodeExpanded(fVariableTable->IsNodeExpanded(path)); 2843 nodeInfo.SetCastedType(node->GetCastedType()); 2844 TableCellValueRenderer* renderer = node->TableCellRenderer(); 2845 if (renderer != NULL) { 2846 Settings* settings = renderer->GetSettings(); 2847 if (settings != NULL) 2848 nodeInfo.SetRendererSettings(settings->Message()); 2849 } 2850 2851 Value* value = node->GetValue(); 2852 Variable* variable = node->GetVariable(); 2853 TypeComponentPath* componentPath = node->GetPath(); 2854 ObjectID* id = variable->ID(); 2855 2856 status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo); 2857 if (error != B_OK) 2858 return error; 2859 2860 if (value != NULL && updateValues) { 2861 BVariant variableValueData; 2862 if (value->ToVariant(variableValueData)) 2863 error = viewState->Values()->SetValue(id, componentPath, 2864 variableValueData); 2865 if (error != B_OK) 2866 return error; 2867 } 2868 2869 // recurse 2870 error = _AddViewStateDescendentNodeInfos(viewState, node, path, 2871 updateValues); 2872 if (error != B_OK) 2873 return error; 2874 2875 path.RemoveLastComponent(); 2876 } 2877 2878 return B_OK; 2879 } 2880 2881 2882 status_t 2883 VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState, 2884 void* parent, TreeTablePath& path) 2885 { 2886 int32 childCount = fVariableTableModel->CountChildren(parent); 2887 for (int32 i = 0; i < childCount; i++) { 2888 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i); 2889 if (!path.AddComponent(i)) 2890 return B_NO_MEMORY; 2891 2892 // apply the node's info, if any 2893 ObjectID* objectID = node->GetVariable()->ID(); 2894 TypeComponentPath* componentPath = node->GetPath(); 2895 const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( 2896 objectID, componentPath); 2897 if (nodeInfo != NULL) { 2898 // NB: if the node info indicates that the node in question 2899 // was being cast to a different type, this *must* be applied 2900 // before any other view state restoration, since it 2901 // potentially changes the child hierarchy under that node. 2902 Type* type = nodeInfo->GetCastedType(); 2903 if (type != NULL) { 2904 ValueNode* valueNode = NULL; 2905 if (TypeHandlerRoster::Default()->CreateValueNode( 2906 node->NodeChild(), type, valueNode) == B_OK) { 2907 node->NodeChild()->SetNode(valueNode); 2908 node->SetCastedType(type); 2909 } 2910 } 2911 2912 // we don't have a renderer yet so we can't apply the settings 2913 // at this stage. Store them on the model node so we can lazily 2914 // apply them once the value is retrieved. 2915 node->SetLastRendererSettings(nodeInfo->GetRendererSettings()); 2916 2917 fVariableTable->SetNodeExpanded(path, 2918 nodeInfo->IsNodeExpanded()); 2919 2920 BVariant previousValue; 2921 if (viewState->Values()->GetValue(objectID, componentPath, 2922 previousValue)) { 2923 node->SetPreviousValue(previousValue); 2924 } 2925 } 2926 2927 // recurse 2928 status_t error = _ApplyViewStateDescendentNodeInfos(viewState, node, 2929 path); 2930 if (error != B_OK) 2931 return error; 2932 2933 path.RemoveLastComponent(); 2934 } 2935 2936 return B_OK; 2937 } 2938 2939 2940 void 2941 VariablesView::_CopyVariableValueToClipboard() 2942 { 2943 ModelNode* node = reinterpret_cast<ModelNode*>( 2944 fVariableTable->SelectionModel()->NodeAt(0)); 2945 2946 Value* value = node->GetValue(); 2947 BString valueData; 2948 if (value != NULL && value->ToString(valueData)) { 2949 be_clipboard->Lock(); 2950 be_clipboard->Data()->RemoveData("text/plain"); 2951 be_clipboard->Data()->AddData ("text/plain", 2952 B_MIME_TYPE, valueData.String(), 2953 valueData.Length()); 2954 be_clipboard->Commit(); 2955 be_clipboard->Unlock(); 2956 } 2957 } 2958 2959 2960 status_t 2961 VariablesView::_AddExpression(const char* expression, 2962 bool persistentExpression, ExpressionInfo*& _info) 2963 { 2964 ExpressionInfoEntry* entry = NULL; 2965 if (persistentExpression) { 2966 // if our stack frame doesn't have an associated function, 2967 // we can't add an expression 2968 FunctionInstance* function = fStackFrame->Function(); 2969 if (function == NULL) 2970 return B_NOT_ALLOWED; 2971 2972 FunctionID* id = function->GetFunctionID(); 2973 if (id == NULL) 2974 return B_NO_MEMORY; 2975 2976 BReference<FunctionID> idReference(id, true); 2977 2978 entry = fExpressions->Lookup(FunctionKey(id)); 2979 if (entry == NULL) { 2980 entry = new(std::nothrow) ExpressionInfoEntry(id); 2981 if (entry == NULL) 2982 return B_NO_MEMORY; 2983 status_t error = fExpressions->Insert(entry); 2984 if (error != B_OK) { 2985 delete entry; 2986 return error; 2987 } 2988 } 2989 } 2990 2991 ExpressionInfo* info = new(std::nothrow) ExpressionInfo(expression); 2992 2993 if (info == NULL) 2994 return B_NO_MEMORY; 2995 2996 BReference<ExpressionInfo> infoReference(info, true); 2997 2998 if (persistentExpression) { 2999 if (!entry->AddItem(info)) 3000 return B_NO_MEMORY; 3001 } else 3002 fTemporaryExpression = info; 3003 3004 info->AddListener(this); 3005 infoReference.Detach(); 3006 _info = info; 3007 return B_OK; 3008 } 3009 3010 3011 void 3012 VariablesView::_RemoveExpression(ModelNode* node) 3013 { 3014 if (!fExpressionChildren.HasItem(node->NodeChild())) 3015 return; 3016 3017 FunctionID* id = fStackFrame->Function()->GetFunctionID(); 3018 BReference<FunctionID> idReference(id, true); 3019 3020 ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); 3021 if (entry == NULL) 3022 return; 3023 3024 for (int32 i = 0; i < entry->CountItems(); i++) { 3025 ExpressionInfo* info = entry->ItemAt(i); 3026 if (info->Expression() == node->Name()) { 3027 entry->RemoveItemAt(i); 3028 info->RemoveListener(this); 3029 info->ReleaseReference(); 3030 break; 3031 } 3032 } 3033 3034 fVariableTableModel->RemoveSyntheticNode(node); 3035 } 3036 3037 3038 void 3039 VariablesView::_RestoreExpressionNodes() 3040 { 3041 FunctionInstance* instance = fStackFrame->Function(); 3042 if (instance == NULL) 3043 return; 3044 3045 FunctionID* id = instance->GetFunctionID(); 3046 if (id == NULL) 3047 return; 3048 3049 BReference<FunctionID> idReference(id, true); 3050 3051 ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); 3052 if (entry == NULL) 3053 return; 3054 3055 for (int32 i = 0; i < entry->CountItems(); i++) { 3056 ExpressionInfo* info = entry->ItemAt(i); 3057 fListener->ExpressionEvaluationRequested(info, fStackFrame, fThread); 3058 } 3059 } 3060 3061 3062 void 3063 VariablesView::_AddExpressionNode(ExpressionInfo* info, status_t result, 3064 ExpressionResult* value) 3065 { 3066 bool temporaryExpression = (info == fTemporaryExpression); 3067 Variable* variable = NULL; 3068 BReference<Variable> variableReference; 3069 BVariant valueData; 3070 3071 ExpressionVariableID* id 3072 = new(std::nothrow) ExpressionVariableID(info); 3073 if (id == NULL) 3074 return; 3075 BReference<ObjectID> idReference(id, true); 3076 3077 Type* type = NULL; 3078 ValueLocation* location = NULL; 3079 ValueNodeChild* child = NULL; 3080 BReference<Type> typeReference; 3081 BReference<ValueLocation> locationReference; 3082 if (value->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) { 3083 value->PrimitiveValue()->ToVariant(valueData); 3084 if (_GetTypeForTypeCode(valueData.Type(), type) != B_OK) 3085 return; 3086 typeReference.SetTo(type, true); 3087 3088 location = new(std::nothrow) ValueLocation(); 3089 if (location == NULL) 3090 return; 3091 locationReference.SetTo(location, true); 3092 3093 if (valueData.IsNumber()) { 3094 3095 ValuePieceLocation piece; 3096 if (!piece.SetToValue(valueData.Bytes(), valueData.Size()) 3097 || !location->AddPiece(piece)) { 3098 return; 3099 } 3100 } 3101 } else if (value->Kind() == EXPRESSION_RESULT_KIND_VALUE_NODE) { 3102 child = value->ValueNodeValue(); 3103 type = child->GetType(); 3104 typeReference.SetTo(type); 3105 location = child->Location(); 3106 locationReference.SetTo(location); 3107 } 3108 3109 variable = new(std::nothrow) Variable(id, 3110 info->Expression(), type, location); 3111 if (variable == NULL) 3112 return; 3113 variableReference.SetTo(variable, true); 3114 3115 status_t error = fVariableTableModel->AddSyntheticNode(variable, child, 3116 info->Expression()); 3117 if (error != B_OK) 3118 return; 3119 3120 // In the case of either an evaluation error, or an unsupported result 3121 // type, set an explanatory string for the result directly. 3122 if (result != B_OK || valueData.Type() == B_STRING_TYPE) { 3123 StringValue* explicitValue = new(std::nothrow) StringValue( 3124 valueData.ToString()); 3125 if (explicitValue == NULL) 3126 return; 3127 3128 child->Node()->SetLocationAndValue(NULL, explicitValue, B_OK); 3129 } 3130 3131 if (temporaryExpression || fExpressionChildren.AddItem(child)) { 3132 child->AcquireReference(); 3133 3134 if (temporaryExpression) 3135 return; 3136 3137 // attempt to restore our newly added node's view state, 3138 // if applicable. 3139 FunctionID* functionID = fStackFrame->Function() 3140 ->GetFunctionID(); 3141 if (functionID == NULL) 3142 return; 3143 BReference<FunctionID> functionIDReference(functionID, 3144 true); 3145 VariablesViewState* viewState = fViewStateHistory 3146 ->GetState(fThread->ID(), functionID); 3147 if (viewState != NULL) { 3148 TreeTablePath path; 3149 _ApplyViewStateDescendentNodeInfos(viewState, 3150 fVariableTableModel->Root(), path); 3151 } 3152 } 3153 } 3154 3155 3156 void 3157 VariablesView::_HandleTypecastResult(status_t result, ExpressionResult* value) 3158 { 3159 BString errorMessage; 3160 if (value == NULL) { 3161 errorMessage.SetToFormat("Failed to evaluate expression \"%s\": %s (%" 3162 B_PRId32 ")", fPendingTypecastInfo->Expression().String(), 3163 strerror(result), result); 3164 } else if (result != B_OK) { 3165 BVariant valueData; 3166 value->PrimitiveValue()->ToVariant(valueData); 3167 3168 // usually, the evaluation can give us back an error message to 3169 // specifically indicate why it failed. If it did, simply use 3170 // the message directly, otherwise fall back to generating an error 3171 // message based on the error code 3172 if (valueData.Type() == B_STRING_TYPE) 3173 errorMessage = valueData.ToString(); 3174 else { 3175 errorMessage.SetToFormat("Failed to evaluate expression \"%s\":" 3176 " %s (%" B_PRId32 ")", 3177 fPendingTypecastInfo->Expression().String(), strerror(result), 3178 result); 3179 } 3180 3181 } else if (value->Kind() != EXPRESSION_RESULT_KIND_TYPE) { 3182 errorMessage.SetToFormat("Expression \"%s\" does not evaluate to a" 3183 " type.", fPendingTypecastInfo->Expression().String()); 3184 } 3185 3186 if (!errorMessage.IsEmpty()) { 3187 BAlert* alert = new(std::nothrow) BAlert("Typecast error", 3188 errorMessage, "Close"); 3189 if (alert != NULL) 3190 alert->Go(); 3191 3192 return; 3193 } 3194 3195 Type* type = value->GetType(); 3196 BReference<Type> typeRef(type); 3197 ValueNode* valueNode = NULL; 3198 ModelNode* node = fPendingTypecastInfo->TargetNode(); 3199 if (TypeHandlerRoster::Default()->CreateValueNode(node->NodeChild(), type, 3200 valueNode) != B_OK) { 3201 return; 3202 } 3203 3204 node->NodeChild()->SetNode(valueNode); 3205 node->SetCastedType(type); 3206 fVariableTableModel->NotifyNodeChanged(node); 3207 } 3208 3209 3210 void 3211 VariablesView::_HandleEditVariableRequest(ModelNode* node, Value* value) 3212 { 3213 // get a value handler 3214 ValueHandler* valueHandler; 3215 status_t error = ValueHandlerRoster::Default()->FindValueHandler(value, 3216 valueHandler); 3217 if (error != B_OK) 3218 return; 3219 3220 ValueNode* valueNode = node->NodeChild()->Node(); 3221 3222 BReference<ValueHandler> handlerReference(valueHandler, true); 3223 TableCellValueRenderer* renderer = node->TableCellRenderer(); 3224 TableCellValueEditor* editor = NULL; 3225 error = valueHandler->GetTableCellValueEditor(value, 3226 renderer != NULL ? renderer->GetSettings() : NULL, editor); 3227 if (error != B_OK || editor == NULL) 3228 return; 3229 3230 BReference<TableCellValueEditor> editorReference(editor, true); 3231 3232 try { 3233 fEditWindow = VariableEditWindow::Create(value, valueNode, editor, 3234 this); 3235 } catch (...) { 3236 fEditWindow = NULL; 3237 return; 3238 } 3239 3240 fEditWindow->Show(); 3241 } 3242 3243 3244 status_t 3245 VariablesView::_GetTypeForTypeCode(int32 type, Type*& _resultType) const 3246 { 3247 if (BVariant::TypeIsNumber(type) || type == B_STRING_TYPE) { 3248 _resultType = new(std::nothrow) SyntheticPrimitiveType(type); 3249 if (_resultType == NULL) 3250 return B_NO_MEMORY; 3251 3252 return B_OK; 3253 } 3254 3255 return B_NOT_SUPPORTED; 3256 } 3257 3258 3259 // #pragma mark - Listener 3260 3261 3262 VariablesView::Listener::~Listener() 3263 { 3264 } 3265