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 // set value/handler/renderer 1328 modelNode->SetValue(value); 1329 modelNode->SetValueHandler(valueHandler); 1330 modelNode->SetTableCellRenderer(renderer); 1331 1332 // we have to restore renderer settings here since until this point 1333 // we don't yet know what renderer is in use. 1334 if (renderer != NULL) { 1335 Settings* settings = renderer->GetSettings(); 1336 if (settings != NULL) 1337 settings->RestoreValues(modelNode->GetLastRendererSettings()); 1338 } 1339 1340 1341 1342 // notify table model listeners 1343 NotifyNodeChanged(modelNode); 1344 } 1345 1346 1347 int32 1348 VariablesView::VariableTableModel::CountColumns() const 1349 { 1350 return 3; 1351 } 1352 1353 1354 void* 1355 VariablesView::VariableTableModel::Root() const 1356 { 1357 return (void*)this; 1358 } 1359 1360 1361 int32 1362 VariablesView::VariableTableModel::CountChildren(void* parent) const 1363 { 1364 if (parent == this) 1365 return fNodes.CountItems(); 1366 1367 // If the node only has a hidden child, pretend the node directly has the 1368 // child's children. 1369 ModelNode* modelNode = (ModelNode*)parent; 1370 int32 childCount = modelNode->CountChildren(); 1371 if (childCount == 1) { 1372 ModelNode* child = modelNode->ChildAt(0); 1373 if (child->IsHidden()) 1374 return child->CountChildren(); 1375 } 1376 1377 return childCount; 1378 } 1379 1380 1381 void* 1382 VariablesView::VariableTableModel::ChildAt(void* parent, int32 index) const 1383 { 1384 if (parent == this) 1385 return fNodes.ItemAt(index); 1386 1387 // If the node only has a hidden child, pretend the node directly has the 1388 // child's children. 1389 ModelNode* modelNode = (ModelNode*)parent; 1390 int32 childCount = modelNode->CountChildren(); 1391 if (childCount == 1) { 1392 ModelNode* child = modelNode->ChildAt(0); 1393 if (child->IsHidden()) 1394 return child->ChildAt(index); 1395 } 1396 1397 return modelNode->ChildAt(index); 1398 } 1399 1400 1401 bool 1402 VariablesView::VariableTableModel::GetValueAt(void* object, int32 columnIndex, 1403 BVariant& _value) 1404 { 1405 ModelNode* node = (ModelNode*)object; 1406 1407 switch (columnIndex) { 1408 case 0: 1409 _value.SetTo(node->Name(), B_VARIANT_DONT_COPY_DATA); 1410 return true; 1411 case 1: 1412 if (node->GetValue() == NULL) { 1413 ValueLocation* location = node->NodeChild()->Location(); 1414 if (location == NULL) 1415 return false; 1416 1417 ValueNode* childNode = node->NodeChild()->Node(); 1418 if (childNode == NULL) 1419 return false; 1420 1421 Type* nodeChildRawType = childNode->GetType()->ResolveRawType( 1422 false); 1423 if (nodeChildRawType->Kind() == TYPE_COMPOUND) 1424 { 1425 if (location->CountPieces() > 1) 1426 return false; 1427 1428 BString data; 1429 ValuePieceLocation piece = location->PieceAt(0); 1430 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 1431 return false; 1432 1433 data.SetToFormat("[@ %#" B_PRIx64 "]", piece.address); 1434 _value.SetTo(data); 1435 return true; 1436 } 1437 return false; 1438 } 1439 1440 _value.SetTo(node, VALUE_NODE_TYPE); 1441 return true; 1442 case 2: 1443 { 1444 // use the type of the underlying value node, as it may 1445 // be different from the initially assigned top level type 1446 // due to casting 1447 ValueNode* childNode = node->NodeChild()->Node(); 1448 if (childNode == NULL) 1449 return false; 1450 1451 Type* type = childNode->GetType(); 1452 if (type == NULL) 1453 return false; 1454 1455 _value.SetTo(type->Name(), B_VARIANT_DONT_COPY_DATA); 1456 return true; 1457 } 1458 default: 1459 return false; 1460 } 1461 } 1462 1463 1464 void 1465 VariablesView::VariableTableModel::NodeExpanded(ModelNode* node) 1466 { 1467 AutoLocker<ValueNodeContainer> containerLocker( 1468 fNodeManager->GetContainer()); 1469 // add children of all children 1470 1471 // If the node only has a hidden child, add the child's children instead. 1472 if (node->CountChildren() == 1) { 1473 ModelNode* child = node->ChildAt(0); 1474 if (child->IsHidden()) 1475 node = child; 1476 } 1477 1478 // add the children 1479 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) 1480 fNodeManager->AddChildNodes(child->NodeChild()); 1481 } 1482 1483 1484 void 1485 VariablesView::VariableTableModel::NotifyNodeChanged(ModelNode* node) 1486 { 1487 if (!node->IsHidden()) { 1488 TreeTablePath treePath; 1489 if (GetTreePath(node, treePath)) { 1490 int32 index = treePath.RemoveLastComponent(); 1491 NotifyNodesChanged(treePath, index, 1); 1492 } 1493 } 1494 } 1495 1496 1497 void 1498 VariablesView::VariableTableModel::NotifyNodeHidden(ModelNode* node) 1499 { 1500 fContainerListener->ModelNodeHidden(node); 1501 } 1502 1503 1504 bool 1505 VariablesView::VariableTableModel::GetToolTipForTablePath( 1506 const TreeTablePath& path, int32 columnIndex, BToolTip** _tip) 1507 { 1508 ModelNode* node = (ModelNode*)NodeForPath(path); 1509 if (node == NULL) 1510 return false; 1511 1512 BString tipData; 1513 ValueNodeChild* child = node->NodeChild(); 1514 status_t error = child->LocationResolutionState(); 1515 if (error != B_OK) 1516 tipData.SetToFormat("Unable to resolve location: %s", strerror(error)); 1517 else { 1518 ValueNode* valueNode = child->Node(); 1519 if (valueNode == NULL) 1520 return false; 1521 error = valueNode->LocationAndValueResolutionState(); 1522 if (error != B_OK) { 1523 tipData.SetToFormat("Unable to resolve value: %s\n\n", 1524 strerror(error)); 1525 } 1526 1527 switch (columnIndex) { 1528 case 0: 1529 { 1530 ValueLocation* location = child->Location(); 1531 for (int32 i = 0; i < location->CountPieces(); i++) { 1532 ValuePieceLocation piece = location->PieceAt(i); 1533 BString pieceData; 1534 switch (piece.type) { 1535 case VALUE_PIECE_LOCATION_MEMORY: 1536 pieceData.SetToFormat("(%" B_PRId32 "): Address: " 1537 "%#" B_PRIx64 ", Size: %" B_PRId64 " bytes\n", 1538 i, piece.address, piece.size); 1539 break; 1540 case VALUE_PIECE_LOCATION_REGISTER: 1541 { 1542 Architecture* architecture = fThread->GetTeam() 1543 ->GetArchitecture(); 1544 pieceData.SetToFormat("(%" B_PRId32 "): Register " 1545 "(%s)\n", i, 1546 architecture->Registers()[piece.reg].Name()); 1547 break; 1548 } 1549 default: 1550 break; 1551 } 1552 1553 tipData += pieceData; 1554 } 1555 tipData += "Editable: "; 1556 tipData += error == B_OK && location->IsWritable() 1557 ? "Yes" : "No"; 1558 break; 1559 } 1560 case 1: 1561 { 1562 Value* value = node->GetValue(); 1563 if (value != NULL) 1564 value->ToString(tipData); 1565 1566 break; 1567 } 1568 default: 1569 break; 1570 } 1571 } 1572 1573 if (tipData.IsEmpty()) 1574 return false; 1575 1576 *_tip = new(std::nothrow) BTextToolTip(tipData); 1577 if (*_tip == NULL) 1578 return false; 1579 1580 return true; 1581 } 1582 1583 1584 status_t 1585 VariablesView::VariableTableModel::AddSyntheticNode(Variable* variable, 1586 ValueNodeChild*& _child, const char* presentationName) 1587 { 1588 ValueNodeContainer* container = fNodeManager->GetContainer(); 1589 AutoLocker<ValueNodeContainer> containerLocker(container); 1590 1591 status_t error; 1592 if (_child == NULL) { 1593 _child = new(std::nothrow) VariableValueNodeChild(variable); 1594 if (_child == NULL) 1595 return B_NO_MEMORY; 1596 1597 BReference<ValueNodeChild> childReference(_child, true); 1598 ValueNode* valueNode; 1599 if (_child->IsInternal()) 1600 error = _child->CreateInternalNode(valueNode); 1601 else { 1602 error = TypeHandlerRoster::Default()->CreateValueNode(_child, 1603 _child->GetType(), valueNode); 1604 } 1605 1606 if (error != B_OK) 1607 return error; 1608 1609 _child->SetNode(valueNode); 1610 valueNode->ReleaseReference(); 1611 } 1612 1613 container->AddChild(_child); 1614 1615 error = _AddNode(variable, NULL, _child); 1616 if (error != B_OK) { 1617 container->RemoveChild(_child); 1618 return error; 1619 } 1620 1621 // since we're injecting these nodes synthetically, 1622 // we have to manually ask the node manager to create any 1623 // applicable children; this would normally be done implicitly 1624 // for top level nodes, as they're added from the parameters/locals, 1625 // but not here. 1626 fNodeManager->AddChildNodes(_child); 1627 1628 ModelNode* childNode = fNodeTable.Lookup(_child); 1629 if (childNode != NULL) { 1630 if (presentationName != NULL) 1631 childNode->SetPresentationName(presentationName); 1632 1633 ValueNode* valueNode = _child->Node(); 1634 if (valueNode->LocationAndValueResolutionState() 1635 == VALUE_NODE_UNRESOLVED) { 1636 fContainerListener->ModelNodeValueRequested(childNode); 1637 } else 1638 ValueNodeValueChanged(valueNode); 1639 } 1640 ValueNodeChildrenCreated(_child->Node()); 1641 1642 return B_OK; 1643 } 1644 1645 1646 void 1647 VariablesView::VariableTableModel::RemoveSyntheticNode(ModelNode* node) 1648 { 1649 int32 index = fNodes.IndexOf(node); 1650 if (index < 0) 1651 return; 1652 1653 fNodeTable.Remove(node); 1654 1655 fNodes.RemoveItemAt(index); 1656 1657 NotifyNodesRemoved(TreeTablePath(), index, 1); 1658 1659 node->ReleaseReference(); 1660 } 1661 1662 1663 status_t 1664 VariablesView::VariableTableModel::_AddNode(Variable* variable, 1665 ModelNode* parent, ValueNodeChild* nodeChild, bool isPresentationNode, 1666 bool isOnlyChild) 1667 { 1668 // Don't create nodes for unspecified types -- we can't get/show their 1669 // value anyway. 1670 Type* nodeChildRawType = nodeChild->GetType()->ResolveRawType(false); 1671 if (nodeChildRawType->Kind() == TYPE_UNSPECIFIED) 1672 return B_OK; 1673 1674 ModelNode* node = new(std::nothrow) ModelNode(parent, variable, nodeChild, 1675 isPresentationNode); 1676 BReference<ModelNode> nodeReference(node, true); 1677 if (node == NULL || node->Init() != B_OK) 1678 return B_NO_MEMORY; 1679 1680 int32 childIndex; 1681 1682 if (parent != NULL) { 1683 childIndex = parent->CountChildren(); 1684 1685 if (!parent->AddChild(node)) 1686 return B_NO_MEMORY; 1687 // the parent has a reference, now 1688 } else { 1689 childIndex = fNodes.CountItems(); 1690 1691 if (!fNodes.AddItem(node)) 1692 return B_NO_MEMORY; 1693 nodeReference.Detach(); 1694 // the fNodes list has a reference, now 1695 } 1696 1697 fNodeTable.Insert(node); 1698 1699 // if an address type node has only a single child, and that child 1700 // is a compound type, mark it hidden 1701 if (isOnlyChild && parent != NULL) { 1702 ValueNode* parentValueNode = parent->NodeChild()->Node(); 1703 if (parentValueNode != NULL) { 1704 if (parentValueNode->GetType()->ResolveRawType(false)->Kind() 1705 == TYPE_ADDRESS) { 1706 type_kind childKind = nodeChildRawType->Kind(); 1707 if (childKind == TYPE_COMPOUND || childKind == TYPE_ARRAY) { 1708 node->SetHidden(true); 1709 1710 // we need to tell the listener about nodes like this so 1711 // any necessary actions can be taken for them (i.e. value 1712 // resolution), since they're otherwise invisible to 1713 // outsiders. 1714 NotifyNodeHidden(node); 1715 } 1716 } 1717 } 1718 } 1719 1720 // notify table model listeners 1721 if (!node->IsHidden()) { 1722 TreeTablePath path; 1723 if (parent == NULL || GetTreePath(parent, path)) 1724 NotifyNodesAdded(path, childIndex, 1); 1725 } 1726 1727 // if the node is hidden, add its children 1728 if (node->IsHidden()) 1729 fNodeManager->AddChildNodes(nodeChild); 1730 1731 return B_OK; 1732 } 1733 1734 1735 bool 1736 VariablesView::VariableTableModel::GetTreePath(ModelNode* node, 1737 TreeTablePath& _path) const 1738 { 1739 // recurse, if the node has a parent 1740 if (ModelNode* parent = node->Parent()) { 1741 if (!GetTreePath(parent, _path)) 1742 return false; 1743 1744 if (node->IsHidden()) 1745 return true; 1746 1747 return _path.AddComponent(parent->IndexOf(node)); 1748 } 1749 1750 // no parent -- get the index and start the path 1751 int32 index = fNodes.IndexOf(node); 1752 _path.Clear(); 1753 return index >= 0 && _path.AddComponent(index); 1754 } 1755 1756 1757 // #pragma mark - VariablesView 1758 1759 1760 VariablesView::VariablesView(Listener* listener) 1761 : 1762 BGroupView(B_VERTICAL), 1763 fThread(NULL), 1764 fStackFrame(NULL), 1765 fVariableTable(NULL), 1766 fVariableTableModel(NULL), 1767 fContainerListener(NULL), 1768 fPreviousViewState(NULL), 1769 fViewStateHistory(NULL), 1770 fExpressions(NULL), 1771 fExpressionChildren(10, false), 1772 fTableCellContextMenuTracker(NULL), 1773 fPendingTypecastInfo(NULL), 1774 fTemporaryExpression(NULL), 1775 fFrameClearPending(false), 1776 fEditWindow(NULL), 1777 fListener(listener) 1778 { 1779 SetName("Variables"); 1780 } 1781 1782 1783 VariablesView::~VariablesView() 1784 { 1785 if (fEditWindow != NULL) 1786 BMessenger(fEditWindow).SendMessage(B_QUIT_REQUESTED); 1787 1788 SetStackFrame(NULL, NULL); 1789 fVariableTable->SetTreeTableModel(NULL); 1790 1791 if (fPreviousViewState != NULL) 1792 fPreviousViewState->ReleaseReference(); 1793 delete fViewStateHistory; 1794 1795 if (fVariableTableModel != NULL) { 1796 fVariableTableModel->SetContainerListener(NULL); 1797 delete fVariableTableModel; 1798 } 1799 1800 delete fContainerListener; 1801 if (fPendingTypecastInfo != NULL) 1802 fPendingTypecastInfo->ReleaseReference(); 1803 1804 if (fTemporaryExpression != NULL) 1805 fTemporaryExpression->ReleaseReference(); 1806 } 1807 1808 1809 /*static*/ VariablesView* 1810 VariablesView::Create(Listener* listener, ValueNodeManager* manager) 1811 { 1812 VariablesView* self = new VariablesView(listener); 1813 1814 try { 1815 self->_Init(manager); 1816 } catch (...) { 1817 delete self; 1818 throw; 1819 } 1820 1821 return self; 1822 } 1823 1824 1825 void 1826 VariablesView::SetStackFrame(::Thread* thread, StackFrame* stackFrame) 1827 { 1828 bool updateValues = fFrameClearPending; 1829 // We only want to save previous values if we've continued 1830 // execution (i.e. thread/frame are being cleared). 1831 // Otherwise, we'll overwrite our previous values simply 1832 // by switching frames within the same stack trace, which isn't 1833 // desired behavior. 1834 1835 fFrameClearPending = false; 1836 1837 if (thread == fThread && stackFrame == fStackFrame) 1838 return; 1839 1840 _SaveViewState(updateValues); 1841 1842 _FinishContextMenu(true); 1843 1844 for (int32 i = 0; i < fExpressionChildren.CountItems(); i++) 1845 fExpressionChildren.ItemAt(i)->ReleaseReference(); 1846 fExpressionChildren.MakeEmpty(); 1847 1848 if (fThread != NULL) 1849 fThread->ReleaseReference(); 1850 if (fStackFrame != NULL) 1851 fStackFrame->ReleaseReference(); 1852 1853 fThread = thread; 1854 fStackFrame = stackFrame; 1855 1856 if (fThread != NULL) 1857 fThread->AcquireReference(); 1858 if (fStackFrame != NULL) 1859 fStackFrame->AcquireReference(); 1860 1861 fVariableTableModel->SetStackFrame(fThread, fStackFrame); 1862 1863 // request loading the parameter and variable values 1864 if (fThread != NULL && fStackFrame != NULL) { 1865 AutoLocker<Team> locker(fThread->GetTeam()); 1866 1867 void* root = fVariableTableModel->Root(); 1868 int32 count = fVariableTableModel->CountChildren(root); 1869 for (int32 i = 0; i < count; i++) { 1870 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i); 1871 _RequestNodeValue(node); 1872 } 1873 1874 _RestoreExpressionNodes(); 1875 } 1876 1877 _RestoreViewState(); 1878 } 1879 1880 1881 void 1882 VariablesView::MessageReceived(BMessage* message) 1883 { 1884 switch (message->what) { 1885 case MSG_SHOW_INSPECTOR_WINDOW: 1886 { 1887 // TODO: it'd probably be more ideal to extend the context 1888 // action mechanism to allow one to specify an explicit 1889 // target for each action rather than them all defaulting 1890 // to targetting here. 1891 Looper()->PostMessage(message); 1892 break; 1893 } 1894 case MSG_SHOW_VARIABLE_EDIT_WINDOW: 1895 { 1896 if (fEditWindow != NULL) 1897 fEditWindow->Activate(); 1898 else { 1899 ModelNode* node = NULL; 1900 if (message->FindPointer("node", reinterpret_cast<void**>( 1901 &node)) != B_OK) { 1902 break; 1903 } 1904 1905 Value* value = NULL; 1906 if (message->FindPointer("value", reinterpret_cast<void**>( 1907 &value)) != B_OK) { 1908 break; 1909 } 1910 1911 _HandleEditVariableRequest(node, value); 1912 } 1913 break; 1914 } 1915 case MSG_VARIABLE_EDIT_WINDOW_CLOSED: 1916 { 1917 fEditWindow = NULL; 1918 break; 1919 } 1920 case MSG_WRITE_VARIABLE_VALUE: 1921 { 1922 Value* value = NULL; 1923 if (message->FindPointer("value", reinterpret_cast<void**>( 1924 &value)) != B_OK) { 1925 break; 1926 } 1927 1928 BReference<Value> valueReference(value, true); 1929 1930 ValueNode* node = NULL; 1931 if (message->FindPointer("node", reinterpret_cast<void**>( 1932 &node)) != B_OK) { 1933 break; 1934 } 1935 1936 fListener->ValueNodeWriteRequested(node, 1937 fStackFrame->GetCpuState(), value); 1938 break; 1939 } 1940 case MSG_SHOW_TYPECAST_NODE_PROMPT: 1941 { 1942 BMessage* promptMessage = new(std::nothrow) BMessage( 1943 MSG_TYPECAST_NODE); 1944 1945 if (promptMessage == NULL) 1946 return; 1947 1948 ObjectDeleter<BMessage> messageDeleter(promptMessage); 1949 promptMessage->AddPointer("node", fVariableTable 1950 ->SelectionModel()->NodeAt(0)); 1951 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 1952 "Specify Type", "Type: ", NULL, BMessenger(this), 1953 promptMessage); 1954 if (promptWindow == NULL) 1955 return; 1956 1957 messageDeleter.Detach(); 1958 promptWindow->CenterOnScreen(); 1959 promptWindow->Show(); 1960 break; 1961 } 1962 case MSG_TYPECAST_NODE: 1963 { 1964 ModelNode* node = NULL; 1965 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 1966 != B_OK) { 1967 break; 1968 } 1969 1970 BString typeExpression; 1971 if (message->FindString("text", &typeExpression) == B_OK) { 1972 if (typeExpression.IsEmpty()) 1973 break; 1974 1975 if (fPendingTypecastInfo != NULL) 1976 fPendingTypecastInfo->ReleaseReference(); 1977 1978 fPendingTypecastInfo = new(std::nothrow) 1979 VariablesExpressionInfo(typeExpression, node); 1980 if (fPendingTypecastInfo == NULL) { 1981 // TODO: notify user 1982 break; 1983 } 1984 1985 fPendingTypecastInfo->AddListener(this); 1986 fListener->ExpressionEvaluationRequested(fPendingTypecastInfo, 1987 fStackFrame, fThread); 1988 } 1989 break; 1990 } 1991 case MSG_TYPECAST_TO_ARRAY: 1992 { 1993 ModelNode* node = NULL; 1994 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 1995 != B_OK) { 1996 break; 1997 } 1998 1999 Type* baseType = dynamic_cast<AddressType*>(node->NodeChild() 2000 ->Node()->GetType())->BaseType(); 2001 ArrayType* arrayType = NULL; 2002 if (baseType->CreateDerivedArrayType(0, kMaxArrayElementCount, 2003 false, arrayType) != B_OK) { 2004 break; 2005 } 2006 2007 AddressType* addressType = NULL; 2008 BReference<Type> typeRef(arrayType, true); 2009 if (arrayType->CreateDerivedAddressType(DERIVED_TYPE_POINTER, 2010 addressType) != B_OK) { 2011 break; 2012 } 2013 2014 typeRef.Detach(); 2015 typeRef.SetTo(addressType, true); 2016 ValueNode* valueNode = NULL; 2017 if (TypeHandlerRoster::Default()->CreateValueNode( 2018 node->NodeChild(), addressType, valueNode) != B_OK) { 2019 break; 2020 } 2021 2022 typeRef.Detach(); 2023 node->NodeChild()->SetNode(valueNode); 2024 node->SetCastedType(addressType); 2025 fVariableTableModel->NotifyNodeChanged(node); 2026 break; 2027 } 2028 case MSG_SHOW_CONTAINER_RANGE_PROMPT: 2029 { 2030 ModelNode* node = (ModelNode*)fVariableTable 2031 ->SelectionModel()->NodeAt(0); 2032 int32 lowerBound, upperBound; 2033 ValueNode* valueNode = node->NodeChild()->Node(); 2034 if (!valueNode->IsRangedContainer()) { 2035 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2036 if (!valueNode->IsRangedContainer()) 2037 break; 2038 } 2039 2040 bool fixedRange = valueNode->IsContainerRangeFixed(); 2041 if (valueNode->SupportedChildRange(lowerBound, upperBound) 2042 != B_OK) { 2043 break; 2044 } 2045 2046 BMessage* promptMessage = new(std::nothrow) BMessage( 2047 MSG_SET_CONTAINER_RANGE); 2048 if (promptMessage == NULL) 2049 break; 2050 2051 ObjectDeleter<BMessage> messageDeleter(promptMessage); 2052 promptMessage->AddPointer("node", node); 2053 promptMessage->AddBool("fixedRange", fixedRange); 2054 BString infoText; 2055 if (fixedRange) { 2056 infoText.SetToFormat("Allowed range: %" B_PRId32 2057 "-%" B_PRId32 ".", lowerBound, upperBound); 2058 } else { 2059 infoText.SetToFormat("Current range: %" B_PRId32 2060 "-%" B_PRId32 ".", lowerBound, upperBound); 2061 } 2062 2063 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 2064 "Set Range", "Range: ", infoText.String(), BMessenger(this), 2065 promptMessage); 2066 if (promptWindow == NULL) 2067 return; 2068 2069 messageDeleter.Detach(); 2070 promptWindow->CenterOnScreen(); 2071 promptWindow->Show(); 2072 break; 2073 } 2074 case MSG_SET_CONTAINER_RANGE: 2075 { 2076 ModelNode* node = (ModelNode*)fVariableTable 2077 ->SelectionModel()->NodeAt(0); 2078 int32 lowerBound, upperBound; 2079 ValueNode* valueNode = node->NodeChild()->Node(); 2080 if (!valueNode->IsRangedContainer()) 2081 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2082 if (valueNode->SupportedChildRange(lowerBound, upperBound) != B_OK) 2083 break; 2084 2085 bool fixedRange = message->FindBool("fixedRange"); 2086 2087 BString rangeExpression = message->FindString("text"); 2088 if (rangeExpression.Length() == 0) 2089 break; 2090 2091 RangeList ranges; 2092 status_t result = UiUtils::ParseRangeExpression( 2093 rangeExpression, lowerBound, upperBound, fixedRange, ranges); 2094 if (result != B_OK) 2095 break; 2096 2097 valueNode->ClearChildren(); 2098 for (int32 i = 0; i < ranges.CountRanges(); i++) { 2099 const Range* range = ranges.RangeAt(i); 2100 result = valueNode->CreateChildrenInRange( 2101 fThread->GetTeam()->GetTeamTypeInformation(), 2102 range->lowerBound, range->upperBound); 2103 if (result != B_OK) 2104 break; 2105 } 2106 break; 2107 } 2108 case MSG_SHOW_WATCH_VARIABLE_PROMPT: 2109 { 2110 ModelNode* node = reinterpret_cast<ModelNode*>( 2111 fVariableTable->SelectionModel()->NodeAt(0)); 2112 ValueLocation* location = node->NodeChild()->Location(); 2113 ValuePieceLocation piece = location->PieceAt(0); 2114 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 2115 break; 2116 2117 BMessage looperMessage(*message); 2118 looperMessage.AddUInt64("address", piece.address); 2119 looperMessage.AddInt32("length", piece.size); 2120 looperMessage.AddUInt32("type", B_DATA_READ_WRITE_WATCHPOINT); 2121 Looper()->PostMessage(&looperMessage); 2122 break; 2123 } 2124 case MSG_ADD_WATCH_EXPRESSION: 2125 { 2126 BMessage looperMessage(MSG_SHOW_EXPRESSION_PROMPT_WINDOW); 2127 looperMessage.AddPointer("target", this); 2128 Looper()->PostMessage(&looperMessage); 2129 break; 2130 } 2131 case MSG_REMOVE_WATCH_EXPRESSION: 2132 { 2133 ModelNode* node; 2134 if (message->FindPointer("node", reinterpret_cast<void**>(&node)) 2135 != B_OK) { 2136 break; 2137 } 2138 2139 _RemoveExpression(node); 2140 break; 2141 } 2142 case MSG_ADD_NEW_EXPRESSION: 2143 { 2144 const char* expression; 2145 if (message->FindString("expression", &expression) != B_OK) 2146 break; 2147 2148 bool persistentExpression = message->FindBool("persistent"); 2149 2150 ExpressionInfo* info; 2151 status_t error = _AddExpression(expression, persistentExpression, 2152 info); 2153 if (error != B_OK) { 2154 // TODO: notify user of failure 2155 break; 2156 } 2157 2158 fListener->ExpressionEvaluationRequested(info, fStackFrame, 2159 fThread); 2160 break; 2161 } 2162 case MSG_EXPRESSION_EVALUATED: 2163 { 2164 ExpressionInfo* info; 2165 status_t result; 2166 ExpressionResult* value = NULL; 2167 if (message->FindPointer("info", 2168 reinterpret_cast<void**>(&info)) != B_OK 2169 || message->FindInt32("result", &result) != B_OK) { 2170 break; 2171 } 2172 2173 BReference<ExpressionResult> valueReference; 2174 if (message->FindPointer("value", reinterpret_cast<void**>(&value)) 2175 == B_OK) { 2176 valueReference.SetTo(value, true); 2177 } 2178 2179 VariablesExpressionInfo* variableInfo 2180 = dynamic_cast<VariablesExpressionInfo*>(info); 2181 if (variableInfo != NULL) { 2182 if (fPendingTypecastInfo == variableInfo) { 2183 _HandleTypecastResult(result, value); 2184 fPendingTypecastInfo->ReleaseReference(); 2185 fPendingTypecastInfo = NULL; 2186 } 2187 } else { 2188 _AddExpressionNode(info, result, value); 2189 if (info == fTemporaryExpression) { 2190 info->ReleaseReference(); 2191 fTemporaryExpression = NULL; 2192 } 2193 } 2194 2195 break; 2196 } 2197 case MSG_VALUE_NODE_CHANGED: 2198 { 2199 ValueNodeChild* nodeChild; 2200 ValueNode* oldNode; 2201 ValueNode* newNode; 2202 if (message->FindPointer("nodeChild", (void**)&nodeChild) == B_OK 2203 && message->FindPointer("oldNode", (void**)&oldNode) == B_OK 2204 && message->FindPointer("newNode", (void**)&newNode) == B_OK) { 2205 BReference<ValueNodeChild> nodeChildReference(nodeChild, true); 2206 BReference<ValueNode> oldNodeReference(oldNode, true); 2207 BReference<ValueNode> newNodeReference(newNode, true); 2208 2209 fVariableTableModel->ValueNodeChanged(nodeChild, oldNode, 2210 newNode); 2211 } 2212 2213 break; 2214 } 2215 case MSG_VALUE_NODE_CHILDREN_CREATED: 2216 { 2217 ValueNode* node; 2218 if (message->FindPointer("node", (void**)&node) == B_OK) { 2219 BReference<ValueNode> newNodeReference(node, true); 2220 fVariableTableModel->ValueNodeChildrenCreated(node); 2221 } 2222 2223 break; 2224 } 2225 case MSG_VALUE_NODE_CHILDREN_DELETED: 2226 { 2227 ValueNode* node; 2228 if (message->FindPointer("node", (void**)&node) == B_OK) { 2229 BReference<ValueNode> newNodeReference(node, true); 2230 fVariableTableModel->ValueNodeChildrenDeleted(node); 2231 } 2232 2233 break; 2234 } 2235 case MSG_VALUE_NODE_VALUE_CHANGED: 2236 { 2237 ValueNode* node; 2238 if (message->FindPointer("node", (void**)&node) == B_OK) { 2239 BReference<ValueNode> newNodeReference(node, true); 2240 fVariableTableModel->ValueNodeValueChanged(node); 2241 } 2242 2243 break; 2244 } 2245 case MSG_RESTORE_PARTIAL_VIEW_STATE: 2246 { 2247 ModelNode* node; 2248 if (message->FindPointer("node", (void**)&node) == B_OK) { 2249 TreeTablePath path; 2250 if (fVariableTableModel->GetTreePath(node, path)) { 2251 FunctionID* functionID = fStackFrame->Function() 2252 ->GetFunctionID(); 2253 if (functionID == NULL) 2254 return; 2255 BReference<FunctionID> functionIDReference(functionID, 2256 true); 2257 VariablesViewState* viewState = fViewStateHistory 2258 ->GetState(fThread->ID(), functionID); 2259 if (viewState != NULL) { 2260 _ApplyViewStateDescendentNodeInfos(viewState, node, 2261 path); 2262 } 2263 } 2264 } 2265 break; 2266 } 2267 case MSG_VALUE_NODE_NEEDS_VALUE: 2268 case MSG_MODEL_NODE_HIDDEN: 2269 { 2270 ModelNode* node; 2271 if (message->FindPointer("node", (void**)&node) == B_OK) { 2272 BReference<ModelNode> modelNodeReference(node, true); 2273 _RequestNodeValue(node); 2274 } 2275 2276 break; 2277 } 2278 case MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE: 2279 { 2280 _FinishContextMenu(false); 2281 break; 2282 } 2283 case MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED: 2284 { 2285 ModelNode* node; 2286 if (message->FindPointer("node", (void**)&node) != B_OK) 2287 break; 2288 BReference<ModelNode> nodeReference(node, true); 2289 2290 fVariableTableModel->NotifyNodeChanged(node); 2291 break; 2292 } 2293 case B_COPY: 2294 { 2295 _CopyVariableValueToClipboard(); 2296 break; 2297 } 2298 default: 2299 BGroupView::MessageReceived(message); 2300 break; 2301 } 2302 } 2303 2304 2305 void 2306 VariablesView::DetachedFromWindow() 2307 { 2308 _FinishContextMenu(true); 2309 } 2310 2311 2312 void 2313 VariablesView::LoadSettings(const BMessage& settings) 2314 { 2315 BMessage tableSettings; 2316 if (settings.FindMessage("variableTable", &tableSettings) == B_OK) { 2317 GuiSettingsUtils::UnarchiveTableSettings(tableSettings, 2318 fVariableTable); 2319 } 2320 } 2321 2322 2323 status_t 2324 VariablesView::SaveSettings(BMessage& settings) 2325 { 2326 settings.MakeEmpty(); 2327 2328 BMessage tableSettings; 2329 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings, 2330 fVariableTable); 2331 if (result == B_OK) 2332 result = settings.AddMessage("variableTable", &tableSettings); 2333 2334 return result; 2335 } 2336 2337 2338 void 2339 VariablesView::SetStackFrameClearPending() 2340 { 2341 fFrameClearPending = true; 2342 } 2343 2344 2345 void 2346 VariablesView::TreeTableNodeExpandedChanged(TreeTable* table, 2347 const TreeTablePath& path, bool expanded) 2348 { 2349 if (fFrameClearPending) 2350 return; 2351 2352 if (expanded) { 2353 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2354 if (node == NULL) 2355 return; 2356 2357 fVariableTableModel->NodeExpanded(node); 2358 2359 // request the values of all children that don't have any yet 2360 2361 // If the node only has a hidden child, directly load the child's 2362 // children's values. 2363 if (node->CountChildren() == 1) { 2364 ModelNode* child = node->ChildAt(0); 2365 if (child->IsHidden()) 2366 node = child; 2367 } 2368 2369 // request the values 2370 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) { 2371 if (child->IsPresentationNode()) 2372 continue; 2373 2374 _RequestNodeValue(child); 2375 } 2376 } 2377 } 2378 2379 2380 void 2381 VariablesView::TreeTableNodeInvoked(TreeTable* table, 2382 const TreeTablePath& path) 2383 { 2384 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2385 if (node == NULL) 2386 return; 2387 2388 ValueNodeChild* child = node->NodeChild(); 2389 2390 if (child->LocationResolutionState() != B_OK) 2391 return; 2392 2393 ValueLocation* location = child->Location(); 2394 if (!location->IsWritable()) 2395 return; 2396 2397 Value* value = node->GetValue(); 2398 if (value == NULL) 2399 return; 2400 2401 BMessage message(MSG_SHOW_VARIABLE_EDIT_WINDOW); 2402 message.AddPointer("node", node); 2403 message.AddPointer("value", value); 2404 2405 BMessenger(this).SendMessage(&message); 2406 } 2407 2408 2409 void 2410 VariablesView::TreeTableCellMouseDown(TreeTable* table, 2411 const TreeTablePath& path, int32 columnIndex, BPoint screenWhere, 2412 uint32 buttons) 2413 { 2414 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) 2415 return; 2416 2417 if (fFrameClearPending) 2418 return; 2419 2420 _FinishContextMenu(true); 2421 2422 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 2423 if (node == NULL) 2424 return; 2425 2426 Settings* settings = NULL; 2427 SettingsMenu* settingsMenu = NULL; 2428 BReference<SettingsMenu> settingsMenuReference; 2429 status_t error = B_OK; 2430 TableCellValueRenderer* cellRenderer = node->TableCellRenderer(); 2431 if (cellRenderer != NULL) { 2432 settings = cellRenderer->GetSettings(); 2433 if (settings != NULL) { 2434 error = node->GetValueHandler() 2435 ->CreateTableCellValueSettingsMenu(node->GetValue(), settings, 2436 settingsMenu); 2437 settingsMenuReference.SetTo(settingsMenu, true); 2438 if (error != B_OK) 2439 return; 2440 } 2441 } 2442 2443 TableCellContextMenuTracker* tracker = new(std::nothrow) 2444 TableCellContextMenuTracker(node, Looper(), this); 2445 BReference<TableCellContextMenuTracker> trackerReference(tracker); 2446 2447 ContextActionList* preActionList; 2448 ContextActionList* postActionList; 2449 2450 error = _GetContextActionsForNode(node, preActionList, postActionList); 2451 if (error != B_OK) 2452 return; 2453 2454 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter( 2455 preActionList); 2456 2457 BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter( 2458 postActionList); 2459 2460 if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList, 2461 postActionList) != B_OK) { 2462 return; 2463 } 2464 2465 fTableCellContextMenuTracker = trackerReference.Detach(); 2466 fTableCellContextMenuTracker->ShowMenu(screenWhere); 2467 } 2468 2469 2470 void 2471 VariablesView::ExpressionEvaluated(ExpressionInfo* info, status_t result, 2472 ExpressionResult* value) 2473 { 2474 BMessage message(MSG_EXPRESSION_EVALUATED); 2475 message.AddPointer("info", info); 2476 message.AddInt32("result", result); 2477 BReference<ExpressionResult> valueReference; 2478 2479 if (value != NULL) { 2480 valueReference.SetTo(value); 2481 message.AddPointer("value", value); 2482 } 2483 2484 if (BMessenger(this).SendMessage(&message) == B_OK) 2485 valueReference.Detach(); 2486 } 2487 2488 2489 void 2490 VariablesView::_Init(ValueNodeManager* manager) 2491 { 2492 fVariableTable = new TreeTable("variable list", 0, B_FANCY_BORDER); 2493 AddChild(fVariableTable->ToView()); 2494 fVariableTable->SetSortingEnabled(false); 2495 2496 // columns 2497 fVariableTable->AddColumn(new StringTableColumn(0, "Variable", 80, 40, 1000, 2498 B_TRUNCATE_END, B_ALIGN_LEFT)); 2499 fVariableTable->AddColumn(new VariableValueColumn(1, "Value", 80, 40, 1000, 2500 B_TRUNCATE_END, B_ALIGN_RIGHT)); 2501 fVariableTable->AddColumn(new StringTableColumn(2, "Type", 80, 40, 1000, 2502 B_TRUNCATE_END, B_ALIGN_LEFT)); 2503 2504 fVariableTableModel = new VariableTableModel(manager); 2505 if (fVariableTableModel->Init() != B_OK) 2506 throw std::bad_alloc(); 2507 fVariableTable->SetTreeTableModel(fVariableTableModel); 2508 fVariableTable->SetToolTipProvider(fVariableTableModel); 2509 2510 fContainerListener = new ContainerListener(this); 2511 fVariableTableModel->SetContainerListener(fContainerListener); 2512 2513 fVariableTable->AddTreeTableListener(this); 2514 2515 fViewStateHistory = new VariablesViewStateHistory; 2516 if (fViewStateHistory->Init() != B_OK) 2517 throw std::bad_alloc(); 2518 2519 fExpressions = new ExpressionInfoTable(); 2520 if (fExpressions->Init() != B_OK) 2521 throw std::bad_alloc(); 2522 } 2523 2524 2525 void 2526 VariablesView::_RequestNodeValue(ModelNode* node) 2527 { 2528 // get the node child and its container 2529 ValueNodeChild* nodeChild = node->NodeChild(); 2530 ValueNodeContainer* container = nodeChild->Container(); 2531 2532 BReference<ValueNodeContainer> containerReference(container); 2533 AutoLocker<ValueNodeContainer> containerLocker(container); 2534 2535 if (container == NULL || nodeChild->Container() != container) 2536 return; 2537 2538 // get the value node and check whether its value has not yet been resolved 2539 ValueNode* valueNode = nodeChild->Node(); 2540 if (valueNode == NULL) { 2541 ModelNode* parent = node->Parent(); 2542 if (parent != NULL) { 2543 TreeTablePath path; 2544 if (!fVariableTableModel->GetTreePath(parent, path)) 2545 return; 2546 2547 // if the parent node was already expanded when the child was 2548 // added, we may not yet have added a value node. 2549 // Notify the table model that this needs to be done. 2550 if (fVariableTable->IsNodeExpanded(path)) 2551 fVariableTableModel->NodeExpanded(parent); 2552 } 2553 } 2554 2555 if (valueNode == NULL || valueNode->LocationAndValueResolutionState() 2556 != VALUE_NODE_UNRESOLVED) { 2557 return; 2558 } 2559 2560 BReference<ValueNode> valueNodeReference(valueNode); 2561 containerLocker.Unlock(); 2562 2563 // request resolution of the value 2564 fListener->ValueNodeValueRequested(fStackFrame != NULL 2565 ? fStackFrame->GetCpuState() : NULL, container, valueNode); 2566 } 2567 2568 2569 status_t 2570 VariablesView::_GetContextActionsForNode(ModelNode* node, 2571 ContextActionList*& _preActions, ContextActionList*& _postActions) 2572 { 2573 _preActions = NULL; 2574 _postActions = NULL; 2575 2576 ValueLocation* location = node->NodeChild()->Location(); 2577 2578 _preActions = new(std::nothrow) ContextActionList; 2579 if (_preActions == NULL) 2580 return B_NO_MEMORY; 2581 2582 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter( 2583 _preActions); 2584 2585 status_t result = B_OK; 2586 BMessage* message = NULL; 2587 2588 // only show the Inspect option if the value is in fact located 2589 // in memory. 2590 if (location != NULL) { 2591 if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) { 2592 result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW, 2593 _preActions, message); 2594 if (result != B_OK) 2595 return result; 2596 message->AddUInt64("address", location->PieceAt(0).address); 2597 } 2598 2599 ValueNode* valueNode = node->NodeChild()->Node(); 2600 2601 if (valueNode != NULL) { 2602 Value* value = valueNode->GetValue(); 2603 if (location->IsWritable() && value != NULL) { 2604 result = _AddContextAction("Edit" B_UTF8_ELLIPSIS, 2605 MSG_SHOW_VARIABLE_EDIT_WINDOW, _preActions, message); 2606 if (result != B_OK) 2607 return result; 2608 message->AddPointer("node", node); 2609 message->AddPointer("value", value); 2610 } 2611 AddressType* type = dynamic_cast<AddressType*>(valueNode->GetType()); 2612 if (type != NULL && type->BaseType() != NULL) { 2613 result = _AddContextAction("Cast to array", MSG_TYPECAST_TO_ARRAY, 2614 _preActions, message); 2615 if (result != B_OK) 2616 return result; 2617 message->AddPointer("node", node); 2618 } 2619 } 2620 2621 result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS, 2622 MSG_SHOW_TYPECAST_NODE_PROMPT, _preActions, message); 2623 if (result != B_OK) 2624 return result; 2625 2626 result = _AddContextAction("Watch" B_UTF8_ELLIPSIS, 2627 MSG_SHOW_WATCH_VARIABLE_PROMPT, _preActions, message); 2628 if (result != B_OK) 2629 return result; 2630 2631 if (valueNode == NULL) 2632 return B_OK; 2633 2634 if (valueNode->LocationAndValueResolutionState() == B_OK) { 2635 result = _AddContextAction("Copy Value", B_COPY, _preActions, message); 2636 if (result != B_OK) 2637 return result; 2638 } 2639 2640 bool addRangedContainerItem = false; 2641 // if the current node isn't itself a ranged container, check if it 2642 // contains a hidden node which is, since in the latter case we 2643 // want to present the range selection as well. 2644 if (valueNode->IsRangedContainer()) 2645 addRangedContainerItem = true; 2646 else if (node->CountChildren() == 1 && node->ChildAt(0)->IsHidden()) { 2647 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2648 if (valueNode != NULL && valueNode->IsRangedContainer()) 2649 addRangedContainerItem = true; 2650 } 2651 2652 if (addRangedContainerItem) { 2653 result = _AddContextAction("Set visible range" B_UTF8_ELLIPSIS, 2654 MSG_SHOW_CONTAINER_RANGE_PROMPT, _preActions, message); 2655 if (result != B_OK) 2656 return result; 2657 } 2658 } 2659 2660 _postActions = new(std::nothrow) ContextActionList; 2661 if (_postActions == NULL) 2662 return B_NO_MEMORY; 2663 2664 BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter( 2665 _postActions); 2666 2667 result = _AddContextAction("Add watch expression" B_UTF8_ELLIPSIS, 2668 MSG_ADD_WATCH_EXPRESSION, _postActions, message); 2669 if (result != B_OK) 2670 return result; 2671 2672 if (fExpressionChildren.HasItem(node->NodeChild())) { 2673 result = _AddContextAction("Remove watch expression", 2674 MSG_REMOVE_WATCH_EXPRESSION, _postActions, message); 2675 if (result != B_OK) 2676 return result; 2677 message->AddPointer("node", node); 2678 } 2679 2680 preActionListDeleter.Detach(); 2681 postActionListDeleter.Detach(); 2682 2683 return B_OK; 2684 } 2685 2686 2687 status_t 2688 VariablesView::_AddContextAction(const char* action, uint32 what, 2689 ContextActionList* actions, BMessage*& _message) 2690 { 2691 _message = new(std::nothrow) BMessage(what); 2692 if (_message == NULL) 2693 return B_NO_MEMORY; 2694 2695 ObjectDeleter<BMessage> messageDeleter(_message); 2696 2697 ActionMenuItem* item = new(std::nothrow) ActionMenuItem(action, 2698 _message); 2699 if (item == NULL) 2700 return B_NO_MEMORY; 2701 2702 messageDeleter.Detach(); 2703 ObjectDeleter<ActionMenuItem> actionDeleter(item); 2704 if (!actions->AddItem(item)) 2705 return B_NO_MEMORY; 2706 2707 actionDeleter.Detach(); 2708 2709 return B_OK; 2710 } 2711 2712 2713 void 2714 VariablesView::_FinishContextMenu(bool force) 2715 { 2716 if (fTableCellContextMenuTracker != NULL) { 2717 if (!fTableCellContextMenuTracker->FinishMenu(force) || force) { 2718 fTableCellContextMenuTracker->ReleaseReference(); 2719 fTableCellContextMenuTracker = NULL; 2720 } 2721 } 2722 } 2723 2724 2725 2726 void 2727 VariablesView::_SaveViewState(bool updateValues) const 2728 { 2729 if (fThread == NULL || fStackFrame == NULL 2730 || fStackFrame->Function() == NULL) { 2731 return; 2732 } 2733 2734 // get the function ID 2735 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2736 if (functionID == NULL) 2737 return; 2738 BReference<FunctionID> functionIDReference(functionID, true); 2739 2740 StackFrameValues* values = NULL; 2741 ExpressionValues* expressionValues = NULL; 2742 BReference<StackFrameValues> valuesReference; 2743 BReference<ExpressionValues> expressionsReference; 2744 2745 if (!updateValues) { 2746 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), 2747 functionID); 2748 if (viewState != NULL) { 2749 values = viewState->Values(); 2750 valuesReference.SetTo(values); 2751 2752 expressionValues = viewState->GetExpressionValues(); 2753 expressionsReference.SetTo(expressionValues); 2754 } 2755 } 2756 2757 if (values == NULL) { 2758 values = new(std::nothrow) StackFrameValues; 2759 if (values == NULL) 2760 return; 2761 valuesReference.SetTo(values, true); 2762 2763 if (values->Init() != B_OK) 2764 return; 2765 2766 expressionValues = new(std::nothrow) ExpressionValues; 2767 if (expressionValues == NULL) 2768 return; 2769 expressionsReference.SetTo(expressionValues, true); 2770 2771 if (expressionValues->Init() != B_OK) 2772 return; 2773 } 2774 2775 // create an empty view state 2776 VariablesViewState* viewState = new(std::nothrow) VariablesViewState; 2777 if (viewState == NULL) 2778 return; 2779 BReference<VariablesViewState> viewStateReference(viewState, true); 2780 2781 if (viewState->Init() != B_OK) 2782 return; 2783 2784 viewState->SetValues(values); 2785 viewState->SetExpressionValues(expressionValues); 2786 2787 // populate it 2788 TreeTablePath path; 2789 if (_AddViewStateDescendentNodeInfos(viewState, 2790 fVariableTableModel->Root(), path, updateValues) != B_OK) { 2791 return; 2792 } 2793 2794 // add the view state to the history 2795 fViewStateHistory->SetState(fThread->ID(), functionID, viewState); 2796 } 2797 2798 2799 void 2800 VariablesView::_RestoreViewState() 2801 { 2802 if (fPreviousViewState != NULL) { 2803 fPreviousViewState->ReleaseReference(); 2804 fPreviousViewState = NULL; 2805 } 2806 2807 if (fThread == NULL || fStackFrame == NULL 2808 || fStackFrame->Function() == NULL) { 2809 return; 2810 } 2811 2812 // get the function ID 2813 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2814 if (functionID == NULL) 2815 return; 2816 BReference<FunctionID> functionIDReference(functionID, true); 2817 2818 // get the previous view state 2819 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), 2820 functionID); 2821 if (viewState == NULL) 2822 return; 2823 2824 // apply the view state 2825 TreeTablePath path; 2826 _ApplyViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), 2827 path); 2828 } 2829 2830 2831 status_t 2832 VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState, 2833 void* parent, TreeTablePath& path, bool updateValues) const 2834 { 2835 int32 childCount = fVariableTableModel->CountChildren(parent); 2836 for (int32 i = 0; i < childCount; i++) { 2837 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i); 2838 if (!path.AddComponent(i)) 2839 return B_NO_MEMORY; 2840 2841 // add the node's info 2842 VariablesViewNodeInfo nodeInfo; 2843 nodeInfo.SetNodeExpanded(fVariableTable->IsNodeExpanded(path)); 2844 nodeInfo.SetCastedType(node->GetCastedType()); 2845 TableCellValueRenderer* renderer = node->TableCellRenderer(); 2846 if (renderer != NULL) { 2847 Settings* settings = renderer->GetSettings(); 2848 if (settings != NULL) 2849 nodeInfo.SetRendererSettings(settings->Message()); 2850 } 2851 2852 Value* value = node->GetValue(); 2853 Variable* variable = node->GetVariable(); 2854 TypeComponentPath* componentPath = node->GetPath(); 2855 ObjectID* id = variable->ID(); 2856 2857 status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo); 2858 if (error != B_OK) 2859 return error; 2860 2861 if (value != NULL && updateValues) { 2862 BVariant variableValueData; 2863 if (value->ToVariant(variableValueData)) 2864 error = viewState->Values()->SetValue(id, componentPath, 2865 variableValueData); 2866 if (error != B_OK) 2867 return error; 2868 } 2869 2870 // recurse 2871 error = _AddViewStateDescendentNodeInfos(viewState, node, path, 2872 updateValues); 2873 if (error != B_OK) 2874 return error; 2875 2876 path.RemoveLastComponent(); 2877 } 2878 2879 return B_OK; 2880 } 2881 2882 2883 status_t 2884 VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState, 2885 void* parent, TreeTablePath& path) 2886 { 2887 int32 childCount = fVariableTableModel->CountChildren(parent); 2888 for (int32 i = 0; i < childCount; i++) { 2889 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i); 2890 if (!path.AddComponent(i)) 2891 return B_NO_MEMORY; 2892 2893 // apply the node's info, if any 2894 ObjectID* objectID = node->GetVariable()->ID(); 2895 TypeComponentPath* componentPath = node->GetPath(); 2896 const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( 2897 objectID, componentPath); 2898 if (nodeInfo != NULL) { 2899 // NB: if the node info indicates that the node in question 2900 // was being cast to a different type, this *must* be applied 2901 // before any other view state restoration, since it 2902 // potentially changes the child hierarchy under that node. 2903 Type* type = nodeInfo->GetCastedType(); 2904 if (type != NULL) { 2905 ValueNode* valueNode = NULL; 2906 if (TypeHandlerRoster::Default()->CreateValueNode( 2907 node->NodeChild(), type, valueNode) == B_OK) { 2908 node->NodeChild()->SetNode(valueNode); 2909 node->SetCastedType(type); 2910 } 2911 } 2912 2913 // we don't have a renderer yet so we can't apply the settings 2914 // at this stage. Store them on the model node so we can lazily 2915 // apply them once the value is retrieved. 2916 node->SetLastRendererSettings(nodeInfo->GetRendererSettings()); 2917 2918 fVariableTable->SetNodeExpanded(path, 2919 nodeInfo->IsNodeExpanded()); 2920 2921 BVariant previousValue; 2922 if (viewState->Values()->GetValue(objectID, componentPath, 2923 previousValue)) { 2924 node->SetPreviousValue(previousValue); 2925 } 2926 } 2927 2928 // recurse 2929 status_t error = _ApplyViewStateDescendentNodeInfos(viewState, node, 2930 path); 2931 if (error != B_OK) 2932 return error; 2933 2934 path.RemoveLastComponent(); 2935 } 2936 2937 return B_OK; 2938 } 2939 2940 2941 void 2942 VariablesView::_CopyVariableValueToClipboard() 2943 { 2944 ModelNode* node = reinterpret_cast<ModelNode*>( 2945 fVariableTable->SelectionModel()->NodeAt(0)); 2946 2947 Value* value = node->GetValue(); 2948 BString valueData; 2949 if (value != NULL && value->ToString(valueData)) { 2950 be_clipboard->Lock(); 2951 be_clipboard->Data()->RemoveData("text/plain"); 2952 be_clipboard->Data()->AddData ("text/plain", 2953 B_MIME_TYPE, valueData.String(), 2954 valueData.Length()); 2955 be_clipboard->Commit(); 2956 be_clipboard->Unlock(); 2957 } 2958 } 2959 2960 2961 status_t 2962 VariablesView::_AddExpression(const char* expression, 2963 bool persistentExpression, ExpressionInfo*& _info) 2964 { 2965 ExpressionInfoEntry* entry = NULL; 2966 if (persistentExpression) { 2967 // if our stack frame doesn't have an associated function, 2968 // we can't add an expression 2969 FunctionInstance* function = fStackFrame->Function(); 2970 if (function == NULL) 2971 return B_NOT_ALLOWED; 2972 2973 FunctionID* id = function->GetFunctionID(); 2974 if (id == NULL) 2975 return B_NO_MEMORY; 2976 2977 BReference<FunctionID> idReference(id, true); 2978 2979 entry = fExpressions->Lookup(FunctionKey(id)); 2980 if (entry == NULL) { 2981 entry = new(std::nothrow) ExpressionInfoEntry(id); 2982 if (entry == NULL) 2983 return B_NO_MEMORY; 2984 status_t error = fExpressions->Insert(entry); 2985 if (error != B_OK) { 2986 delete entry; 2987 return error; 2988 } 2989 } 2990 } 2991 2992 ExpressionInfo* info = new(std::nothrow) ExpressionInfo(expression); 2993 2994 if (info == NULL) 2995 return B_NO_MEMORY; 2996 2997 BReference<ExpressionInfo> infoReference(info, true); 2998 2999 if (persistentExpression) { 3000 if (!entry->AddItem(info)) 3001 return B_NO_MEMORY; 3002 } else 3003 fTemporaryExpression = info; 3004 3005 info->AddListener(this); 3006 infoReference.Detach(); 3007 _info = info; 3008 return B_OK; 3009 } 3010 3011 3012 void 3013 VariablesView::_RemoveExpression(ModelNode* node) 3014 { 3015 if (!fExpressionChildren.HasItem(node->NodeChild())) 3016 return; 3017 3018 FunctionID* id = fStackFrame->Function()->GetFunctionID(); 3019 BReference<FunctionID> idReference(id, true); 3020 3021 ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); 3022 if (entry == NULL) 3023 return; 3024 3025 for (int32 i = 0; i < entry->CountItems(); i++) { 3026 ExpressionInfo* info = entry->ItemAt(i); 3027 if (info->Expression() == node->Name()) { 3028 entry->RemoveItemAt(i); 3029 info->RemoveListener(this); 3030 info->ReleaseReference(); 3031 break; 3032 } 3033 } 3034 3035 fVariableTableModel->RemoveSyntheticNode(node); 3036 } 3037 3038 3039 void 3040 VariablesView::_RestoreExpressionNodes() 3041 { 3042 FunctionInstance* instance = fStackFrame->Function(); 3043 if (instance == NULL) 3044 return; 3045 3046 FunctionID* id = instance->GetFunctionID(); 3047 if (id == NULL) 3048 return; 3049 3050 BReference<FunctionID> idReference(id, true); 3051 3052 ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); 3053 if (entry == NULL) 3054 return; 3055 3056 for (int32 i = 0; i < entry->CountItems(); i++) { 3057 ExpressionInfo* info = entry->ItemAt(i); 3058 fListener->ExpressionEvaluationRequested(info, fStackFrame, fThread); 3059 } 3060 } 3061 3062 3063 void 3064 VariablesView::_AddExpressionNode(ExpressionInfo* info, status_t result, 3065 ExpressionResult* value) 3066 { 3067 bool temporaryExpression = (info == fTemporaryExpression); 3068 Variable* variable = NULL; 3069 BReference<Variable> variableReference; 3070 BVariant valueData; 3071 3072 ExpressionVariableID* id 3073 = new(std::nothrow) ExpressionVariableID(info); 3074 if (id == NULL) 3075 return; 3076 BReference<ObjectID> idReference(id, true); 3077 3078 Type* type = NULL; 3079 ValueLocation* location = NULL; 3080 ValueNodeChild* child = NULL; 3081 BReference<Type> typeReference; 3082 BReference<ValueLocation> locationReference; 3083 if (value->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) { 3084 value->PrimitiveValue()->ToVariant(valueData); 3085 if (_GetTypeForTypeCode(valueData.Type(), type) != B_OK) 3086 return; 3087 typeReference.SetTo(type, true); 3088 3089 location = new(std::nothrow) ValueLocation(); 3090 if (location == NULL) 3091 return; 3092 locationReference.SetTo(location, true); 3093 3094 if (valueData.IsNumber()) { 3095 3096 ValuePieceLocation piece; 3097 if (!piece.SetToValue(valueData.Bytes(), valueData.Size()) 3098 || !location->AddPiece(piece)) { 3099 return; 3100 } 3101 } 3102 } else if (value->Kind() == EXPRESSION_RESULT_KIND_VALUE_NODE) { 3103 child = value->ValueNodeValue(); 3104 type = child->GetType(); 3105 typeReference.SetTo(type); 3106 location = child->Location(); 3107 locationReference.SetTo(location); 3108 } 3109 3110 variable = new(std::nothrow) Variable(id, 3111 info->Expression(), type, location); 3112 if (variable == NULL) 3113 return; 3114 variableReference.SetTo(variable, true); 3115 3116 status_t error = fVariableTableModel->AddSyntheticNode(variable, child, 3117 info->Expression()); 3118 if (error != B_OK) 3119 return; 3120 3121 // In the case of either an evaluation error, or an unsupported result 3122 // type, set an explanatory string for the result directly. 3123 if (result != B_OK || valueData.Type() == B_STRING_TYPE) { 3124 StringValue* explicitValue = new(std::nothrow) StringValue( 3125 valueData.ToString()); 3126 if (explicitValue == NULL) 3127 return; 3128 3129 child->Node()->SetLocationAndValue(NULL, explicitValue, B_OK); 3130 } 3131 3132 if (temporaryExpression || fExpressionChildren.AddItem(child)) { 3133 child->AcquireReference(); 3134 3135 if (temporaryExpression) 3136 return; 3137 3138 // attempt to restore our newly added node's view state, 3139 // if applicable. 3140 FunctionID* functionID = fStackFrame->Function() 3141 ->GetFunctionID(); 3142 if (functionID == NULL) 3143 return; 3144 BReference<FunctionID> functionIDReference(functionID, 3145 true); 3146 VariablesViewState* viewState = fViewStateHistory 3147 ->GetState(fThread->ID(), functionID); 3148 if (viewState != NULL) { 3149 TreeTablePath path; 3150 _ApplyViewStateDescendentNodeInfos(viewState, 3151 fVariableTableModel->Root(), path); 3152 } 3153 } 3154 } 3155 3156 3157 void 3158 VariablesView::_HandleTypecastResult(status_t result, ExpressionResult* value) 3159 { 3160 BString errorMessage; 3161 if (value == NULL) { 3162 errorMessage.SetToFormat("Failed to evaluate expression \"%s\": %s (%" 3163 B_PRId32 ")", fPendingTypecastInfo->Expression().String(), 3164 strerror(result), result); 3165 } else if (result != B_OK) { 3166 BVariant valueData; 3167 value->PrimitiveValue()->ToVariant(valueData); 3168 3169 // usually, the evaluation can give us back an error message to 3170 // specifically indicate why it failed. If it did, simply use 3171 // the message directly, otherwise fall back to generating an error 3172 // message based on the error code 3173 if (valueData.Type() == B_STRING_TYPE) 3174 errorMessage = valueData.ToString(); 3175 else { 3176 errorMessage.SetToFormat("Failed to evaluate expression \"%s\":" 3177 " %s (%" B_PRId32 ")", 3178 fPendingTypecastInfo->Expression().String(), strerror(result), 3179 result); 3180 } 3181 3182 } else if (value->Kind() != EXPRESSION_RESULT_KIND_TYPE) { 3183 errorMessage.SetToFormat("Expression \"%s\" does not evaluate to a" 3184 " type.", fPendingTypecastInfo->Expression().String()); 3185 } 3186 3187 if (!errorMessage.IsEmpty()) { 3188 BAlert* alert = new(std::nothrow) BAlert("Typecast error", 3189 errorMessage, "Close"); 3190 if (alert != NULL) 3191 alert->Go(); 3192 3193 return; 3194 } 3195 3196 Type* type = value->GetType(); 3197 BReference<Type> typeRef(type); 3198 ValueNode* valueNode = NULL; 3199 ModelNode* node = fPendingTypecastInfo->TargetNode(); 3200 if (TypeHandlerRoster::Default()->CreateValueNode(node->NodeChild(), type, 3201 valueNode) != B_OK) { 3202 return; 3203 } 3204 3205 node->NodeChild()->SetNode(valueNode); 3206 node->SetCastedType(type); 3207 fVariableTableModel->NotifyNodeChanged(node); 3208 } 3209 3210 3211 void 3212 VariablesView::_HandleEditVariableRequest(ModelNode* node, Value* value) 3213 { 3214 // get a value handler 3215 ValueHandler* valueHandler; 3216 status_t error = ValueHandlerRoster::Default()->FindValueHandler(value, 3217 valueHandler); 3218 if (error != B_OK) 3219 return; 3220 3221 ValueNode* valueNode = node->NodeChild()->Node(); 3222 3223 BReference<ValueHandler> handlerReference(valueHandler, true); 3224 TableCellValueRenderer* renderer = node->TableCellRenderer(); 3225 TableCellValueEditor* editor = NULL; 3226 error = valueHandler->GetTableCellValueEditor(value, 3227 renderer != NULL ? renderer->GetSettings() : NULL, editor); 3228 if (error != B_OK || editor == NULL) 3229 return; 3230 3231 BReference<TableCellValueEditor> editorReference(editor, true); 3232 3233 try { 3234 fEditWindow = VariableEditWindow::Create(value, valueNode, editor, 3235 this); 3236 } catch (...) { 3237 fEditWindow = NULL; 3238 return; 3239 } 3240 3241 fEditWindow->Show(); 3242 } 3243 3244 3245 status_t 3246 VariablesView::_GetTypeForTypeCode(int32 type, Type*& _resultType) const 3247 { 3248 if (BVariant::TypeIsNumber(type) || type == B_STRING_TYPE) { 3249 _resultType = new(std::nothrow) SyntheticPrimitiveType(type); 3250 if (_resultType == NULL) 3251 return B_NO_MEMORY; 3252 3253 return B_OK; 3254 } 3255 3256 return B_NOT_SUPPORTED; 3257 } 3258 3259 3260 // #pragma mark - Listener 3261 3262 3263 VariablesView::Listener::~Listener() 3264 { 3265 } 3266