1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2011-2014, 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 "FileSourceCode.h" 29 #include "Function.h" 30 #include "FunctionID.h" 31 #include "FunctionInstance.h" 32 #include "GuiSettingsUtils.h" 33 #include "MessageCodes.h" 34 #include "RangeList.h" 35 #include "Register.h" 36 #include "SettingsMenu.h" 37 #include "SourceLanguage.h" 38 #include "StackTrace.h" 39 #include "StackFrame.h" 40 #include "StackFrameValues.h" 41 #include "TableCellValueRenderer.h" 42 #include "Team.h" 43 #include "TeamDebugInfo.h" 44 #include "Thread.h" 45 #include "Tracing.h" 46 #include "TypeComponentPath.h" 47 #include "TypeHandlerRoster.h" 48 #include "TypeLookupConstraints.h" 49 #include "UiUtils.h" 50 #include "Value.h" 51 #include "ValueHandler.h" 52 #include "ValueHandlerRoster.h" 53 #include "ValueLocation.h" 54 #include "ValueNode.h" 55 #include "ValueNodeManager.h" 56 #include "Variable.h" 57 #include "VariableValueNodeChild.h" 58 #include "VariablesViewState.h" 59 #include "VariablesViewStateHistory.h" 60 61 62 enum { 63 VALUE_NODE_TYPE = 'valn' 64 }; 65 66 67 enum { 68 MSG_MODEL_NODE_HIDDEN = 'monh', 69 MSG_VALUE_NODE_NEEDS_VALUE = 'mvnv', 70 MSG_RESTORE_PARTIAL_VIEW_STATE = 'mpvs' 71 }; 72 73 74 // maximum number of array elements to show by default 75 static const uint64 kMaxArrayElementCount = 10; 76 77 78 class VariablesView::ContainerListener : public ValueNodeContainer::Listener { 79 public: 80 ContainerListener(BHandler* indirectTarget); 81 82 void SetModel(VariableTableModel* model); 83 84 virtual void ValueNodeChanged(ValueNodeChild* nodeChild, 85 ValueNode* oldNode, ValueNode* newNode); 86 virtual void ValueNodeChildrenCreated(ValueNode* node); 87 virtual void ValueNodeChildrenDeleted(ValueNode* node); 88 virtual void ValueNodeValueChanged(ValueNode* node); 89 90 virtual void ModelNodeHidden(ModelNode* node); 91 92 virtual void ModelNodeValueRequested(ModelNode* node); 93 94 virtual void ModelNodeRestoreViewStateRequested(ModelNode* node); 95 96 private: 97 BHandler* fIndirectTarget; 98 VariableTableModel* fModel; 99 }; 100 101 102 class VariablesView::ModelNode : public BReferenceable { 103 public: 104 ModelNode(ModelNode* parent, Variable* variable, ValueNodeChild* nodeChild, 105 bool isPresentationNode) 106 : 107 fParent(parent), 108 fNodeChild(nodeChild), 109 fVariable(variable), 110 fValue(NULL), 111 fValueHandler(NULL), 112 fTableCellRenderer(NULL), 113 fLastRendererSettings(), 114 fCastedType(NULL), 115 fComponentPath(NULL), 116 fIsPresentationNode(isPresentationNode), 117 fHidden(false) 118 { 119 fNodeChild->AcquireReference(); 120 } 121 122 ~ModelNode() 123 { 124 SetTableCellRenderer(NULL); 125 SetValueHandler(NULL); 126 SetValue(NULL); 127 128 for (int32 i = 0; ModelNode* child = fChildren.ItemAt(i); i++) 129 child->ReleaseReference(); 130 131 fNodeChild->ReleaseReference(); 132 133 if (fComponentPath != NULL) 134 fComponentPath->ReleaseReference(); 135 136 if (fCastedType != NULL) 137 fCastedType->ReleaseReference(); 138 } 139 140 status_t Init() 141 { 142 fComponentPath = new(std::nothrow) TypeComponentPath(); 143 if (fComponentPath == NULL) 144 return B_NO_MEMORY; 145 146 if (fParent != NULL) 147 *fComponentPath = *fParent->GetPath(); 148 149 TypeComponent component; 150 // TODO: this should actually discriminate between different 151 // classes of type component kinds 152 component.SetToBaseType(fNodeChild->GetType()->Kind(), 153 0, fNodeChild->Name()); 154 155 fComponentPath->AddComponent(component); 156 157 return B_OK; 158 } 159 160 ModelNode* Parent() const 161 { 162 return fParent; 163 } 164 165 ValueNodeChild* NodeChild() const 166 { 167 return fNodeChild; 168 } 169 170 const BString& Name() const 171 { 172 return fNodeChild->Name(); 173 } 174 175 Type* GetType() const 176 { 177 if (fCastedType != NULL) 178 return fCastedType; 179 180 return fNodeChild->GetType(); 181 } 182 183 Variable* GetVariable() const 184 { 185 return fVariable; 186 } 187 188 Value* GetValue() const 189 { 190 return fValue; 191 } 192 193 void SetValue(Value* value) 194 { 195 if (value == fValue) 196 return; 197 198 if (fValue != NULL) 199 fValue->ReleaseReference(); 200 201 fValue = value; 202 203 if (fValue != NULL) 204 fValue->AcquireReference(); 205 } 206 207 Type* GetCastedType() const 208 { 209 return fCastedType; 210 } 211 212 void SetCastedType(Type* type) 213 { 214 if (fCastedType != NULL) 215 fCastedType->ReleaseReference(); 216 217 fCastedType = type; 218 if (type != NULL) 219 fCastedType->AcquireReference(); 220 } 221 222 const BMessage& GetLastRendererSettings() const 223 { 224 return fLastRendererSettings; 225 } 226 227 void SetLastRendererSettings(const BMessage& settings) 228 { 229 fLastRendererSettings = settings; 230 } 231 232 TypeComponentPath* GetPath() const 233 { 234 return fComponentPath; 235 } 236 237 ValueHandler* GetValueHandler() const 238 { 239 return fValueHandler; 240 } 241 242 void SetValueHandler(ValueHandler* handler) 243 { 244 if (handler == fValueHandler) 245 return; 246 247 if (fValueHandler != NULL) 248 fValueHandler->ReleaseReference(); 249 250 fValueHandler = handler; 251 252 if (fValueHandler != NULL) 253 fValueHandler->AcquireReference(); 254 } 255 256 257 TableCellValueRenderer* TableCellRenderer() const 258 { 259 return fTableCellRenderer; 260 } 261 262 void SetTableCellRenderer(TableCellValueRenderer* renderer) 263 { 264 if (renderer == fTableCellRenderer) 265 return; 266 267 if (fTableCellRenderer != NULL) 268 fTableCellRenderer->ReleaseReference(); 269 270 fTableCellRenderer = renderer; 271 272 if (fTableCellRenderer != NULL) 273 fTableCellRenderer->AcquireReference(); 274 } 275 276 bool IsPresentationNode() const 277 { 278 return fIsPresentationNode; 279 } 280 281 bool IsHidden() const 282 { 283 return fHidden; 284 } 285 286 void SetHidden(bool hidden) 287 { 288 fHidden = hidden; 289 } 290 291 int32 CountChildren() const 292 { 293 return fChildren.CountItems(); 294 } 295 296 ModelNode* ChildAt(int32 index) const 297 { 298 return fChildren.ItemAt(index); 299 } 300 301 int32 IndexOf(ModelNode* child) const 302 { 303 return fChildren.IndexOf(child); 304 } 305 306 bool AddChild(ModelNode* child) 307 { 308 if (!fChildren.AddItem(child)) 309 return false; 310 311 child->AcquireReference(); 312 return true; 313 } 314 315 bool RemoveChild(ModelNode* child) 316 { 317 if (!fChildren.RemoveItem(child)) 318 return false; 319 320 child->ReleaseReference(); 321 return true; 322 } 323 324 bool RemoveAllChildren() 325 { 326 for (int32 i = 0; i < fChildren.CountItems(); i++) 327 RemoveChild(fChildren.ItemAt(i)); 328 329 return true; 330 } 331 332 private: 333 typedef BObjectList<ModelNode> ChildList; 334 335 private: 336 ModelNode* fParent; 337 ValueNodeChild* fNodeChild; 338 Variable* fVariable; 339 Value* fValue; 340 ValueHandler* fValueHandler; 341 TableCellValueRenderer* fTableCellRenderer; 342 BMessage fLastRendererSettings; 343 Type* fCastedType; 344 ChildList fChildren; 345 TypeComponentPath* fComponentPath; 346 bool fIsPresentationNode; 347 bool fHidden; 348 349 public: 350 ModelNode* fNext; 351 }; 352 353 354 // #pragma mark - VariableValueColumn 355 356 357 class VariablesView::VariableValueColumn : public StringTableColumn { 358 public: 359 VariableValueColumn(int32 modelIndex, const char* title, float width, 360 float minWidth, float maxWidth, uint32 truncate = B_TRUNCATE_MIDDLE, 361 alignment align = B_ALIGN_RIGHT) 362 : 363 StringTableColumn(modelIndex, title, width, minWidth, maxWidth, 364 truncate, align) 365 { 366 } 367 368 protected: 369 void DrawValue(const BVariant& value, BRect rect, BView* targetView) 370 { 371 // draw the node's value with the designated renderer 372 if (value.Type() == VALUE_NODE_TYPE) { 373 ModelNode* node = dynamic_cast<ModelNode*>(value.ToReferenceable()); 374 if (node != NULL && node->GetValue() != NULL 375 && node->TableCellRenderer() != NULL) { 376 node->TableCellRenderer()->RenderValue(node->GetValue(), rect, 377 targetView); 378 return; 379 } 380 } else if (value.Type() == B_STRING_TYPE) { 381 fField.SetString(value.ToString()); 382 } else { 383 // fall back to drawing an empty string 384 fField.SetString(""); 385 } 386 fField.SetWidth(Width()); 387 fColumn.DrawField(&fField, rect, targetView); 388 } 389 390 float GetPreferredWidth(const BVariant& value, BView* targetView) const 391 { 392 // get the preferred width from the node's designated renderer 393 if (value.Type() == VALUE_NODE_TYPE) { 394 ModelNode* node = dynamic_cast<ModelNode*>(value.ToReferenceable()); 395 if (node != NULL && node->GetValue() != NULL 396 && node->TableCellRenderer() != NULL) { 397 return node->TableCellRenderer()->PreferredValueWidth( 398 node->GetValue(), targetView); 399 } 400 } 401 402 return fColumn.BTitledColumn::GetPreferredWidth(NULL, targetView); 403 } 404 405 virtual BField* PrepareField(const BVariant& _value) const 406 { 407 return NULL; 408 } 409 }; 410 411 412 // #pragma mark - VariableTableModel 413 414 415 class VariablesView::VariableTableModel : public TreeTableModel, 416 public TreeTableToolTipProvider { 417 public: 418 VariableTableModel(); 419 ~VariableTableModel(); 420 421 status_t Init(); 422 423 void SetContainerListener( 424 ContainerListener* listener); 425 426 void SetStackFrame(Thread* thread, 427 StackFrame* stackFrame); 428 429 void ValueNodeChanged(ValueNodeChild* nodeChild, 430 ValueNode* oldNode, ValueNode* newNode); 431 void ValueNodeChildrenCreated(ValueNode* node); 432 void ValueNodeChildrenDeleted(ValueNode* node); 433 void ValueNodeValueChanged(ValueNode* node); 434 435 virtual int32 CountColumns() const; 436 virtual void* Root() const; 437 virtual int32 CountChildren(void* parent) const; 438 virtual void* ChildAt(void* parent, int32 index) const; 439 virtual bool GetValueAt(void* object, int32 columnIndex, 440 BVariant& _value); 441 442 bool GetTreePath(ModelNode* node, 443 TreeTablePath& _path) const; 444 445 void NodeExpanded(ModelNode* node); 446 447 void NotifyNodeChanged(ModelNode* node); 448 void NotifyNodeHidden(ModelNode* node); 449 450 virtual bool GetToolTipForTablePath( 451 const TreeTablePath& path, 452 int32 columnIndex, BToolTip** _tip); 453 454 private: 455 struct NodeHashDefinition { 456 typedef ValueNodeChild* KeyType; 457 typedef ModelNode ValueType; 458 459 size_t HashKey(const ValueNodeChild* key) const 460 { 461 return (size_t)key; 462 } 463 464 size_t Hash(const ModelNode* value) const 465 { 466 return HashKey(value->NodeChild()); 467 } 468 469 bool Compare(const ValueNodeChild* key, 470 const ModelNode* value) const 471 { 472 return value->NodeChild() == key; 473 } 474 475 ModelNode*& GetLink(ModelNode* value) const 476 { 477 return value->fNext; 478 } 479 }; 480 481 typedef BObjectList<ModelNode> NodeList; 482 typedef BOpenHashTable<NodeHashDefinition> NodeTable; 483 484 private: 485 // container must be locked 486 487 status_t _AddNode(Variable* variable, ModelNode* parent, 488 ValueNodeChild* nodeChild, 489 bool isPresentationNode = false, 490 bool isOnlyChild = false); 491 492 private: 493 Thread* fThread; 494 ValueNodeManager* fNodeManager; 495 ContainerListener* fContainerListener; 496 NodeList fNodes; 497 NodeTable fNodeTable; 498 }; 499 500 501 class VariablesView::ContextMenu : public BPopUpMenu { 502 public: 503 ContextMenu(const BMessenger& parent, const char* name) 504 : BPopUpMenu(name, false, false), 505 fParent(parent) 506 { 507 } 508 509 virtual void Hide() 510 { 511 BPopUpMenu::Hide(); 512 513 BMessage message(MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE); 514 message.AddPointer("menu", this); 515 fParent.SendMessage(&message); 516 } 517 518 private: 519 BMessenger fParent; 520 }; 521 522 523 // #pragma mark - TableCellContextMenuTracker 524 525 526 class VariablesView::TableCellContextMenuTracker : public BReferenceable, 527 Settings::Listener { 528 public: 529 TableCellContextMenuTracker(ModelNode* node, BLooper* parentLooper, 530 const BMessenger& parent) 531 : 532 fNode(node), 533 fParentLooper(parentLooper), 534 fParent(parent), 535 fRendererSettings(NULL), 536 fRendererSettingsMenu(NULL), 537 fRendererMenuAdded(false), 538 fMenuPreparedToShow(false) 539 { 540 fNode->AcquireReference(); 541 } 542 543 ~TableCellContextMenuTracker() 544 { 545 FinishMenu(true); 546 547 if (fRendererSettingsMenu != NULL) 548 fRendererSettingsMenu->ReleaseReference(); 549 550 if (fRendererSettings != NULL) 551 fRendererSettings->ReleaseReference(); 552 553 fNode->ReleaseReference(); 554 } 555 556 status_t Init(Settings* rendererSettings, 557 SettingsMenu* rendererSettingsMenu, 558 ContextActionList* preSettingsActions = NULL, 559 ContextActionList* postSettingsActions = NULL) 560 { 561 if (rendererSettings == NULL && preSettingsActions == NULL 562 && postSettingsActions == NULL) { 563 return B_BAD_VALUE; 564 } 565 566 if (rendererSettings != NULL) { 567 fRendererSettings = rendererSettings; 568 fRendererSettings->AcquireReference(); 569 570 571 fRendererSettingsMenu = rendererSettingsMenu; 572 fRendererSettingsMenu->AcquireReference(); 573 } 574 575 fContextMenu = new(std::nothrow) ContextMenu(fParent, 576 "table cell settings popup"); 577 if (fContextMenu == NULL) 578 return B_NO_MEMORY; 579 580 status_t error = B_OK; 581 if (preSettingsActions != NULL 582 && preSettingsActions->CountItems() > 0) { 583 error = _AddActionItems(preSettingsActions); 584 if (error != B_OK) 585 return error; 586 587 if (fRendererSettingsMenu != NULL || postSettingsActions != NULL) 588 fContextMenu->AddSeparatorItem(); 589 } 590 591 if (fRendererSettingsMenu != NULL) { 592 error = fRendererSettingsMenu->AddToMenu(fContextMenu, 593 fContextMenu->CountItems()); 594 if (error != B_OK) 595 return error; 596 597 if (postSettingsActions != NULL) 598 fContextMenu->AddSeparatorItem(); 599 } 600 601 if (postSettingsActions != NULL) { 602 error = _AddActionItems(postSettingsActions); 603 if (error != B_OK) 604 return error; 605 606 } 607 608 if (fRendererSettings != NULL) { 609 AutoLocker<Settings> settingsLocker(fRendererSettings); 610 fRendererSettings->AddListener(this); 611 fRendererMenuAdded = true; 612 } 613 614 return B_OK; 615 } 616 617 void ShowMenu(BPoint screenWhere) 618 { 619 if (fRendererMenuAdded) 620 fRendererSettingsMenu->PrepareToShow(fParentLooper); 621 622 for (int32 i = 0; i < fContextMenu->CountItems(); i++) { 623 ActionMenuItem* item = dynamic_cast<ActionMenuItem*>( 624 fContextMenu->ItemAt(i)); 625 if (item != NULL) 626 item->PrepareToShow(fParentLooper, fParent.Target(NULL)); 627 } 628 629 fMenuPreparedToShow = true; 630 631 BRect mouseRect(screenWhere, screenWhere); 632 mouseRect.InsetBy(-4.0, -4.0); 633 fContextMenu->Go(screenWhere, true, false, mouseRect, true); 634 } 635 636 bool FinishMenu(bool force) 637 { 638 bool stillActive = false; 639 640 if (fMenuPreparedToShow) { 641 if (fRendererMenuAdded) 642 stillActive = fRendererSettingsMenu->Finish(fParentLooper, 643 force); 644 for (int32 i = 0; i < fContextMenu->CountItems(); i++) { 645 ActionMenuItem* item = dynamic_cast<ActionMenuItem*>( 646 fContextMenu->ItemAt(i)); 647 if (item != NULL) { 648 stillActive |= item->Finish(fParentLooper, 649 fParent.Target(NULL), force); 650 } 651 } 652 653 fMenuPreparedToShow = stillActive; 654 } 655 656 if (fRendererMenuAdded) { 657 fRendererSettingsMenu->RemoveFromMenu(); 658 fRendererSettings->RemoveListener(this); 659 fRendererMenuAdded = false; 660 } 661 662 if (fContextMenu != NULL) { 663 delete fContextMenu; 664 fContextMenu = NULL; 665 } 666 667 return stillActive; 668 } 669 670 private: 671 // Settings::Listener 672 673 virtual void SettingValueChanged(Setting* setting) 674 { 675 BMessage message(MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED); 676 fNode->AcquireReference(); 677 if (message.AddPointer("node", fNode) != B_OK 678 || fParent.SendMessage(&message) != B_OK) { 679 fNode->ReleaseReference(); 680 } 681 } 682 683 status_t _AddActionItems(ContextActionList* actions) 684 { 685 if (fContextMenu == NULL) 686 return B_BAD_VALUE; 687 688 int32 index = fContextMenu->CountItems(); 689 for (int32 i = 0; ActionMenuItem* item = actions->ItemAt(i); i++) { 690 if (!fContextMenu->AddItem(item, index + i)) { 691 for (i--; i >= 0; i--) 692 fContextMenu->RemoveItem(fContextMenu->ItemAt(index + i)); 693 694 return B_NO_MEMORY; 695 } 696 } 697 698 return B_OK; 699 } 700 701 private: 702 ModelNode* fNode; 703 BLooper* fParentLooper; 704 BMessenger fParent; 705 ContextMenu* fContextMenu; 706 Settings* fRendererSettings; 707 SettingsMenu* fRendererSettingsMenu; 708 bool fRendererMenuAdded; 709 bool fMenuPreparedToShow; 710 }; 711 712 713 // #pragma mark - ContainerListener 714 715 716 VariablesView::ContainerListener::ContainerListener(BHandler* indirectTarget) 717 : 718 fIndirectTarget(indirectTarget), 719 fModel(NULL) 720 { 721 } 722 723 724 void 725 VariablesView::ContainerListener::SetModel(VariableTableModel* model) 726 { 727 fModel = model; 728 } 729 730 731 void 732 VariablesView::ContainerListener::ValueNodeChanged(ValueNodeChild* nodeChild, 733 ValueNode* oldNode, ValueNode* newNode) 734 { 735 // If the looper is already locked, invoke the model's hook synchronously. 736 if (fIndirectTarget->Looper()->IsLocked()) { 737 fModel->ValueNodeChanged(nodeChild, oldNode, newNode); 738 return; 739 } 740 741 // looper not locked yet -- call asynchronously to avoid reverse locking 742 // order 743 BReference<ValueNodeChild> nodeChildReference(nodeChild); 744 BReference<ValueNode> oldNodeReference(oldNode); 745 BReference<ValueNode> newNodeReference(newNode); 746 747 BMessage message(MSG_VALUE_NODE_CHANGED); 748 if (message.AddPointer("nodeChild", nodeChild) == B_OK 749 && message.AddPointer("oldNode", oldNode) == B_OK 750 && message.AddPointer("newNode", newNode) == B_OK 751 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 752 == B_OK) { 753 nodeChildReference.Detach(); 754 oldNodeReference.Detach(); 755 newNodeReference.Detach(); 756 } 757 } 758 759 760 void 761 VariablesView::ContainerListener::ValueNodeChildrenCreated(ValueNode* node) 762 { 763 // If the looper is already locked, invoke the model's hook synchronously. 764 if (fIndirectTarget->Looper()->IsLocked()) { 765 fModel->ValueNodeChildrenCreated(node); 766 return; 767 } 768 769 // looper not locked yet -- call asynchronously to avoid reverse locking 770 // order 771 BReference<ValueNode> nodeReference(node); 772 773 BMessage message(MSG_VALUE_NODE_CHILDREN_CREATED); 774 if (message.AddPointer("node", node) == B_OK 775 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 776 == B_OK) { 777 nodeReference.Detach(); 778 } 779 } 780 781 782 void 783 VariablesView::ContainerListener::ValueNodeChildrenDeleted(ValueNode* node) 784 { 785 // If the looper is already locked, invoke the model's hook synchronously. 786 if (fIndirectTarget->Looper()->IsLocked()) { 787 fModel->ValueNodeChildrenDeleted(node); 788 return; 789 } 790 791 // looper not locked yet -- call asynchronously to avoid reverse locking 792 // order 793 BReference<ValueNode> nodeReference(node); 794 795 BMessage message(MSG_VALUE_NODE_CHILDREN_DELETED); 796 if (message.AddPointer("node", node) == B_OK 797 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 798 == B_OK) { 799 nodeReference.Detach(); 800 } 801 } 802 803 804 void 805 VariablesView::ContainerListener::ValueNodeValueChanged(ValueNode* node) 806 { 807 // If the looper is already locked, invoke the model's hook synchronously. 808 if (fIndirectTarget->Looper()->IsLocked()) { 809 fModel->ValueNodeValueChanged(node); 810 return; 811 } 812 813 // looper not locked yet -- call asynchronously to avoid reverse locking 814 // order 815 BReference<ValueNode> nodeReference(node); 816 817 BMessage message(MSG_VALUE_NODE_VALUE_CHANGED); 818 if (message.AddPointer("node", node) == B_OK 819 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 820 == B_OK) { 821 nodeReference.Detach(); 822 } 823 } 824 825 826 void 827 VariablesView::ContainerListener::ModelNodeHidden(ModelNode* node) 828 { 829 BReference<ModelNode> nodeReference(node); 830 831 BMessage message(MSG_MODEL_NODE_HIDDEN); 832 if (message.AddPointer("node", node) == B_OK 833 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 834 == B_OK) { 835 nodeReference.Detach(); 836 } 837 } 838 839 840 void 841 VariablesView::ContainerListener::ModelNodeValueRequested(ModelNode* node) 842 { 843 BReference<ModelNode> nodeReference(node); 844 845 BMessage message(MSG_VALUE_NODE_NEEDS_VALUE); 846 if (message.AddPointer("node", node) == B_OK 847 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 848 == B_OK) { 849 nodeReference.Detach(); 850 } 851 } 852 853 854 void 855 VariablesView::ContainerListener::ModelNodeRestoreViewStateRequested( 856 ModelNode* node) 857 { 858 BReference<ModelNode> nodeReference(node); 859 860 BMessage message(MSG_RESTORE_PARTIAL_VIEW_STATE); 861 if (message.AddPointer("node", node) == B_OK 862 && fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget) 863 == B_OK) { 864 nodeReference.Detach(); 865 } 866 } 867 868 869 // #pragma mark - VariableTableModel 870 871 872 VariablesView::VariableTableModel::VariableTableModel() 873 : 874 fThread(NULL), 875 fNodeManager(NULL), 876 fContainerListener(NULL), 877 fNodeTable() 878 { 879 } 880 881 882 VariablesView::VariableTableModel::~VariableTableModel() 883 { 884 if (fNodeManager != NULL) 885 fNodeManager->ReleaseReference(); 886 } 887 888 889 status_t 890 VariablesView::VariableTableModel::Init() 891 { 892 fNodeManager = new(std::nothrow) ValueNodeManager(); 893 if (fNodeManager == NULL) 894 return B_NO_MEMORY; 895 896 return fNodeTable.Init(); 897 } 898 899 900 void 901 VariablesView::VariableTableModel::SetContainerListener( 902 ContainerListener* listener) 903 { 904 if (listener == fContainerListener) 905 return; 906 907 if (fContainerListener != NULL) { 908 if (fNodeManager != NULL) 909 fNodeManager->RemoveListener(fContainerListener); 910 911 fContainerListener->SetModel(NULL); 912 } 913 914 fContainerListener = listener; 915 916 if (fContainerListener != NULL) { 917 fContainerListener->SetModel(this); 918 919 if (fNodeManager != NULL) 920 fNodeManager->AddListener(fContainerListener); 921 } 922 } 923 924 925 void 926 VariablesView::VariableTableModel::SetStackFrame(Thread* thread, 927 StackFrame* stackFrame) 928 { 929 fThread = thread; 930 931 fNodeManager->SetStackFrame(thread, stackFrame); 932 933 int32 count = fNodes.CountItems(); 934 fNodeTable.Clear(true); 935 936 if (!fNodes.IsEmpty()) { 937 for (int32 i = 0; i < count; i++) 938 fNodes.ItemAt(i)->ReleaseReference(); 939 fNodes.MakeEmpty(); 940 } 941 942 NotifyNodesRemoved(TreeTablePath(), 0, count); 943 944 if (stackFrame == NULL) 945 return; 946 947 ValueNodeContainer* container = fNodeManager->GetContainer(); 948 AutoLocker<ValueNodeContainer> containerLocker(container); 949 950 for (int32 i = 0; i < container->CountChildren(); i++) { 951 VariableValueNodeChild* child = dynamic_cast<VariableValueNodeChild *>( 952 container->ChildAt(i)); 953 _AddNode(child->GetVariable(), NULL, child); 954 // top level nodes get their children added immediately 955 // so those won't invoke our callback hook. Add them directly here. 956 ValueNodeChildrenCreated(child->Node()); 957 } 958 } 959 960 961 void 962 VariablesView::VariableTableModel::ValueNodeChanged(ValueNodeChild* nodeChild, 963 ValueNode* oldNode, ValueNode* newNode) 964 { 965 AutoLocker<ValueNodeContainer> containerLocker( 966 fNodeManager->GetContainer()); 967 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 968 if (modelNode == NULL) 969 return; 970 971 if (oldNode != NULL) { 972 ValueNodeChildrenDeleted(oldNode); 973 NotifyNodeChanged(modelNode); 974 } 975 } 976 977 978 void 979 VariablesView::VariableTableModel::ValueNodeChildrenCreated( 980 ValueNode* valueNode) 981 { 982 AutoLocker<ValueNodeContainer> containerLocker( 983 fNodeManager->GetContainer()); 984 985 // check whether we know the node 986 ValueNodeChild* nodeChild = valueNode->NodeChild(); 987 if (nodeChild == NULL) 988 return; 989 990 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 991 if (modelNode == NULL) 992 return; 993 994 // Iterate through the children and create model nodes for the ones we 995 // don't know yet. 996 int32 childCount = valueNode->CountChildren(); 997 for (int32 i = 0; i < childCount; i++) { 998 ValueNodeChild* child = valueNode->ChildAt(i); 999 if (fNodeTable.Lookup(child) == NULL) { 1000 _AddNode(modelNode->GetVariable(), modelNode, child, 1001 child->IsInternal(), childCount == 1); 1002 } 1003 1004 ModelNode* childNode = fNodeTable.Lookup(child); 1005 if (childNode != NULL) 1006 fContainerListener->ModelNodeValueRequested(childNode); 1007 } 1008 1009 if (valueNode->ChildCreationNeedsValue()) 1010 fContainerListener->ModelNodeRestoreViewStateRequested(modelNode); 1011 } 1012 1013 1014 void 1015 VariablesView::VariableTableModel::ValueNodeChildrenDeleted(ValueNode* node) 1016 { 1017 AutoLocker<ValueNodeContainer> containerLocker( 1018 fNodeManager->GetContainer()); 1019 1020 // check whether we know the node 1021 ValueNodeChild* nodeChild = node->NodeChild(); 1022 if (nodeChild == NULL) 1023 return; 1024 1025 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1026 if (modelNode == NULL) 1027 return; 1028 1029 // in the case of an address node with a hidden child, 1030 // we want to send removal notifications for the children 1031 // instead. 1032 BReference<ModelNode> hiddenChild; 1033 if (modelNode->CountChildren() == 1 1034 && modelNode->ChildAt(0)->IsHidden()) { 1035 hiddenChild.SetTo(modelNode->ChildAt(0)); 1036 modelNode->RemoveChild(hiddenChild); 1037 modelNode = hiddenChild; 1038 fNodeTable.Remove(hiddenChild); 1039 } 1040 1041 for (int32 i = modelNode->CountChildren() - 1; i >= 0 ; i--) { 1042 BReference<ModelNode> childNode = modelNode->ChildAt(i); 1043 // recursively remove the current node's child hierarchy. 1044 if (childNode->CountChildren() != 0) 1045 ValueNodeChildrenDeleted(childNode->NodeChild()->Node()); 1046 1047 TreeTablePath treePath; 1048 if (GetTreePath(childNode, treePath)) { 1049 int32 index = treePath.RemoveLastComponent(); 1050 NotifyNodesRemoved(treePath, index, 1); 1051 } 1052 modelNode->RemoveChild(childNode); 1053 fNodeTable.Remove(childNode); 1054 } 1055 } 1056 1057 1058 void 1059 VariablesView::VariableTableModel::ValueNodeValueChanged(ValueNode* valueNode) 1060 { 1061 AutoLocker<ValueNodeContainer> containerLocker( 1062 fNodeManager->GetContainer()); 1063 1064 // check whether we know the node 1065 ValueNodeChild* nodeChild = valueNode->NodeChild(); 1066 if (nodeChild == NULL) 1067 return; 1068 1069 ModelNode* modelNode = fNodeTable.Lookup(nodeChild); 1070 if (modelNode == NULL) 1071 return; 1072 1073 // check whether the value actually changed 1074 Value* value = valueNode->GetValue(); 1075 if (value == modelNode->GetValue()) 1076 return; 1077 1078 // get a value handler 1079 ValueHandler* valueHandler; 1080 status_t error = ValueHandlerRoster::Default()->FindValueHandler(value, 1081 valueHandler); 1082 if (error != B_OK) 1083 return; 1084 BReference<ValueHandler> handlerReference(valueHandler, true); 1085 1086 // create a table cell renderer for the value 1087 TableCellValueRenderer* renderer = NULL; 1088 error = valueHandler->GetTableCellValueRenderer(value, renderer); 1089 if (error != B_OK) 1090 return; 1091 1092 // set value/handler/renderer 1093 modelNode->SetValue(value); 1094 modelNode->SetValueHandler(valueHandler); 1095 modelNode->SetTableCellRenderer(renderer); 1096 1097 // we have to restore renderer settings here since until this point 1098 // we don't yet know what renderer is in use. 1099 if (renderer != NULL) { 1100 Settings* settings = renderer->GetSettings(); 1101 if (settings != NULL) 1102 settings->RestoreValues(modelNode->GetLastRendererSettings()); 1103 } 1104 1105 // notify table model listeners 1106 NotifyNodeChanged(modelNode); 1107 } 1108 1109 1110 int32 1111 VariablesView::VariableTableModel::CountColumns() const 1112 { 1113 return 3; 1114 } 1115 1116 1117 void* 1118 VariablesView::VariableTableModel::Root() const 1119 { 1120 return (void*)this; 1121 } 1122 1123 1124 int32 1125 VariablesView::VariableTableModel::CountChildren(void* parent) const 1126 { 1127 if (parent == this) 1128 return fNodes.CountItems(); 1129 1130 // If the node only has a hidden child, pretend the node directly has the 1131 // child's children. 1132 ModelNode* modelNode = (ModelNode*)parent; 1133 int32 childCount = modelNode->CountChildren(); 1134 if (childCount == 1) { 1135 ModelNode* child = modelNode->ChildAt(0); 1136 if (child->IsHidden()) 1137 return child->CountChildren(); 1138 } 1139 1140 return childCount; 1141 } 1142 1143 1144 void* 1145 VariablesView::VariableTableModel::ChildAt(void* parent, int32 index) const 1146 { 1147 if (parent == this) 1148 return fNodes.ItemAt(index); 1149 1150 // If the node only has a hidden child, pretend the node directly has the 1151 // child's children. 1152 ModelNode* modelNode = (ModelNode*)parent; 1153 int32 childCount = modelNode->CountChildren(); 1154 if (childCount == 1) { 1155 ModelNode* child = modelNode->ChildAt(0); 1156 if (child->IsHidden()) 1157 return child->ChildAt(index); 1158 } 1159 1160 return modelNode->ChildAt(index); 1161 } 1162 1163 1164 bool 1165 VariablesView::VariableTableModel::GetValueAt(void* object, int32 columnIndex, 1166 BVariant& _value) 1167 { 1168 ModelNode* node = (ModelNode*)object; 1169 1170 switch (columnIndex) { 1171 case 0: 1172 _value.SetTo(node->Name(), B_VARIANT_DONT_COPY_DATA); 1173 return true; 1174 case 1: 1175 if (node->GetValue() == NULL) { 1176 ValueLocation* location = node->NodeChild()->Location(); 1177 if (location == NULL) 1178 return false; 1179 1180 Type* nodeChildRawType = node->NodeChild()->Node()->GetType() 1181 ->ResolveRawType(false); 1182 if (nodeChildRawType->Kind() == TYPE_COMPOUND) 1183 { 1184 if (location->CountPieces() > 1) 1185 return false; 1186 1187 BString data; 1188 ValuePieceLocation piece = location->PieceAt(0); 1189 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 1190 return false; 1191 1192 data.SetToFormat("[@ %#" B_PRIx64 "]", piece.address); 1193 _value.SetTo(data); 1194 return true; 1195 } 1196 return false; 1197 } 1198 1199 _value.SetTo(node, VALUE_NODE_TYPE); 1200 return true; 1201 case 2: 1202 { 1203 Type* type = node->GetType(); 1204 if (type == NULL) 1205 return false; 1206 1207 _value.SetTo(type->Name(), B_VARIANT_DONT_COPY_DATA); 1208 return true; 1209 } 1210 default: 1211 return false; 1212 } 1213 } 1214 1215 1216 void 1217 VariablesView::VariableTableModel::NodeExpanded(ModelNode* node) 1218 { 1219 AutoLocker<ValueNodeContainer> containerLocker( 1220 fNodeManager->GetContainer()); 1221 // add children of all children 1222 1223 // If the node only has a hidden child, add the child's children instead. 1224 if (node->CountChildren() == 1) { 1225 ModelNode* child = node->ChildAt(0); 1226 if (child->IsHidden()) 1227 node = child; 1228 } 1229 1230 // add the children 1231 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) 1232 fNodeManager->AddChildNodes(child->NodeChild()); 1233 } 1234 1235 1236 void 1237 VariablesView::VariableTableModel::NotifyNodeChanged(ModelNode* node) 1238 { 1239 if (!node->IsHidden()) { 1240 TreeTablePath treePath; 1241 if (GetTreePath(node, treePath)) { 1242 int32 index = treePath.RemoveLastComponent(); 1243 NotifyNodesChanged(treePath, index, 1); 1244 } 1245 } 1246 } 1247 1248 1249 void 1250 VariablesView::VariableTableModel::NotifyNodeHidden(ModelNode* node) 1251 { 1252 fContainerListener->ModelNodeHidden(node); 1253 } 1254 1255 1256 bool 1257 VariablesView::VariableTableModel::GetToolTipForTablePath( 1258 const TreeTablePath& path, int32 columnIndex, BToolTip** _tip) 1259 { 1260 ModelNode* node = (ModelNode*)NodeForPath(path); 1261 if (node == NULL) 1262 return false; 1263 1264 if (node->NodeChild()->LocationResolutionState() != B_OK) 1265 return false; 1266 1267 BString tipData; 1268 switch (columnIndex) { 1269 case 0: 1270 { 1271 ValueLocation* location = node->NodeChild()->Location(); 1272 for (int32 i = 0; i < location->CountPieces(); i++) { 1273 ValuePieceLocation piece = location->PieceAt(i); 1274 BString pieceData; 1275 switch (piece.type) { 1276 case VALUE_PIECE_LOCATION_MEMORY: 1277 pieceData.SetToFormat("(%" B_PRId32 "): Address: %#" 1278 B_PRIx64 ", Size: %" B_PRId64 " bytes", i, 1279 piece.address, piece.size); 1280 break; 1281 case VALUE_PIECE_LOCATION_REGISTER: 1282 { 1283 Architecture* architecture = fThread->GetTeam() 1284 ->GetArchitecture(); 1285 pieceData.SetToFormat("(%" B_PRId32 "): Register (%s)", 1286 i, architecture->Registers()[piece.reg].Name()); 1287 break; 1288 } 1289 default: 1290 break; 1291 } 1292 1293 tipData += pieceData; 1294 if (i < location->CountPieces() - 1) 1295 tipData += "\n"; 1296 } 1297 break; 1298 } 1299 case 1: 1300 { 1301 Value* value = node->GetValue(); 1302 if (value != NULL) 1303 value->ToString(tipData); 1304 1305 break; 1306 } 1307 default: 1308 break; 1309 } 1310 1311 if (tipData.IsEmpty()) 1312 return false; 1313 1314 *_tip = new(std::nothrow) BTextToolTip(tipData); 1315 if (*_tip == NULL) 1316 return false; 1317 1318 return true; 1319 } 1320 1321 1322 status_t 1323 VariablesView::VariableTableModel::_AddNode(Variable* variable, 1324 ModelNode* parent, ValueNodeChild* nodeChild, bool isPresentationNode, 1325 bool isOnlyChild) 1326 { 1327 // Don't create nodes for unspecified types -- we can't get/show their 1328 // value anyway. 1329 Type* nodeChildRawType = nodeChild->GetType()->ResolveRawType(false); 1330 if (nodeChildRawType->Kind() == TYPE_UNSPECIFIED) 1331 return B_OK; 1332 1333 ModelNode* node = new(std::nothrow) ModelNode(parent, variable, nodeChild, 1334 isPresentationNode); 1335 BReference<ModelNode> nodeReference(node, true); 1336 if (node == NULL || node->Init() != B_OK) 1337 return B_NO_MEMORY; 1338 1339 int32 childIndex; 1340 1341 if (parent != NULL) { 1342 childIndex = parent->CountChildren(); 1343 1344 if (!parent->AddChild(node)) 1345 return B_NO_MEMORY; 1346 // the parent has a reference, now 1347 } else { 1348 childIndex = fNodes.CountItems(); 1349 1350 if (!fNodes.AddItem(node)) 1351 return B_NO_MEMORY; 1352 nodeReference.Detach(); 1353 // the fNodes list has a reference, now 1354 } 1355 1356 fNodeTable.Insert(node); 1357 1358 // if an address type node has only a single child, and that child 1359 // is a compound type, mark it hidden 1360 if (isOnlyChild && parent != NULL) { 1361 ValueNode* parentValueNode = parent->NodeChild()->Node(); 1362 if (parentValueNode != NULL) { 1363 if (parentValueNode->GetType()->ResolveRawType(false)->Kind() 1364 == TYPE_ADDRESS) { 1365 type_kind childKind = nodeChildRawType->Kind(); 1366 if (childKind == TYPE_COMPOUND || childKind == TYPE_ARRAY) { 1367 node->SetHidden(true); 1368 1369 // we need to tell the listener about nodes like this so 1370 // any necessary actions can be taken for them (i.e. value 1371 // resolution), since they're otherwise invisible to 1372 // outsiders. 1373 NotifyNodeHidden(node); 1374 } 1375 } 1376 } 1377 } 1378 1379 // notify table model listeners 1380 if (!node->IsHidden()) { 1381 TreeTablePath path; 1382 if (parent == NULL || GetTreePath(parent, path)) 1383 NotifyNodesAdded(path, childIndex, 1); 1384 } 1385 1386 // if the node is hidden, add its children 1387 if (node->IsHidden()) 1388 fNodeManager->AddChildNodes(nodeChild); 1389 1390 return B_OK; 1391 } 1392 1393 1394 bool 1395 VariablesView::VariableTableModel::GetTreePath(ModelNode* node, 1396 TreeTablePath& _path) const 1397 { 1398 // recurse, if the node has a parent 1399 if (ModelNode* parent = node->Parent()) { 1400 if (!GetTreePath(parent, _path)) 1401 return false; 1402 1403 if (node->IsHidden()) 1404 return true; 1405 1406 return _path.AddComponent(parent->IndexOf(node)); 1407 } 1408 1409 // no parent -- get the index and start the path 1410 int32 index = fNodes.IndexOf(node); 1411 _path.Clear(); 1412 return index >= 0 && _path.AddComponent(index); 1413 } 1414 1415 1416 // #pragma mark - VariablesView 1417 1418 1419 VariablesView::VariablesView(Listener* listener) 1420 : 1421 BGroupView(B_VERTICAL), 1422 fThread(NULL), 1423 fStackFrame(NULL), 1424 fVariableTable(NULL), 1425 fVariableTableModel(NULL), 1426 fContainerListener(NULL), 1427 fPreviousViewState(NULL), 1428 fViewStateHistory(NULL), 1429 fTableCellContextMenuTracker(NULL), 1430 fFrameClearPending(false), 1431 fListener(listener) 1432 { 1433 SetName("Variables"); 1434 } 1435 1436 1437 VariablesView::~VariablesView() 1438 { 1439 SetStackFrame(NULL, NULL); 1440 fVariableTable->SetTreeTableModel(NULL); 1441 1442 if (fPreviousViewState != NULL) 1443 fPreviousViewState->ReleaseReference(); 1444 delete fViewStateHistory; 1445 1446 if (fVariableTableModel != NULL) { 1447 fVariableTableModel->SetContainerListener(NULL); 1448 delete fVariableTableModel; 1449 } 1450 1451 delete fContainerListener; 1452 } 1453 1454 1455 /*static*/ VariablesView* 1456 VariablesView::Create(Listener* listener) 1457 { 1458 VariablesView* self = new VariablesView(listener); 1459 1460 try { 1461 self->_Init(); 1462 } catch (...) { 1463 delete self; 1464 throw; 1465 } 1466 1467 return self; 1468 } 1469 1470 1471 void 1472 VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame) 1473 { 1474 fFrameClearPending = false; 1475 1476 if (thread == fThread && stackFrame == fStackFrame) 1477 return; 1478 1479 _SaveViewState(); 1480 1481 _FinishContextMenu(true); 1482 1483 if (fThread != NULL) 1484 fThread->ReleaseReference(); 1485 if (fStackFrame != NULL) 1486 fStackFrame->ReleaseReference(); 1487 1488 fThread = thread; 1489 fStackFrame = stackFrame; 1490 1491 if (fThread != NULL) 1492 fThread->AcquireReference(); 1493 if (fStackFrame != NULL) 1494 fStackFrame->AcquireReference(); 1495 1496 fVariableTableModel->SetStackFrame(fThread, fStackFrame); 1497 1498 // request loading the parameter and variable values 1499 if (fThread != NULL && fStackFrame != NULL) { 1500 AutoLocker<Team> locker(fThread->GetTeam()); 1501 1502 void* root = fVariableTableModel->Root(); 1503 int32 count = fVariableTableModel->CountChildren(root); 1504 for (int32 i = 0; i < count; i++) { 1505 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i); 1506 _RequestNodeValue(node); 1507 } 1508 } 1509 1510 _RestoreViewState(); 1511 } 1512 1513 1514 void 1515 VariablesView::MessageReceived(BMessage* message) 1516 { 1517 switch (message->what) { 1518 case MSG_SHOW_INSPECTOR_WINDOW: 1519 { 1520 // TODO: it'd probably be more ideal to extend the context 1521 // action mechanism to allow one to specify an explicit 1522 // target for each action rather than them all defaulting 1523 // to targetting here. 1524 Looper()->PostMessage(message); 1525 break; 1526 } 1527 case MSG_SHOW_TYPECAST_NODE_PROMPT: 1528 { 1529 BMessage* promptMessage = new(std::nothrow) BMessage( 1530 MSG_TYPECAST_NODE); 1531 1532 if (promptMessage == NULL) 1533 return; 1534 1535 ObjectDeleter<BMessage> messageDeleter(promptMessage); 1536 promptMessage->AddPointer("node", fVariableTable 1537 ->SelectionModel()->NodeAt(0)); 1538 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 1539 "Specify Type", "Type: ", NULL, BMessenger(this), 1540 promptMessage); 1541 if (promptWindow == NULL) 1542 return; 1543 1544 messageDeleter.Detach(); 1545 promptWindow->CenterOnScreen(); 1546 promptWindow->Show(); 1547 break; 1548 } 1549 case MSG_TYPECAST_NODE: 1550 { 1551 ModelNode* node = NULL; 1552 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 1553 != B_OK) { 1554 break; 1555 } 1556 1557 Type* type = NULL; 1558 BString typeExpression = message->FindString("text"); 1559 if (typeExpression.Length() == 0) 1560 break; 1561 1562 FileSourceCode* code = fStackFrame->Function()->GetFunction() 1563 ->GetSourceCode(); 1564 if (code == NULL) 1565 break; 1566 1567 SourceLanguage* language = code->GetSourceLanguage(); 1568 if (language == NULL) 1569 break; 1570 1571 if (language->ParseTypeExpression(typeExpression, 1572 fThread->GetTeam()->DebugInfo(), type) != B_OK) { 1573 BString errorMessage; 1574 errorMessage.SetToFormat("Failed to resolve type %s", 1575 typeExpression.String()); 1576 BAlert* alert = new(std::nothrow) BAlert("Error", 1577 errorMessage.String(), "Close"); 1578 if (alert != NULL) 1579 alert->Go(); 1580 break; 1581 } 1582 1583 BReference<Type> typeRef(type, true); 1584 ValueNode* valueNode = NULL; 1585 if (TypeHandlerRoster::Default()->CreateValueNode( 1586 node->NodeChild(), type, valueNode) != B_OK) { 1587 break; 1588 } 1589 1590 typeRef.Detach(); 1591 node->NodeChild()->SetNode(valueNode); 1592 node->SetCastedType(type); 1593 fVariableTableModel->NotifyNodeChanged(node); 1594 break; 1595 } 1596 case MSG_TYPECAST_TO_ARRAY: 1597 { 1598 ModelNode* node = NULL; 1599 if (message->FindPointer("node", reinterpret_cast<void **>(&node)) 1600 != B_OK) { 1601 break; 1602 } 1603 1604 Type* baseType = dynamic_cast<AddressType*>(node->NodeChild() 1605 ->Node()->GetType())->BaseType(); 1606 ArrayType* arrayType = NULL; 1607 if (baseType->CreateDerivedArrayType(0, kMaxArrayElementCount, 1608 false, arrayType) != B_OK) { 1609 break; 1610 } 1611 1612 AddressType* addressType = NULL; 1613 BReference<Type> typeRef(arrayType, true); 1614 if (arrayType->CreateDerivedAddressType(DERIVED_TYPE_POINTER, 1615 addressType) != B_OK) { 1616 break; 1617 } 1618 1619 typeRef.Detach(); 1620 typeRef.SetTo(addressType, true); 1621 ValueNode* valueNode = NULL; 1622 if (TypeHandlerRoster::Default()->CreateValueNode( 1623 node->NodeChild(), addressType, valueNode) != B_OK) { 1624 break; 1625 } 1626 1627 typeRef.Detach(); 1628 node->NodeChild()->SetNode(valueNode); 1629 node->SetCastedType(addressType); 1630 fVariableTableModel->NotifyNodeChanged(node); 1631 break; 1632 } 1633 case MSG_SHOW_CONTAINER_RANGE_PROMPT: 1634 { 1635 ModelNode* node = (ModelNode*)fVariableTable 1636 ->SelectionModel()->NodeAt(0); 1637 int32 lowerBound, upperBound; 1638 ValueNode* valueNode = node->NodeChild()->Node(); 1639 if (!valueNode->IsRangedContainer()) { 1640 valueNode = node->ChildAt(0)->NodeChild()->Node(); 1641 if (!valueNode->IsRangedContainer()) 1642 break; 1643 } 1644 1645 bool fixedRange = valueNode->IsContainerRangeFixed(); 1646 if (valueNode->SupportedChildRange(lowerBound, upperBound) 1647 != B_OK) { 1648 break; 1649 } 1650 1651 BMessage* promptMessage = new(std::nothrow) BMessage( 1652 MSG_SET_CONTAINER_RANGE); 1653 if (promptMessage == NULL) 1654 break; 1655 1656 ObjectDeleter<BMessage> messageDeleter(promptMessage); 1657 promptMessage->AddPointer("node", node); 1658 promptMessage->AddBool("fixedRange", fixedRange); 1659 BString infoText; 1660 if (fixedRange) { 1661 infoText.SetToFormat("Allowed range: %" B_PRId32 1662 "-%" B_PRId32 ".", lowerBound, upperBound); 1663 } else { 1664 infoText.SetToFormat("Current range: %" B_PRId32 1665 "-%" B_PRId32 ".", lowerBound, upperBound); 1666 } 1667 1668 PromptWindow* promptWindow = new(std::nothrow) PromptWindow( 1669 "Set Range", "Range: ", infoText.String(), BMessenger(this), 1670 promptMessage); 1671 if (promptWindow == NULL) 1672 return; 1673 1674 messageDeleter.Detach(); 1675 promptWindow->CenterOnScreen(); 1676 promptWindow->Show(); 1677 break; 1678 } 1679 case MSG_SET_CONTAINER_RANGE: 1680 { 1681 ModelNode* node = (ModelNode*)fVariableTable 1682 ->SelectionModel()->NodeAt(0); 1683 int32 lowerBound, upperBound; 1684 ValueNode* valueNode = node->NodeChild()->Node(); 1685 if (!valueNode->IsRangedContainer()) 1686 valueNode = node->ChildAt(0)->NodeChild()->Node(); 1687 if (valueNode->SupportedChildRange(lowerBound, upperBound) != B_OK) 1688 break; 1689 1690 bool fixedRange = message->FindBool("fixedRange"); 1691 1692 BString rangeExpression = message->FindString("text"); 1693 if (rangeExpression.Length() == 0) 1694 break; 1695 1696 RangeList ranges; 1697 status_t result = UiUtils::ParseRangeExpression( 1698 rangeExpression, lowerBound, upperBound, fixedRange, ranges); 1699 if (result != B_OK) 1700 break; 1701 1702 valueNode->ClearChildren(); 1703 for (int32 i = 0; i < ranges.CountRanges(); i++) { 1704 const Range* range = ranges.RangeAt(i); 1705 result = valueNode->CreateChildrenInRange( 1706 range->lowerBound, range->upperBound); 1707 if (result != B_OK) 1708 break; 1709 } 1710 break; 1711 } 1712 case MSG_SHOW_WATCH_VARIABLE_PROMPT: 1713 { 1714 ModelNode* node = reinterpret_cast<ModelNode*>( 1715 fVariableTable->SelectionModel()->NodeAt(0)); 1716 ValueLocation* location = node->NodeChild()->Location(); 1717 ValuePieceLocation piece = location->PieceAt(0); 1718 if (piece.type != VALUE_PIECE_LOCATION_MEMORY) 1719 break; 1720 1721 BMessage looperMessage(*message); 1722 looperMessage.AddUInt64("address", piece.address); 1723 looperMessage.AddInt32("length", piece.size); 1724 looperMessage.AddUInt32("type", B_DATA_READ_WRITE_WATCHPOINT); 1725 Looper()->PostMessage(&looperMessage); 1726 break; 1727 } 1728 case MSG_VALUE_NODE_CHANGED: 1729 { 1730 ValueNodeChild* nodeChild; 1731 ValueNode* oldNode; 1732 ValueNode* newNode; 1733 if (message->FindPointer("nodeChild", (void**)&nodeChild) == B_OK 1734 && message->FindPointer("oldNode", (void**)&oldNode) == B_OK 1735 && message->FindPointer("newNode", (void**)&newNode) == B_OK) { 1736 BReference<ValueNodeChild> nodeChildReference(nodeChild, true); 1737 BReference<ValueNode> oldNodeReference(oldNode, true); 1738 BReference<ValueNode> newNodeReference(newNode, true); 1739 1740 fVariableTableModel->ValueNodeChanged(nodeChild, oldNode, 1741 newNode); 1742 } 1743 1744 break; 1745 } 1746 case MSG_VALUE_NODE_CHILDREN_CREATED: 1747 { 1748 ValueNode* node; 1749 if (message->FindPointer("node", (void**)&node) == B_OK) { 1750 BReference<ValueNode> newNodeReference(node, true); 1751 fVariableTableModel->ValueNodeChildrenCreated(node); 1752 } 1753 1754 break; 1755 } 1756 case MSG_VALUE_NODE_CHILDREN_DELETED: 1757 { 1758 ValueNode* node; 1759 if (message->FindPointer("node", (void**)&node) == B_OK) { 1760 BReference<ValueNode> newNodeReference(node, true); 1761 fVariableTableModel->ValueNodeChildrenDeleted(node); 1762 } 1763 1764 break; 1765 } 1766 case MSG_VALUE_NODE_VALUE_CHANGED: 1767 { 1768 ValueNode* node; 1769 if (message->FindPointer("node", (void**)&node) == B_OK) { 1770 BReference<ValueNode> newNodeReference(node, true); 1771 fVariableTableModel->ValueNodeValueChanged(node); 1772 } 1773 1774 break; 1775 } 1776 case MSG_RESTORE_PARTIAL_VIEW_STATE: 1777 { 1778 ModelNode* node; 1779 if (message->FindPointer("node", (void**)&node) == B_OK) { 1780 TreeTablePath path; 1781 if (fVariableTableModel->GetTreePath(node, path)) { 1782 FunctionID* functionID = fStackFrame->Function() 1783 ->GetFunctionID(); 1784 if (functionID == NULL) 1785 return; 1786 BReference<FunctionID> functionIDReference(functionID, 1787 true); 1788 VariablesViewState* viewState = fViewStateHistory 1789 ->GetState(fThread->ID(), functionID); 1790 if (viewState != NULL) { 1791 _ApplyViewStateDescendentNodeInfos(viewState, node, 1792 path); 1793 } 1794 } 1795 } 1796 break; 1797 } 1798 case MSG_VALUE_NODE_NEEDS_VALUE: 1799 case MSG_MODEL_NODE_HIDDEN: 1800 { 1801 ModelNode* node; 1802 if (message->FindPointer("node", (void**)&node) == B_OK) { 1803 BReference<ModelNode> modelNodeReference(node, true); 1804 _RequestNodeValue(node); 1805 } 1806 1807 break; 1808 } 1809 case MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE: 1810 { 1811 _FinishContextMenu(false); 1812 break; 1813 } 1814 case MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED: 1815 { 1816 ModelNode* node; 1817 if (message->FindPointer("node", (void**)&node) != B_OK) 1818 break; 1819 BReference<ModelNode> nodeReference(node, true); 1820 1821 fVariableTableModel->NotifyNodeChanged(node); 1822 break; 1823 } 1824 case B_COPY: 1825 { 1826 _CopyVariableValueToClipboard(); 1827 break; 1828 } 1829 default: 1830 BGroupView::MessageReceived(message); 1831 break; 1832 } 1833 } 1834 1835 1836 void 1837 VariablesView::DetachedFromWindow() 1838 { 1839 _FinishContextMenu(true); 1840 } 1841 1842 1843 void 1844 VariablesView::LoadSettings(const BMessage& settings) 1845 { 1846 BMessage tableSettings; 1847 if (settings.FindMessage("variableTable", &tableSettings) == B_OK) { 1848 GuiSettingsUtils::UnarchiveTableSettings(tableSettings, 1849 fVariableTable); 1850 } 1851 } 1852 1853 1854 status_t 1855 VariablesView::SaveSettings(BMessage& settings) 1856 { 1857 settings.MakeEmpty(); 1858 1859 BMessage tableSettings; 1860 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings, 1861 fVariableTable); 1862 if (result == B_OK) 1863 result = settings.AddMessage("variableTable", &tableSettings); 1864 1865 return result; 1866 } 1867 1868 1869 void 1870 VariablesView::SetStackFrameClearPending() 1871 { 1872 fFrameClearPending = true; 1873 } 1874 1875 1876 void 1877 VariablesView::TreeTableNodeExpandedChanged(TreeTable* table, 1878 const TreeTablePath& path, bool expanded) 1879 { 1880 if (fFrameClearPending) 1881 return; 1882 1883 if (expanded) { 1884 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 1885 if (node == NULL) 1886 return; 1887 1888 fVariableTableModel->NodeExpanded(node); 1889 1890 // request the values of all children that don't have any yet 1891 1892 // If the node only has a hidden child, directly load the child's 1893 // children's values. 1894 if (node->CountChildren() == 1) { 1895 ModelNode* child = node->ChildAt(0); 1896 if (child->IsHidden()) 1897 node = child; 1898 } 1899 1900 // request the values 1901 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) { 1902 if (child->IsPresentationNode()) 1903 continue; 1904 1905 _RequestNodeValue(child); 1906 } 1907 } 1908 } 1909 1910 1911 void 1912 VariablesView::TreeTableCellMouseDown(TreeTable* table, 1913 const TreeTablePath& path, int32 columnIndex, BPoint screenWhere, 1914 uint32 buttons) 1915 { 1916 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) 1917 return; 1918 1919 if (fFrameClearPending) 1920 return; 1921 1922 _FinishContextMenu(true); 1923 1924 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path); 1925 if (node == NULL) 1926 return; 1927 1928 Settings* settings = NULL; 1929 SettingsMenu* settingsMenu = NULL; 1930 BReference<SettingsMenu> settingsMenuReference; 1931 status_t error = B_OK; 1932 TableCellValueRenderer* cellRenderer = node->TableCellRenderer(); 1933 if (cellRenderer != NULL) { 1934 settings = cellRenderer->GetSettings(); 1935 if (settings != NULL) { 1936 error = node->GetValueHandler() 1937 ->CreateTableCellValueSettingsMenu(node->GetValue(), settings, 1938 settingsMenu); 1939 settingsMenuReference.SetTo(settingsMenu, true); 1940 if (error != B_OK) 1941 return; 1942 } 1943 } 1944 1945 TableCellContextMenuTracker* tracker = new(std::nothrow) 1946 TableCellContextMenuTracker(node, Looper(), this); 1947 BReference<TableCellContextMenuTracker> trackerReference(tracker); 1948 1949 ContextActionList* preActionList = new(std::nothrow) ContextActionList; 1950 if (preActionList == NULL) 1951 return; 1952 1953 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter( 1954 preActionList); 1955 1956 error = _GetContextActionsForNode(node, preActionList); 1957 if (error != B_OK) 1958 return; 1959 1960 if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList) != B_OK) 1961 return; 1962 1963 fTableCellContextMenuTracker = trackerReference.Detach(); 1964 fTableCellContextMenuTracker->ShowMenu(screenWhere); 1965 } 1966 1967 1968 void 1969 VariablesView::_Init() 1970 { 1971 fVariableTable = new TreeTable("variable list", 0, B_FANCY_BORDER); 1972 AddChild(fVariableTable->ToView()); 1973 fVariableTable->SetSortingEnabled(false); 1974 1975 // columns 1976 fVariableTable->AddColumn(new StringTableColumn(0, "Variable", 80, 40, 1000, 1977 B_TRUNCATE_END, B_ALIGN_LEFT)); 1978 fVariableTable->AddColumn(new VariableValueColumn(1, "Value", 80, 40, 1000, 1979 B_TRUNCATE_END, B_ALIGN_RIGHT)); 1980 fVariableTable->AddColumn(new StringTableColumn(2, "Type", 80, 40, 1000, 1981 B_TRUNCATE_END, B_ALIGN_LEFT)); 1982 1983 fVariableTableModel = new VariableTableModel; 1984 if (fVariableTableModel->Init() != B_OK) 1985 throw std::bad_alloc(); 1986 fVariableTable->SetTreeTableModel(fVariableTableModel); 1987 fVariableTable->SetToolTipProvider(fVariableTableModel); 1988 1989 fContainerListener = new ContainerListener(this); 1990 fVariableTableModel->SetContainerListener(fContainerListener); 1991 1992 fVariableTable->AddTreeTableListener(this); 1993 1994 fViewStateHistory = new VariablesViewStateHistory; 1995 if (fViewStateHistory->Init() != B_OK) 1996 throw std::bad_alloc(); 1997 } 1998 1999 2000 void 2001 VariablesView::_RequestNodeValue(ModelNode* node) 2002 { 2003 // get the node child and its container 2004 ValueNodeChild* nodeChild = node->NodeChild(); 2005 ValueNodeContainer* container = nodeChild->Container(); 2006 2007 BReference<ValueNodeContainer> containerReference(container); 2008 AutoLocker<ValueNodeContainer> containerLocker(container); 2009 2010 if (container == NULL || nodeChild->Container() != container) 2011 return; 2012 2013 // get the value node and check whether its value has not yet been resolved 2014 ValueNode* valueNode = nodeChild->Node(); 2015 if (valueNode == NULL) { 2016 ModelNode* parent = node->Parent(); 2017 if (parent != NULL) { 2018 TreeTablePath path; 2019 if (!fVariableTableModel->GetTreePath(parent, path)) 2020 return; 2021 2022 // if the parent node was already expanded when the child was 2023 // added, we may not yet have added a value node. 2024 // Notify the table model that this needs to be done. 2025 if (fVariableTable->IsNodeExpanded(path)) 2026 fVariableTableModel->NodeExpanded(parent); 2027 } 2028 } 2029 2030 if (valueNode == NULL || valueNode->LocationAndValueResolutionState() 2031 != VALUE_NODE_UNRESOLVED) { 2032 return; 2033 } 2034 2035 BReference<ValueNode> valueNodeReference(valueNode); 2036 containerLocker.Unlock(); 2037 2038 // request resolution of the value 2039 fListener->ValueNodeValueRequested(fStackFrame->GetCpuState(), container, 2040 valueNode); 2041 } 2042 2043 2044 status_t 2045 VariablesView::_GetContextActionsForNode(ModelNode* node, 2046 ContextActionList* actions) 2047 { 2048 ValueLocation* location = node->NodeChild()->Location(); 2049 if (location == NULL) 2050 return B_OK; 2051 2052 status_t result = B_OK; 2053 BMessage* message = NULL; 2054 2055 // only show the Inspect option if the value is in fact located 2056 // in memory. 2057 if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) { 2058 result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW, 2059 actions, message); 2060 if (result != B_OK) 2061 return result; 2062 message->AddUInt64("address", location->PieceAt(0).address); 2063 } 2064 2065 ValueNode* valueNode = node->NodeChild()->Node(); 2066 2067 if (valueNode != NULL) { 2068 AddressType* type = dynamic_cast<AddressType*>(valueNode->GetType()); 2069 if (type != NULL && type->BaseType() != NULL) { 2070 result = _AddContextAction("Cast to array", MSG_TYPECAST_TO_ARRAY, 2071 actions, message); 2072 if (result != B_OK) 2073 return result; 2074 message->AddPointer("node", node); 2075 } 2076 } 2077 2078 result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS, 2079 MSG_SHOW_TYPECAST_NODE_PROMPT, actions, message); 2080 if (result != B_OK) 2081 return result; 2082 2083 result = _AddContextAction("Watch" B_UTF8_ELLIPSIS, 2084 MSG_SHOW_WATCH_VARIABLE_PROMPT, actions, message); 2085 if (result != B_OK) 2086 return result; 2087 2088 if (valueNode == NULL) 2089 return B_OK; 2090 2091 if (valueNode->LocationAndValueResolutionState() == B_OK) { 2092 result = _AddContextAction("Copy Value", B_COPY, actions, message); 2093 if (result != B_OK) 2094 return result; 2095 } 2096 2097 // if the current node isn't itself a ranged container, check if it 2098 // contains a hidden node which is, since in the latter case we 2099 // want to present the range selection as well. 2100 if (!valueNode->IsRangedContainer()) { 2101 if (node->CountChildren() == 1 && node->ChildAt(0)->IsHidden()) { 2102 valueNode = node->ChildAt(0)->NodeChild()->Node(); 2103 if (valueNode == NULL || !valueNode->IsRangedContainer()) 2104 return B_OK; 2105 } else 2106 return B_OK; 2107 } 2108 2109 result = _AddContextAction("Set visible range" B_UTF8_ELLIPSIS, 2110 MSG_SHOW_CONTAINER_RANGE_PROMPT, actions, message); 2111 if (result != B_OK) 2112 return result; 2113 2114 return B_OK; 2115 } 2116 2117 2118 status_t 2119 VariablesView::_AddContextAction(const char* action, uint32 what, 2120 ContextActionList* actions, BMessage*& _message) 2121 { 2122 _message = new(std::nothrow) BMessage(what); 2123 if (_message == NULL) 2124 return B_NO_MEMORY; 2125 2126 ObjectDeleter<BMessage> messageDeleter(_message); 2127 2128 ActionMenuItem* item = new(std::nothrow) ActionMenuItem(action, 2129 _message); 2130 if (item == NULL) 2131 return B_NO_MEMORY; 2132 2133 messageDeleter.Detach(); 2134 ObjectDeleter<ActionMenuItem> actionDeleter(item); 2135 if (!actions->AddItem(item)) 2136 return B_NO_MEMORY; 2137 2138 actionDeleter.Detach(); 2139 2140 return B_OK; 2141 } 2142 2143 2144 void 2145 VariablesView::_FinishContextMenu(bool force) 2146 { 2147 if (fTableCellContextMenuTracker != NULL) { 2148 if (!fTableCellContextMenuTracker->FinishMenu(force) || force) { 2149 fTableCellContextMenuTracker->ReleaseReference(); 2150 fTableCellContextMenuTracker = NULL; 2151 } 2152 } 2153 } 2154 2155 2156 2157 void 2158 VariablesView::_SaveViewState() const 2159 { 2160 if (fThread == NULL || fStackFrame == NULL 2161 || fStackFrame->Function() == NULL) { 2162 return; 2163 } 2164 2165 // get the function ID 2166 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2167 if (functionID == NULL) 2168 return; 2169 BReference<FunctionID> functionIDReference(functionID, true); 2170 2171 // create an empty view state 2172 VariablesViewState* viewState = new(std::nothrow) VariablesViewState; 2173 if (viewState == NULL) 2174 return; 2175 BReference<VariablesViewState> viewStateReference(viewState, true); 2176 2177 if (viewState->Init() != B_OK) 2178 return; 2179 2180 // populate it 2181 TreeTablePath path; 2182 if (_AddViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), 2183 path) != B_OK) { 2184 return; 2185 } 2186 // TODO: Add values! 2187 2188 // add the view state to the history 2189 fViewStateHistory->SetState(fThread->ID(), functionID, viewState); 2190 } 2191 2192 2193 void 2194 VariablesView::_RestoreViewState() 2195 { 2196 if (fPreviousViewState != NULL) { 2197 fPreviousViewState->ReleaseReference(); 2198 fPreviousViewState = NULL; 2199 } 2200 2201 if (fThread == NULL || fStackFrame == NULL 2202 || fStackFrame->Function() == NULL) { 2203 return; 2204 } 2205 2206 // get the function ID 2207 FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); 2208 if (functionID == NULL) 2209 return; 2210 BReference<FunctionID> functionIDReference(functionID, true); 2211 2212 // get the previous view state 2213 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), 2214 functionID); 2215 if (viewState == NULL) 2216 return; 2217 2218 // apply the view state 2219 TreeTablePath path; 2220 _ApplyViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), 2221 path); 2222 } 2223 2224 2225 status_t 2226 VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState, 2227 void* parent, TreeTablePath& path) const 2228 { 2229 int32 childCount = fVariableTableModel->CountChildren(parent); 2230 for (int32 i = 0; i < childCount; i++) { 2231 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i); 2232 if (!path.AddComponent(i)) 2233 return B_NO_MEMORY; 2234 2235 // add the node's info 2236 VariablesViewNodeInfo nodeInfo; 2237 nodeInfo.SetNodeExpanded(fVariableTable->IsNodeExpanded(path)); 2238 nodeInfo.SetCastedType(node->GetCastedType()); 2239 TableCellValueRenderer* renderer = node->TableCellRenderer(); 2240 if (renderer != NULL) { 2241 Settings* settings = renderer->GetSettings(); 2242 if (settings != NULL) 2243 nodeInfo.SetRendererSettings(settings->Message()); 2244 } 2245 2246 status_t error = viewState->SetNodeInfo(node->GetVariable()->ID(), 2247 node->GetPath(), nodeInfo); 2248 if (error != B_OK) 2249 return error; 2250 2251 // recurse 2252 error = _AddViewStateDescendentNodeInfos(viewState, node, path); 2253 if (error != B_OK) 2254 return error; 2255 2256 path.RemoveLastComponent(); 2257 } 2258 2259 return B_OK; 2260 } 2261 2262 2263 status_t 2264 VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState, 2265 void* parent, TreeTablePath& path) 2266 { 2267 int32 childCount = fVariableTableModel->CountChildren(parent); 2268 for (int32 i = 0; i < childCount; i++) { 2269 ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i); 2270 if (!path.AddComponent(i)) 2271 return B_NO_MEMORY; 2272 2273 // apply the node's info, if any 2274 const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( 2275 node->GetVariable()->ID(), node->GetPath()); 2276 if (nodeInfo != NULL) { 2277 // NB: if the node info indicates that the node in question 2278 // was being cast to a different type, this *must* be applied 2279 // before any other view state restoration, since it potentially 2280 // changes the child hierarchy under that node. 2281 Type* type = nodeInfo->GetCastedType(); 2282 if (type != NULL) { 2283 ValueNode* valueNode = NULL; 2284 if (TypeHandlerRoster::Default()->CreateValueNode( 2285 node->NodeChild(), type, valueNode) == B_OK) { 2286 node->NodeChild()->SetNode(valueNode); 2287 node->SetCastedType(type); 2288 } 2289 } 2290 2291 // we don't have a renderer yet so we can't apply the settings 2292 // at this stage. Store them on the model node so we can lazily 2293 // apply them once the value is retrieved. 2294 node->SetLastRendererSettings(nodeInfo->GetRendererSettings()); 2295 2296 fVariableTable->SetNodeExpanded(path, nodeInfo->IsNodeExpanded()); 2297 2298 // recurse 2299 status_t error = _ApplyViewStateDescendentNodeInfos(viewState, node, 2300 path); 2301 if (error != B_OK) 2302 return error; 2303 } 2304 2305 path.RemoveLastComponent(); 2306 } 2307 2308 return B_OK; 2309 } 2310 2311 2312 void 2313 VariablesView::_CopyVariableValueToClipboard() 2314 { 2315 ModelNode* node = reinterpret_cast<ModelNode*>( 2316 fVariableTable->SelectionModel()->NodeAt(0)); 2317 2318 Value* value = node->GetValue(); 2319 BString valueData; 2320 if (value != NULL && value->ToString(valueData)) { 2321 be_clipboard->Lock(); 2322 be_clipboard->Data()->RemoveData("text/plain"); 2323 be_clipboard->Data()->AddData ("text/plain", 2324 B_MIME_TYPE, valueData.String(), 2325 valueData.Length()); 2326 be_clipboard->Commit(); 2327 be_clipboard->Unlock(); 2328 } 2329 } 2330 2331 2332 // #pragma mark - Listener 2333 2334 2335 VariablesView::Listener::~Listener() 2336 { 2337 } 2338