Debugger: Further work on memory leak hunting.
[haiku.git] / src / apps / debugger / user_interface / gui / team_window / VariablesView.cpp
blob1fc7b755465b2257b3d4c2af140f51f5f101c202
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 */
8 #include "VariablesView.h"
10 #include <new>
12 #include <debugger.h>
14 #include <Alert.h>
15 #include <Clipboard.h>
16 #include <Looper.h>
17 #include <PopUpMenu.h>
18 #include <ToolTip.h>
20 #include <AutoDeleter.h>
21 #include <AutoLocker.h>
22 #include <PromptWindow.h>
24 #include "table/TableColumns.h"
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"
70 enum {
71 VALUE_NODE_TYPE = 'valn'
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'
84 // maximum number of array elements to show by default
85 static const uint64 kMaxArrayElementCount = 10;
88 // #pragma mark - FunctionKey
91 struct VariablesView::FunctionKey {
92 FunctionID* function;
94 FunctionKey(FunctionID* function)
96 function(function)
100 uint32 HashValue() const
102 return function->HashValue();
105 bool operator==(const FunctionKey& other) const
107 return *function == *other.function;
112 // #pragma mark - ExpressionInfoEntry
115 struct VariablesView::ExpressionInfoEntry : FunctionKey, ExpressionInfoList {
116 ExpressionInfoEntry* next;
118 ExpressionInfoEntry(FunctionID* function)
120 FunctionKey(function),
121 ExpressionInfoList(10, false)
123 function->AcquireReference();
126 ~ExpressionInfoEntry()
128 _Cleanup();
131 void SetInfo(const ExpressionInfoList& infoList)
133 _Cleanup();
135 for (int32 i = 0; i < infoList.CountItems(); i++) {
136 ExpressionInfo* info = infoList.ItemAt(i);
137 if (!AddItem(info))
138 break;
140 info->AcquireReference();
144 private:
145 void _Cleanup()
147 for (int32 i = 0; i < CountItems(); i++)
148 ItemAt(i)->ReleaseReference();
150 MakeEmpty();
155 // #pragma mark - ExpressionInfoEntryHashDefinition
158 struct VariablesView::ExpressionInfoEntryHashDefinition {
159 typedef FunctionKey KeyType;
160 typedef ExpressionInfoEntry ValueType;
162 size_t HashKey(const FunctionKey& key) const
164 return key.HashValue();
167 size_t Hash(const ExpressionInfoEntry* value) const
169 return value->HashValue();
172 bool Compare(const FunctionKey& key,
173 const ExpressionInfoEntry* value) const
175 return key == *value;
178 ExpressionInfoEntry*& GetLink(ExpressionInfoEntry* value) const
180 return value->next;
185 // #pragma mark - ContainerListener
188 class VariablesView::ContainerListener : public ValueNodeContainer::Listener {
189 public:
190 ContainerListener(BHandler* indirectTarget);
192 void SetModel(VariableTableModel* model);
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);
200 virtual void ModelNodeHidden(ModelNode* node);
202 virtual void ModelNodeValueRequested(ModelNode* node);
204 virtual void ModelNodeRestoreViewStateRequested(ModelNode* node);
206 private:
207 BHandler* fIndirectTarget;
208 VariableTableModel* fModel;
212 // #pragma mark - ExpressionVariableID
215 class VariablesView::ExpressionVariableID : public ObjectID {
216 public:
217 ExpressionVariableID(ExpressionInfo* info)
219 fInfo(info)
221 fInfo->AcquireReference();
224 virtual ~ExpressionVariableID()
226 fInfo->ReleaseReference();
229 virtual bool operator==(const ObjectID& other) const
231 const ExpressionVariableID* otherID
232 = dynamic_cast<const ExpressionVariableID*>(&other);
233 if (otherID == NULL)
234 return false;
236 return fInfo == otherID->fInfo;
239 protected:
240 virtual uint32 ComputeHashValue() const
242 uint32 hash = *(uint32*)(&fInfo);
243 hash = hash * 19 + StringUtils::HashValue(fInfo->Expression());
245 return hash;
248 private:
249 ExpressionInfo* fInfo;
253 // #pragma mark - ModelNode
256 class VariablesView::ModelNode : public BReferenceable {
257 public:
258 ModelNode(ModelNode* parent, Variable* variable, ValueNodeChild* nodeChild,
259 bool isPresentationNode)
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()
276 fVariable->AcquireReference();
277 fNodeChild->AcquireReference();
280 ~ModelNode()
282 SetTableCellRenderer(NULL);
283 SetValueHandler(NULL);
284 SetValue(NULL);
286 for (int32 i = 0; ModelNode* child = fChildren.ItemAt(i); i++)
287 child->ReleaseReference();
289 fNodeChild->ReleaseReference();
290 fVariable->ReleaseReference();
292 if (fComponentPath != NULL)
293 fComponentPath->ReleaseReference();
295 if (fCastedType != NULL)
296 fCastedType->ReleaseReference();
299 status_t Init()
301 fComponentPath = new(std::nothrow) TypeComponentPath();
302 if (fComponentPath == NULL)
303 return B_NO_MEMORY;
305 if (fParent != NULL)
306 *fComponentPath = *fParent->GetPath();
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());
314 fComponentPath->AddComponent(component);
316 return B_OK;
319 ModelNode* Parent() const
321 return fParent;
324 ValueNodeChild* NodeChild() const
326 return fNodeChild;
329 const BString& Name() const
331 return fPresentationName.IsEmpty()
332 ? fNodeChild->Name() : fPresentationName;
335 void SetPresentationName(const BString& name)
337 fPresentationName = name;
340 Type* GetType() const
342 if (fCastedType != NULL)
343 return fCastedType;
345 return fNodeChild->GetType();
348 Variable* GetVariable() const
350 return fVariable;
353 Value* GetValue() const
355 return fValue;
358 void SetValue(Value* value)
360 if (value == fValue)
361 return;
363 if (fValue != NULL)
364 fValue->ReleaseReference();
366 fValue = value;
368 if (fValue != NULL)
369 fValue->AcquireReference();
371 _CompareValues();
374 const BVariant& PreviousValue() const
376 return fPreviousValue;
379 void SetPreviousValue(const BVariant& value)
381 fPreviousValue = value;
384 Type* GetCastedType() const
386 return fCastedType;
389 void SetCastedType(Type* type)
391 if (fCastedType != NULL)
392 fCastedType->ReleaseReference();
394 fCastedType = type;
395 if (type != NULL)
396 fCastedType->AcquireReference();
399 const BMessage& GetLastRendererSettings() const
401 return fLastRendererSettings;
404 void SetLastRendererSettings(const BMessage& settings)
406 fLastRendererSettings = settings;
409 TypeComponentPath* GetPath() const
411 return fComponentPath;
414 ValueHandler* GetValueHandler() const
416 return fValueHandler;
419 void SetValueHandler(ValueHandler* handler)
421 if (handler == fValueHandler)
422 return;
424 if (fValueHandler != NULL)
425 fValueHandler->ReleaseReference();
427 fValueHandler = handler;
429 if (fValueHandler != NULL)
430 fValueHandler->AcquireReference();
434 TableCellValueRenderer* TableCellRenderer() const
436 return fTableCellRenderer;
439 void SetTableCellRenderer(TableCellValueRenderer* renderer)
441 if (renderer == fTableCellRenderer)
442 return;
444 if (fTableCellRenderer != NULL)
445 fTableCellRenderer->ReleaseReference();
447 fTableCellRenderer = renderer;
449 if (fTableCellRenderer != NULL)
450 fTableCellRenderer->AcquireReference();
453 bool IsPresentationNode() const
455 return fIsPresentationNode;
458 bool IsHidden() const
460 return fHidden;
463 void SetHidden(bool hidden)
465 fHidden = hidden;
468 bool ValueChanged() const
470 return fValueChanged;
473 int32 CountChildren() const
475 return fChildren.CountItems();
478 ModelNode* ChildAt(int32 index) const
480 return fChildren.ItemAt(index);
483 int32 IndexOf(ModelNode* child) const
485 return fChildren.IndexOf(child);
488 bool AddChild(ModelNode* child)
490 if (!fChildren.AddItem(child))
491 return false;
493 child->AcquireReference();
494 return true;
497 bool RemoveChild(ModelNode* child)
499 if (!fChildren.RemoveItem(child))
500 return false;
502 child->ReleaseReference();
503 return true;
506 bool RemoveAllChildren()
508 for (int32 i = 0; i < fChildren.CountItems(); i++)
509 RemoveChild(fChildren.ItemAt(i));
511 return true;
514 private:
515 typedef BObjectList<ModelNode> ChildList;
517 private:
518 void _CompareValues()
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;
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;
554 public:
555 ModelNode* fNext;
559 // #pragma mark - VariablesExpressionInfo
562 class VariablesView::VariablesExpressionInfo : public ExpressionInfo {
563 public:
564 VariablesExpressionInfo(const BString& expression, ModelNode* node)
566 ExpressionInfo(expression),
567 fTargetNode(node)
569 fTargetNode->AcquireReference();
572 virtual ~VariablesExpressionInfo()
574 fTargetNode->ReleaseReference();
577 inline ModelNode* TargetNode() const
579 return fTargetNode;
582 private:
583 ModelNode* fTargetNode;
587 // #pragma mark - VariableValueColumn
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)
596 StringTableColumn(modelIndex, title, width, minWidth, maxWidth,
597 truncate, align)
601 protected:
602 void DrawValue(const BVariant& value, BRect rect, BView* targetView)
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;
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("");
619 fField.SetWidth(Width());
620 fColumn.DrawField(&fField, rect, targetView);
623 float GetPreferredWidth(const BVariant& value, BView* targetView) const
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);
635 return fColumn.BTitledColumn::GetPreferredWidth(NULL, targetView);
638 virtual BField* PrepareField(const BVariant& _value) const
640 return NULL;
645 // #pragma mark - VariableTableModel
648 class VariablesView::VariableTableModel : public TreeTableModel,
649 public TreeTableToolTipProvider {
650 public:
651 VariableTableModel(ValueNodeManager* manager);
652 ~VariableTableModel();
654 status_t Init();
656 void SetContainerListener(
657 ContainerListener* listener);
659 void SetStackFrame(::Thread* thread,
660 StackFrame* stackFrame);
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);
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);
675 bool GetTreePath(ModelNode* node,
676 TreeTablePath& _path) const;
678 void NodeExpanded(ModelNode* node);
680 void NotifyNodeChanged(ModelNode* node);
681 void NotifyNodeHidden(ModelNode* node);
683 virtual bool GetToolTipForTablePath(
684 const TreeTablePath& path,
685 int32 columnIndex, BToolTip** _tip);
687 status_t AddSyntheticNode(Variable* variable,
688 ValueNodeChild*& _child,
689 const char* presentationName = NULL);
690 void RemoveSyntheticNode(ModelNode* node);
692 private:
693 struct NodeHashDefinition {
694 typedef ValueNodeChild* KeyType;
695 typedef ModelNode ValueType;
697 size_t HashKey(const ValueNodeChild* key) const
699 return (size_t)key;
702 size_t Hash(const ModelNode* value) const
704 return HashKey(value->NodeChild());
707 bool Compare(const ValueNodeChild* key,
708 const ModelNode* value) const
710 return value->NodeChild() == key;
713 ModelNode*& GetLink(ModelNode* value) const
715 return value->fNext;
719 typedef BObjectList<ModelNode> NodeList;
720 typedef BOpenHashTable<NodeHashDefinition> NodeTable;
722 private:
723 // container must be locked
725 status_t _AddNode(Variable* variable, ModelNode* parent,
726 ValueNodeChild* nodeChild,
727 bool isPresentationNode = false,
728 bool isOnlyChild = false);
730 private:
731 ::Thread* fThread;
732 ValueNodeManager* fNodeManager;
733 ContainerListener* fContainerListener;
734 NodeList fNodes;
735 NodeTable fNodeTable;
739 class VariablesView::ContextMenu : public BPopUpMenu {
740 public:
741 ContextMenu(const BMessenger& parent, const char* name)
742 : BPopUpMenu(name, false, false),
743 fParent(parent)
747 virtual void Hide()
749 BPopUpMenu::Hide();
751 BMessage message(MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE);
752 message.AddPointer("menu", this);
753 fParent.SendMessage(&message);
756 private:
757 BMessenger fParent;
761 // #pragma mark - TableCellContextMenuTracker
764 class VariablesView::TableCellContextMenuTracker : public BReferenceable,
765 Settings::Listener {
766 public:
767 TableCellContextMenuTracker(ModelNode* node, BLooper* parentLooper,
768 const BMessenger& parent)
770 fNode(node),
771 fParentLooper(parentLooper),
772 fParent(parent),
773 fRendererSettings(NULL),
774 fRendererSettingsMenu(NULL),
775 fRendererMenuAdded(false),
776 fMenuPreparedToShow(false)
778 fNode->AcquireReference();
781 ~TableCellContextMenuTracker()
783 FinishMenu(true);
785 if (fRendererSettingsMenu != NULL)
786 fRendererSettingsMenu->ReleaseReference();
788 if (fRendererSettings != NULL)
789 fRendererSettings->ReleaseReference();
791 fNode->ReleaseReference();
794 status_t Init(Settings* rendererSettings,
795 SettingsMenu* rendererSettingsMenu,
796 ContextActionList* preSettingsActions = NULL,
797 ContextActionList* postSettingsActions = NULL)
799 if (rendererSettings == NULL && preSettingsActions == NULL
800 && postSettingsActions == NULL) {
801 return B_BAD_VALUE;
804 if (rendererSettings != NULL) {
805 fRendererSettings = rendererSettings;
806 fRendererSettings->AcquireReference();
809 fRendererSettingsMenu = rendererSettingsMenu;
810 fRendererSettingsMenu->AcquireReference();
813 fContextMenu = new(std::nothrow) ContextMenu(fParent,
814 "table cell settings popup");
815 if (fContextMenu == NULL)
816 return B_NO_MEMORY;
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;
825 if (fRendererSettingsMenu != NULL || postSettingsActions != NULL)
826 fContextMenu->AddSeparatorItem();
829 if (fRendererSettingsMenu != NULL) {
830 error = fRendererSettingsMenu->AddToMenu(fContextMenu,
831 fContextMenu->CountItems());
832 if (error != B_OK)
833 return error;
835 if (postSettingsActions != NULL)
836 fContextMenu->AddSeparatorItem();
839 if (postSettingsActions != NULL) {
840 error = _AddActionItems(postSettingsActions);
841 if (error != B_OK)
842 return error;
846 if (fRendererSettings != NULL) {
847 AutoLocker<Settings> settingsLocker(fRendererSettings);
848 fRendererSettings->AddListener(this);
849 fRendererMenuAdded = true;
852 return B_OK;
855 void ShowMenu(BPoint screenWhere)
857 if (fRendererMenuAdded)
858 fRendererSettingsMenu->PrepareToShow(fParentLooper);
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));
867 fMenuPreparedToShow = true;
869 BRect mouseRect(screenWhere, screenWhere);
870 mouseRect.InsetBy(-4.0, -4.0);
871 fContextMenu->Go(screenWhere, true, false, mouseRect, true);
874 bool FinishMenu(bool force)
876 bool stillActive = false;
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);
891 fMenuPreparedToShow = stillActive;
894 if (fRendererMenuAdded) {
895 fRendererSettingsMenu->RemoveFromMenu();
896 fRendererSettings->RemoveListener(this);
897 fRendererMenuAdded = false;
900 if (fContextMenu != NULL) {
901 delete fContextMenu;
902 fContextMenu = NULL;
905 return stillActive;
908 private:
909 // Settings::Listener
911 virtual void SettingValueChanged(Setting* setting)
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();
921 status_t _AddActionItems(ContextActionList* actions)
923 if (fContextMenu == NULL)
924 return B_BAD_VALUE;
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));
932 return B_NO_MEMORY;
936 return B_OK;
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;
951 // #pragma mark - ContainerListener
954 VariablesView::ContainerListener::ContainerListener(BHandler* indirectTarget)
956 fIndirectTarget(indirectTarget),
957 fModel(NULL)
962 void
963 VariablesView::ContainerListener::SetModel(VariableTableModel* model)
965 fModel = model;
969 void
970 VariablesView::ContainerListener::ValueNodeChanged(ValueNodeChild* nodeChild,
971 ValueNode* oldNode, ValueNode* newNode)
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;
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);
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();
998 void
999 VariablesView::ContainerListener::ValueNodeChildrenCreated(ValueNode* node)
1001 // If the looper is already locked, invoke the model's hook synchronously.
1002 if (fIndirectTarget->Looper()->IsLocked()) {
1003 fModel->ValueNodeChildrenCreated(node);
1004 return;
1007 // looper not locked yet -- call asynchronously to avoid reverse locking
1008 // order
1009 BReference<ValueNode> nodeReference(node);
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();
1020 void
1021 VariablesView::ContainerListener::ValueNodeChildrenDeleted(ValueNode* node)
1023 // If the looper is already locked, invoke the model's hook synchronously.
1024 if (fIndirectTarget->Looper()->IsLocked()) {
1025 fModel->ValueNodeChildrenDeleted(node);
1026 return;
1029 // looper not locked yet -- call asynchronously to avoid reverse locking
1030 // order
1031 BReference<ValueNode> nodeReference(node);
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();
1042 void
1043 VariablesView::ContainerListener::ValueNodeValueChanged(ValueNode* node)
1045 // If the looper is already locked, invoke the model's hook synchronously.
1046 if (fIndirectTarget->Looper()->IsLocked()) {
1047 fModel->ValueNodeValueChanged(node);
1048 return;
1051 // looper not locked yet -- call asynchronously to avoid reverse locking
1052 // order
1053 BReference<ValueNode> nodeReference(node);
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();
1064 void
1065 VariablesView::ContainerListener::ModelNodeHidden(ModelNode* node)
1067 BReference<ModelNode> nodeReference(node);
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();
1078 void
1079 VariablesView::ContainerListener::ModelNodeValueRequested(ModelNode* node)
1081 BReference<ModelNode> nodeReference(node);
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();
1092 void
1093 VariablesView::ContainerListener::ModelNodeRestoreViewStateRequested(
1094 ModelNode* node)
1096 BReference<ModelNode> nodeReference(node);
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();
1107 // #pragma mark - VariableTableModel
1110 VariablesView::VariableTableModel::VariableTableModel(
1111 ValueNodeManager* manager)
1113 fThread(NULL),
1114 fNodeManager(manager),
1115 fContainerListener(NULL),
1116 fNodeTable()
1118 fNodeManager->AcquireReference();
1122 VariablesView::VariableTableModel::~VariableTableModel()
1124 fNodeManager->ReleaseReference();
1128 status_t
1129 VariablesView::VariableTableModel::Init()
1131 return fNodeTable.Init();
1135 void
1136 VariablesView::VariableTableModel::SetContainerListener(
1137 ContainerListener* listener)
1139 if (listener == fContainerListener)
1140 return;
1142 if (fContainerListener != NULL) {
1143 if (fNodeManager != NULL)
1144 fNodeManager->RemoveListener(fContainerListener);
1146 fContainerListener->SetModel(NULL);
1149 fContainerListener = listener;
1151 if (fContainerListener != NULL) {
1152 fContainerListener->SetModel(this);
1154 if (fNodeManager != NULL)
1155 fNodeManager->AddListener(fContainerListener);
1160 void
1161 VariablesView::VariableTableModel::SetStackFrame(::Thread* thread,
1162 StackFrame* stackFrame)
1164 fThread = thread;
1166 fNodeManager->SetStackFrame(thread, stackFrame);
1168 int32 count = fNodes.CountItems();
1169 fNodeTable.Clear(true);
1171 if (!fNodes.IsEmpty()) {
1172 for (int32 i = 0; i < count; i++)
1173 fNodes.ItemAt(i)->ReleaseReference();
1174 fNodes.MakeEmpty();
1177 NotifyNodesRemoved(TreeTablePath(), 0, count);
1179 if (stackFrame == NULL)
1180 return;
1182 ValueNodeContainer* container = fNodeManager->GetContainer();
1183 AutoLocker<ValueNodeContainer> containerLocker(container);
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());
1196 void
1197 VariablesView::VariableTableModel::ValueNodeChanged(ValueNodeChild* nodeChild,
1198 ValueNode* oldNode, ValueNode* newNode)
1200 AutoLocker<ValueNodeContainer> containerLocker(
1201 fNodeManager->GetContainer());
1202 ModelNode* modelNode = fNodeTable.Lookup(nodeChild);
1203 if (modelNode == NULL)
1204 return;
1206 if (oldNode != NULL) {
1207 ValueNodeChildrenDeleted(oldNode);
1208 NotifyNodeChanged(modelNode);
1213 void
1214 VariablesView::VariableTableModel::ValueNodeChildrenCreated(
1215 ValueNode* valueNode)
1217 AutoLocker<ValueNodeContainer> containerLocker(
1218 fNodeManager->GetContainer());
1220 // check whether we know the node
1221 ValueNodeChild* nodeChild = valueNode->NodeChild();
1222 if (nodeChild == NULL)
1223 return;
1225 ModelNode* modelNode = fNodeTable.Lookup(nodeChild);
1226 if (modelNode == NULL)
1227 return;
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);
1239 ModelNode* childNode = fNodeTable.Lookup(child);
1240 if (childNode != NULL)
1241 fContainerListener->ModelNodeValueRequested(childNode);
1244 if (valueNode->ChildCreationNeedsValue())
1245 fContainerListener->ModelNodeRestoreViewStateRequested(modelNode);
1249 void
1250 VariablesView::VariableTableModel::ValueNodeChildrenDeleted(ValueNode* node)
1252 AutoLocker<ValueNodeContainer> containerLocker(
1253 fNodeManager->GetContainer());
1255 // check whether we know the node
1256 ValueNodeChild* nodeChild = node->NodeChild();
1257 if (nodeChild == NULL)
1258 return;
1260 ModelNode* modelNode = fNodeTable.Lookup(nodeChild);
1261 if (modelNode == NULL)
1262 return;
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);
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());
1282 TreeTablePath treePath;
1283 if (GetTreePath(childNode, treePath)) {
1284 int32 index = treePath.RemoveLastComponent();
1285 NotifyNodesRemoved(treePath, index, 1);
1287 modelNode->RemoveChild(childNode);
1288 fNodeTable.Remove(childNode);
1293 void
1294 VariablesView::VariableTableModel::ValueNodeValueChanged(ValueNode* valueNode)
1296 AutoLocker<ValueNodeContainer> containerLocker(
1297 fNodeManager->GetContainer());
1299 // check whether we know the node
1300 ValueNodeChild* nodeChild = valueNode->NodeChild();
1301 if (nodeChild == NULL)
1302 return;
1304 ModelNode* modelNode = fNodeTable.Lookup(nodeChild);
1305 if (modelNode == NULL)
1306 return;
1308 // check whether the value actually changed
1309 Value* value = valueNode->GetValue();
1310 if (value == modelNode->GetValue())
1311 return;
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);
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;
1327 // set value/handler/renderer
1328 modelNode->SetValue(value);
1329 modelNode->SetValueHandler(valueHandler);
1330 modelNode->SetTableCellRenderer(renderer);
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());
1342 // notify table model listeners
1343 NotifyNodeChanged(modelNode);
1347 int32
1348 VariablesView::VariableTableModel::CountColumns() const
1350 return 3;
1354 void*
1355 VariablesView::VariableTableModel::Root() const
1357 return (void*)this;
1361 int32
1362 VariablesView::VariableTableModel::CountChildren(void* parent) const
1364 if (parent == this)
1365 return fNodes.CountItems();
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();
1377 return childCount;
1381 void*
1382 VariablesView::VariableTableModel::ChildAt(void* parent, int32 index) const
1384 if (parent == this)
1385 return fNodes.ItemAt(index);
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);
1397 return modelNode->ChildAt(index);
1401 bool
1402 VariablesView::VariableTableModel::GetValueAt(void* object, int32 columnIndex,
1403 BVariant& _value)
1405 ModelNode* node = (ModelNode*)object;
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;
1417 ValueNode* childNode = node->NodeChild()->Node();
1418 if (childNode == NULL)
1419 return false;
1421 Type* nodeChildRawType = childNode->GetType()->ResolveRawType(
1422 false);
1423 if (nodeChildRawType->Kind() == TYPE_COMPOUND)
1425 if (location->CountPieces() > 1)
1426 return false;
1428 BString data;
1429 ValuePieceLocation piece = location->PieceAt(0);
1430 if (piece.type != VALUE_PIECE_LOCATION_MEMORY)
1431 return false;
1433 data.SetToFormat("[@ %#" B_PRIx64 "]", piece.address);
1434 _value.SetTo(data);
1435 return true;
1437 return false;
1440 _value.SetTo(node, VALUE_NODE_TYPE);
1441 return true;
1442 case 2:
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;
1451 Type* type = childNode->GetType();
1452 if (type == NULL)
1453 return false;
1455 _value.SetTo(type->Name(), B_VARIANT_DONT_COPY_DATA);
1456 return true;
1458 default:
1459 return false;
1464 void
1465 VariablesView::VariableTableModel::NodeExpanded(ModelNode* node)
1467 AutoLocker<ValueNodeContainer> containerLocker(
1468 fNodeManager->GetContainer());
1469 // add children of all children
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;
1478 // add the children
1479 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++)
1480 fNodeManager->AddChildNodes(child->NodeChild());
1484 void
1485 VariablesView::VariableTableModel::NotifyNodeChanged(ModelNode* node)
1487 if (!node->IsHidden()) {
1488 TreeTablePath treePath;
1489 if (GetTreePath(node, treePath)) {
1490 int32 index = treePath.RemoveLastComponent();
1491 NotifyNodesChanged(treePath, index, 1);
1497 void
1498 VariablesView::VariableTableModel::NotifyNodeHidden(ModelNode* node)
1500 fContainerListener->ModelNodeHidden(node);
1504 bool
1505 VariablesView::VariableTableModel::GetToolTipForTablePath(
1506 const TreeTablePath& path, int32 columnIndex, BToolTip** _tip)
1508 ModelNode* node = (ModelNode*)NodeForPath(path);
1509 if (node == NULL)
1510 return false;
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));
1527 switch (columnIndex) {
1528 case 0:
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:
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;
1549 default:
1550 break;
1553 tipData += pieceData;
1555 tipData += "Editable: ";
1556 tipData += error == B_OK && location->IsWritable()
1557 ? "Yes" : "No";
1558 break;
1560 case 1:
1562 Value* value = node->GetValue();
1563 if (value != NULL)
1564 value->ToString(tipData);
1566 break;
1568 default:
1569 break;
1573 if (tipData.IsEmpty())
1574 return false;
1576 *_tip = new(std::nothrow) BTextToolTip(tipData);
1577 if (*_tip == NULL)
1578 return false;
1580 return true;
1584 status_t
1585 VariablesView::VariableTableModel::AddSyntheticNode(Variable* variable,
1586 ValueNodeChild*& _child, const char* presentationName)
1588 ValueNodeContainer* container = fNodeManager->GetContainer();
1589 AutoLocker<ValueNodeContainer> containerLocker(container);
1591 status_t error;
1592 if (_child == NULL) {
1593 _child = new(std::nothrow) VariableValueNodeChild(variable);
1594 if (_child == NULL)
1595 return B_NO_MEMORY;
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);
1606 if (error != B_OK)
1607 return error;
1609 _child->SetNode(valueNode);
1610 valueNode->ReleaseReference();
1613 container->AddChild(_child);
1615 error = _AddNode(variable, NULL, _child);
1616 if (error != B_OK) {
1617 container->RemoveChild(_child);
1618 return error;
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);
1628 ModelNode* childNode = fNodeTable.Lookup(_child);
1629 if (childNode != NULL) {
1630 if (presentationName != NULL)
1631 childNode->SetPresentationName(presentationName);
1633 ValueNode* valueNode = _child->Node();
1634 if (valueNode->LocationAndValueResolutionState()
1635 == VALUE_NODE_UNRESOLVED) {
1636 fContainerListener->ModelNodeValueRequested(childNode);
1637 } else
1638 ValueNodeValueChanged(valueNode);
1640 ValueNodeChildrenCreated(_child->Node());
1642 return B_OK;
1646 void
1647 VariablesView::VariableTableModel::RemoveSyntheticNode(ModelNode* node)
1649 int32 index = fNodes.IndexOf(node);
1650 if (index < 0)
1651 return;
1653 fNodeTable.Remove(node);
1655 fNodes.RemoveItemAt(index);
1657 NotifyNodesRemoved(TreeTablePath(), index, 1);
1659 node->ReleaseReference();
1663 status_t
1664 VariablesView::VariableTableModel::_AddNode(Variable* variable,
1665 ModelNode* parent, ValueNodeChild* nodeChild, bool isPresentationNode,
1666 bool isOnlyChild)
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;
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;
1680 int32 childIndex;
1682 if (parent != NULL) {
1683 childIndex = parent->CountChildren();
1685 if (!parent->AddChild(node))
1686 return B_NO_MEMORY;
1687 // the parent has a reference, now
1688 } else {
1689 childIndex = fNodes.CountItems();
1691 if (!fNodes.AddItem(node))
1692 return B_NO_MEMORY;
1693 nodeReference.Detach();
1694 // the fNodes list has a reference, now
1697 fNodeTable.Insert(node);
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);
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);
1720 // notify table model listeners
1721 if (!node->IsHidden()) {
1722 TreeTablePath path;
1723 if (parent == NULL || GetTreePath(parent, path))
1724 NotifyNodesAdded(path, childIndex, 1);
1727 // if the node is hidden, add its children
1728 if (node->IsHidden())
1729 fNodeManager->AddChildNodes(nodeChild);
1731 return B_OK;
1735 bool
1736 VariablesView::VariableTableModel::GetTreePath(ModelNode* node,
1737 TreeTablePath& _path) const
1739 // recurse, if the node has a parent
1740 if (ModelNode* parent = node->Parent()) {
1741 if (!GetTreePath(parent, _path))
1742 return false;
1744 if (node->IsHidden())
1745 return true;
1747 return _path.AddComponent(parent->IndexOf(node));
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);
1757 // #pragma mark - VariablesView
1760 VariablesView::VariablesView(Listener* listener)
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)
1779 SetName("Variables");
1783 VariablesView::~VariablesView()
1785 if (fEditWindow != NULL)
1786 BMessenger(fEditWindow).SendMessage(B_QUIT_REQUESTED);
1788 SetStackFrame(NULL, NULL);
1789 fVariableTable->SetTreeTableModel(NULL);
1791 if (fPreviousViewState != NULL)
1792 fPreviousViewState->ReleaseReference();
1793 delete fViewStateHistory;
1795 if (fVariableTableModel != NULL) {
1796 fVariableTableModel->SetContainerListener(NULL);
1797 delete fVariableTableModel;
1800 delete fContainerListener;
1801 if (fPendingTypecastInfo != NULL)
1802 fPendingTypecastInfo->ReleaseReference();
1804 if (fTemporaryExpression != NULL)
1805 fTemporaryExpression->ReleaseReference();
1809 /*static*/ VariablesView*
1810 VariablesView::Create(Listener* listener, ValueNodeManager* manager)
1812 VariablesView* self = new VariablesView(listener);
1814 try {
1815 self->_Init(manager);
1816 } catch (...) {
1817 delete self;
1818 throw;
1821 return self;
1825 void
1826 VariablesView::SetStackFrame(::Thread* thread, StackFrame* stackFrame)
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.
1835 fFrameClearPending = false;
1837 if (thread == fThread && stackFrame == fStackFrame)
1838 return;
1840 _SaveViewState(updateValues);
1842 _FinishContextMenu(true);
1844 for (int32 i = 0; i < fExpressionChildren.CountItems(); i++)
1845 fExpressionChildren.ItemAt(i)->ReleaseReference();
1846 fExpressionChildren.MakeEmpty();
1848 if (fThread != NULL)
1849 fThread->ReleaseReference();
1850 if (fStackFrame != NULL)
1851 fStackFrame->ReleaseReference();
1853 fThread = thread;
1854 fStackFrame = stackFrame;
1856 if (fThread != NULL)
1857 fThread->AcquireReference();
1858 if (fStackFrame != NULL)
1859 fStackFrame->AcquireReference();
1861 fVariableTableModel->SetStackFrame(fThread, fStackFrame);
1863 // request loading the parameter and variable values
1864 if (fThread != NULL && fStackFrame != NULL) {
1865 AutoLocker<Team> locker(fThread->GetTeam());
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);
1874 _RestoreExpressionNodes();
1877 _RestoreViewState();
1881 void
1882 VariablesView::MessageReceived(BMessage* message)
1884 switch (message->what) {
1885 case MSG_SHOW_INSPECTOR_WINDOW:
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;
1894 case MSG_SHOW_VARIABLE_EDIT_WINDOW:
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;
1905 Value* value = NULL;
1906 if (message->FindPointer("value", reinterpret_cast<void**>(
1907 &value)) != B_OK) {
1908 break;
1911 _HandleEditVariableRequest(node, value);
1913 break;
1915 case MSG_VARIABLE_EDIT_WINDOW_CLOSED:
1917 fEditWindow = NULL;
1918 break;
1920 case MSG_WRITE_VARIABLE_VALUE:
1922 Value* value = NULL;
1923 if (message->FindPointer("value", reinterpret_cast<void**>(
1924 &value)) != B_OK) {
1925 break;
1928 BReference<Value> valueReference(value, true);
1930 ValueNode* node = NULL;
1931 if (message->FindPointer("node", reinterpret_cast<void**>(
1932 &node)) != B_OK) {
1933 break;
1936 fListener->ValueNodeWriteRequested(node,
1937 fStackFrame->GetCpuState(), value);
1938 break;
1940 case MSG_SHOW_TYPECAST_NODE_PROMPT:
1942 BMessage* promptMessage = new(std::nothrow) BMessage(
1943 MSG_TYPECAST_NODE);
1945 if (promptMessage == NULL)
1946 return;
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;
1957 messageDeleter.Detach();
1958 promptWindow->CenterOnScreen();
1959 promptWindow->Show();
1960 break;
1962 case MSG_TYPECAST_NODE:
1964 ModelNode* node = NULL;
1965 if (message->FindPointer("node", reinterpret_cast<void **>(&node))
1966 != B_OK) {
1967 break;
1970 BString typeExpression;
1971 if (message->FindString("text", &typeExpression) == B_OK) {
1972 if (typeExpression.IsEmpty())
1973 break;
1975 if (fPendingTypecastInfo != NULL)
1976 fPendingTypecastInfo->ReleaseReference();
1978 fPendingTypecastInfo = new(std::nothrow)
1979 VariablesExpressionInfo(typeExpression, node);
1980 if (fPendingTypecastInfo == NULL) {
1981 // TODO: notify user
1982 break;
1985 fPendingTypecastInfo->AddListener(this);
1986 fListener->ExpressionEvaluationRequested(fPendingTypecastInfo,
1987 fStackFrame, fThread);
1989 break;
1991 case MSG_TYPECAST_TO_ARRAY:
1993 ModelNode* node = NULL;
1994 if (message->FindPointer("node", reinterpret_cast<void **>(&node))
1995 != B_OK) {
1996 break;
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;
2007 AddressType* addressType = NULL;
2008 BReference<Type> typeRef(arrayType, true);
2009 if (arrayType->CreateDerivedAddressType(DERIVED_TYPE_POINTER,
2010 addressType) != B_OK) {
2011 break;
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;
2022 typeRef.Detach();
2023 node->NodeChild()->SetNode(valueNode);
2024 node->SetCastedType(addressType);
2025 fVariableTableModel->NotifyNodeChanged(node);
2026 break;
2028 case MSG_SHOW_CONTAINER_RANGE_PROMPT:
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;
2040 bool fixedRange = valueNode->IsContainerRangeFixed();
2041 if (valueNode->SupportedChildRange(lowerBound, upperBound)
2042 != B_OK) {
2043 break;
2046 BMessage* promptMessage = new(std::nothrow) BMessage(
2047 MSG_SET_CONTAINER_RANGE);
2048 if (promptMessage == NULL)
2049 break;
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);
2063 PromptWindow* promptWindow = new(std::nothrow) PromptWindow(
2064 "Set Range", "Range: ", infoText.String(), BMessenger(this),
2065 promptMessage);
2066 if (promptWindow == NULL)
2067 return;
2069 messageDeleter.Detach();
2070 promptWindow->CenterOnScreen();
2071 promptWindow->Show();
2072 break;
2074 case MSG_SET_CONTAINER_RANGE:
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;
2085 bool fixedRange = message->FindBool("fixedRange");
2087 BString rangeExpression = message->FindString("text");
2088 if (rangeExpression.Length() == 0)
2089 break;
2091 RangeList ranges;
2092 status_t result = UiUtils::ParseRangeExpression(
2093 rangeExpression, lowerBound, upperBound, fixedRange, ranges);
2094 if (result != B_OK)
2095 break;
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;
2106 break;
2108 case MSG_SHOW_WATCH_VARIABLE_PROMPT:
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;
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;
2124 case MSG_ADD_WATCH_EXPRESSION:
2126 BMessage looperMessage(MSG_SHOW_EXPRESSION_PROMPT_WINDOW);
2127 looperMessage.AddPointer("target", this);
2128 Looper()->PostMessage(&looperMessage);
2129 break;
2131 case MSG_REMOVE_WATCH_EXPRESSION:
2133 ModelNode* node;
2134 if (message->FindPointer("node", reinterpret_cast<void**>(&node))
2135 != B_OK) {
2136 break;
2139 _RemoveExpression(node);
2140 break;
2142 case MSG_ADD_NEW_EXPRESSION:
2144 const char* expression;
2145 if (message->FindString("expression", &expression) != B_OK)
2146 break;
2148 bool persistentExpression = message->FindBool("persistent");
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;
2158 fListener->ExpressionEvaluationRequested(info, fStackFrame,
2159 fThread);
2160 break;
2162 case MSG_EXPRESSION_EVALUATED:
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;
2173 BReference<ExpressionResult> valueReference;
2174 if (message->FindPointer("value", reinterpret_cast<void**>(&value))
2175 == B_OK) {
2176 valueReference.SetTo(value, true);
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;
2187 } else {
2188 _AddExpressionNode(info, result, value);
2189 if (info == fTemporaryExpression) {
2190 info->ReleaseReference();
2191 fTemporaryExpression = NULL;
2195 break;
2197 case MSG_VALUE_NODE_CHANGED:
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);
2209 fVariableTableModel->ValueNodeChanged(nodeChild, oldNode,
2210 newNode);
2213 break;
2215 case MSG_VALUE_NODE_CHILDREN_CREATED:
2217 ValueNode* node;
2218 if (message->FindPointer("node", (void**)&node) == B_OK) {
2219 BReference<ValueNode> newNodeReference(node, true);
2220 fVariableTableModel->ValueNodeChildrenCreated(node);
2223 break;
2225 case MSG_VALUE_NODE_CHILDREN_DELETED:
2227 ValueNode* node;
2228 if (message->FindPointer("node", (void**)&node) == B_OK) {
2229 BReference<ValueNode> newNodeReference(node, true);
2230 fVariableTableModel->ValueNodeChildrenDeleted(node);
2233 break;
2235 case MSG_VALUE_NODE_VALUE_CHANGED:
2237 ValueNode* node;
2238 if (message->FindPointer("node", (void**)&node) == B_OK) {
2239 BReference<ValueNode> newNodeReference(node, true);
2240 fVariableTableModel->ValueNodeValueChanged(node);
2243 break;
2245 case MSG_RESTORE_PARTIAL_VIEW_STATE:
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);
2265 break;
2267 case MSG_VALUE_NODE_NEEDS_VALUE:
2268 case MSG_MODEL_NODE_HIDDEN:
2270 ModelNode* node;
2271 if (message->FindPointer("node", (void**)&node) == B_OK) {
2272 BReference<ModelNode> modelNodeReference(node, true);
2273 _RequestNodeValue(node);
2276 break;
2278 case MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE:
2280 _FinishContextMenu(false);
2281 break;
2283 case MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED:
2285 ModelNode* node;
2286 if (message->FindPointer("node", (void**)&node) != B_OK)
2287 break;
2288 BReference<ModelNode> nodeReference(node, true);
2290 fVariableTableModel->NotifyNodeChanged(node);
2291 break;
2293 case B_COPY:
2295 _CopyVariableValueToClipboard();
2296 break;
2298 default:
2299 BGroupView::MessageReceived(message);
2300 break;
2305 void
2306 VariablesView::DetachedFromWindow()
2308 _FinishContextMenu(true);
2312 void
2313 VariablesView::LoadSettings(const BMessage& settings)
2315 BMessage tableSettings;
2316 if (settings.FindMessage("variableTable", &tableSettings) == B_OK) {
2317 GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
2318 fVariableTable);
2323 status_t
2324 VariablesView::SaveSettings(BMessage& settings)
2326 settings.MakeEmpty();
2328 BMessage tableSettings;
2329 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
2330 fVariableTable);
2331 if (result == B_OK)
2332 result = settings.AddMessage("variableTable", &tableSettings);
2334 return result;
2338 void
2339 VariablesView::SetStackFrameClearPending()
2341 fFrameClearPending = true;
2345 void
2346 VariablesView::TreeTableNodeExpandedChanged(TreeTable* table,
2347 const TreeTablePath& path, bool expanded)
2349 if (fFrameClearPending)
2350 return;
2352 if (expanded) {
2353 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path);
2354 if (node == NULL)
2355 return;
2357 fVariableTableModel->NodeExpanded(node);
2359 // request the values of all children that don't have any yet
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;
2369 // request the values
2370 for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) {
2371 if (child->IsPresentationNode())
2372 continue;
2374 _RequestNodeValue(child);
2380 void
2381 VariablesView::TreeTableNodeInvoked(TreeTable* table,
2382 const TreeTablePath& path)
2384 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path);
2385 if (node == NULL)
2386 return;
2388 ValueNodeChild* child = node->NodeChild();
2390 if (child->LocationResolutionState() != B_OK)
2391 return;
2393 ValueLocation* location = child->Location();
2394 if (!location->IsWritable())
2395 return;
2397 Value* value = node->GetValue();
2398 if (value == NULL)
2399 return;
2401 BMessage message(MSG_SHOW_VARIABLE_EDIT_WINDOW);
2402 message.AddPointer("node", node);
2403 message.AddPointer("value", value);
2405 BMessenger(this).SendMessage(&message);
2409 void
2410 VariablesView::TreeTableCellMouseDown(TreeTable* table,
2411 const TreeTablePath& path, int32 columnIndex, BPoint screenWhere,
2412 uint32 buttons)
2414 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0)
2415 return;
2417 if (fFrameClearPending)
2418 return;
2420 _FinishContextMenu(true);
2422 ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path);
2423 if (node == NULL)
2424 return;
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;
2443 TableCellContextMenuTracker* tracker = new(std::nothrow)
2444 TableCellContextMenuTracker(node, Looper(), this);
2445 BReference<TableCellContextMenuTracker> trackerReference(tracker);
2447 ContextActionList* preActionList;
2448 ContextActionList* postActionList;
2450 error = _GetContextActionsForNode(node, preActionList, postActionList);
2451 if (error != B_OK)
2452 return;
2454 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter(
2455 preActionList);
2457 BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter(
2458 postActionList);
2460 if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList,
2461 postActionList) != B_OK) {
2462 return;
2465 fTableCellContextMenuTracker = trackerReference.Detach();
2466 fTableCellContextMenuTracker->ShowMenu(screenWhere);
2470 void
2471 VariablesView::ExpressionEvaluated(ExpressionInfo* info, status_t result,
2472 ExpressionResult* value)
2474 BMessage message(MSG_EXPRESSION_EVALUATED);
2475 message.AddPointer("info", info);
2476 message.AddInt32("result", result);
2477 BReference<ExpressionResult> valueReference;
2479 if (value != NULL) {
2480 valueReference.SetTo(value);
2481 message.AddPointer("value", value);
2484 if (BMessenger(this).SendMessage(&message) == B_OK)
2485 valueReference.Detach();
2489 void
2490 VariablesView::_Init(ValueNodeManager* manager)
2492 fVariableTable = new TreeTable("variable list", 0, B_FANCY_BORDER);
2493 AddChild(fVariableTable->ToView());
2494 fVariableTable->SetSortingEnabled(false);
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));
2504 fVariableTableModel = new VariableTableModel(manager);
2505 if (fVariableTableModel->Init() != B_OK)
2506 throw std::bad_alloc();
2507 fVariableTable->SetTreeTableModel(fVariableTableModel);
2508 fVariableTable->SetToolTipProvider(fVariableTableModel);
2510 fContainerListener = new ContainerListener(this);
2511 fVariableTableModel->SetContainerListener(fContainerListener);
2513 fVariableTable->AddTreeTableListener(this);
2515 fViewStateHistory = new VariablesViewStateHistory;
2516 if (fViewStateHistory->Init() != B_OK)
2517 throw std::bad_alloc();
2519 fExpressions = new ExpressionInfoTable();
2520 if (fExpressions->Init() != B_OK)
2521 throw std::bad_alloc();
2525 void
2526 VariablesView::_RequestNodeValue(ModelNode* node)
2528 // get the node child and its container
2529 ValueNodeChild* nodeChild = node->NodeChild();
2530 ValueNodeContainer* container = nodeChild->Container();
2532 BReference<ValueNodeContainer> containerReference(container);
2533 AutoLocker<ValueNodeContainer> containerLocker(container);
2535 if (container == NULL || nodeChild->Container() != container)
2536 return;
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;
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);
2555 if (valueNode == NULL || valueNode->LocationAndValueResolutionState()
2556 != VALUE_NODE_UNRESOLVED) {
2557 return;
2560 BReference<ValueNode> valueNodeReference(valueNode);
2561 containerLocker.Unlock();
2563 // request resolution of the value
2564 fListener->ValueNodeValueRequested(fStackFrame != NULL
2565 ? fStackFrame->GetCpuState() : NULL, container, valueNode);
2569 status_t
2570 VariablesView::_GetContextActionsForNode(ModelNode* node,
2571 ContextActionList*& _preActions, ContextActionList*& _postActions)
2573 _preActions = NULL;
2574 _postActions = NULL;
2576 ValueLocation* location = node->NodeChild()->Location();
2578 _preActions = new(std::nothrow) ContextActionList;
2579 if (_preActions == NULL)
2580 return B_NO_MEMORY;
2582 BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter(
2583 _preActions);
2585 status_t result = B_OK;
2586 BMessage* message = NULL;
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);
2599 ValueNode* valueNode = node->NodeChild()->Node();
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);
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);
2621 result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS,
2622 MSG_SHOW_TYPECAST_NODE_PROMPT, _preActions, message);
2623 if (result != B_OK)
2624 return result;
2626 result = _AddContextAction("Watch" B_UTF8_ELLIPSIS,
2627 MSG_SHOW_WATCH_VARIABLE_PROMPT, _preActions, message);
2628 if (result != B_OK)
2629 return result;
2631 if (valueNode == NULL)
2632 return B_OK;
2634 if (valueNode->LocationAndValueResolutionState() == B_OK) {
2635 result = _AddContextAction("Copy Value", B_COPY, _preActions, message);
2636 if (result != B_OK)
2637 return result;
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;
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;
2660 _postActions = new(std::nothrow) ContextActionList;
2661 if (_postActions == NULL)
2662 return B_NO_MEMORY;
2664 BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter(
2665 _postActions);
2667 result = _AddContextAction("Add watch expression" B_UTF8_ELLIPSIS,
2668 MSG_ADD_WATCH_EXPRESSION, _postActions, message);
2669 if (result != B_OK)
2670 return result;
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);
2680 preActionListDeleter.Detach();
2681 postActionListDeleter.Detach();
2683 return B_OK;
2687 status_t
2688 VariablesView::_AddContextAction(const char* action, uint32 what,
2689 ContextActionList* actions, BMessage*& _message)
2691 _message = new(std::nothrow) BMessage(what);
2692 if (_message == NULL)
2693 return B_NO_MEMORY;
2695 ObjectDeleter<BMessage> messageDeleter(_message);
2697 ActionMenuItem* item = new(std::nothrow) ActionMenuItem(action,
2698 _message);
2699 if (item == NULL)
2700 return B_NO_MEMORY;
2702 messageDeleter.Detach();
2703 ObjectDeleter<ActionMenuItem> actionDeleter(item);
2704 if (!actions->AddItem(item))
2705 return B_NO_MEMORY;
2707 actionDeleter.Detach();
2709 return B_OK;
2713 void
2714 VariablesView::_FinishContextMenu(bool force)
2716 if (fTableCellContextMenuTracker != NULL) {
2717 if (!fTableCellContextMenuTracker->FinishMenu(force) || force) {
2718 fTableCellContextMenuTracker->ReleaseReference();
2719 fTableCellContextMenuTracker = NULL;
2726 void
2727 VariablesView::_SaveViewState(bool updateValues) const
2729 if (fThread == NULL || fStackFrame == NULL
2730 || fStackFrame->Function() == NULL) {
2731 return;
2734 // get the function ID
2735 FunctionID* functionID = fStackFrame->Function()->GetFunctionID();
2736 if (functionID == NULL)
2737 return;
2738 BReference<FunctionID> functionIDReference(functionID, true);
2740 StackFrameValues* values = NULL;
2741 ExpressionValues* expressionValues = NULL;
2742 BReference<StackFrameValues> valuesReference;
2743 BReference<ExpressionValues> expressionsReference;
2745 if (!updateValues) {
2746 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(),
2747 functionID);
2748 if (viewState != NULL) {
2749 values = viewState->Values();
2750 valuesReference.SetTo(values);
2752 expressionValues = viewState->GetExpressionValues();
2753 expressionsReference.SetTo(expressionValues);
2757 if (values == NULL) {
2758 values = new(std::nothrow) StackFrameValues;
2759 if (values == NULL)
2760 return;
2761 valuesReference.SetTo(values, true);
2763 if (values->Init() != B_OK)
2764 return;
2766 expressionValues = new(std::nothrow) ExpressionValues;
2767 if (expressionValues == NULL)
2768 return;
2769 expressionsReference.SetTo(expressionValues, true);
2771 if (expressionValues->Init() != B_OK)
2772 return;
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);
2781 if (viewState->Init() != B_OK)
2782 return;
2784 viewState->SetValues(values);
2785 viewState->SetExpressionValues(expressionValues);
2787 // populate it
2788 TreeTablePath path;
2789 if (_AddViewStateDescendentNodeInfos(viewState,
2790 fVariableTableModel->Root(), path, updateValues) != B_OK) {
2791 return;
2794 // add the view state to the history
2795 fViewStateHistory->SetState(fThread->ID(), functionID, viewState);
2799 void
2800 VariablesView::_RestoreViewState()
2802 if (fPreviousViewState != NULL) {
2803 fPreviousViewState->ReleaseReference();
2804 fPreviousViewState = NULL;
2807 if (fThread == NULL || fStackFrame == NULL
2808 || fStackFrame->Function() == NULL) {
2809 return;
2812 // get the function ID
2813 FunctionID* functionID = fStackFrame->Function()->GetFunctionID();
2814 if (functionID == NULL)
2815 return;
2816 BReference<FunctionID> functionIDReference(functionID, true);
2818 // get the previous view state
2819 VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(),
2820 functionID);
2821 if (viewState == NULL)
2822 return;
2824 // apply the view state
2825 TreeTablePath path;
2826 _ApplyViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(),
2827 path);
2831 status_t
2832 VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState,
2833 void* parent, TreeTablePath& path, bool updateValues) const
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;
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());
2852 Value* value = node->GetValue();
2853 Variable* variable = node->GetVariable();
2854 TypeComponentPath* componentPath = node->GetPath();
2855 ObjectID* id = variable->ID();
2857 status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo);
2858 if (error != B_OK)
2859 return error;
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;
2870 // recurse
2871 error = _AddViewStateDescendentNodeInfos(viewState, node, path,
2872 updateValues);
2873 if (error != B_OK)
2874 return error;
2876 path.RemoveLastComponent();
2879 return B_OK;
2883 status_t
2884 VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState,
2885 void* parent, TreeTablePath& path)
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;
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);
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());
2918 fVariableTable->SetNodeExpanded(path,
2919 nodeInfo->IsNodeExpanded());
2921 BVariant previousValue;
2922 if (viewState->Values()->GetValue(objectID, componentPath,
2923 previousValue)) {
2924 node->SetPreviousValue(previousValue);
2928 // recurse
2929 status_t error = _ApplyViewStateDescendentNodeInfos(viewState, node,
2930 path);
2931 if (error != B_OK)
2932 return error;
2934 path.RemoveLastComponent();
2937 return B_OK;
2941 void
2942 VariablesView::_CopyVariableValueToClipboard()
2944 ModelNode* node = reinterpret_cast<ModelNode*>(
2945 fVariableTable->SelectionModel()->NodeAt(0));
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();
2961 status_t
2962 VariablesView::_AddExpression(const char* expression,
2963 bool persistentExpression, ExpressionInfo*& _info)
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;
2973 FunctionID* id = function->GetFunctionID();
2974 if (id == NULL)
2975 return B_NO_MEMORY;
2977 BReference<FunctionID> idReference(id, true);
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;
2992 ExpressionInfo* info = new(std::nothrow) ExpressionInfo(expression);
2994 if (info == NULL)
2995 return B_NO_MEMORY;
2997 BReference<ExpressionInfo> infoReference(info, true);
2999 if (persistentExpression) {
3000 if (!entry->AddItem(info))
3001 return B_NO_MEMORY;
3002 } else
3003 fTemporaryExpression = info;
3005 info->AddListener(this);
3006 infoReference.Detach();
3007 _info = info;
3008 return B_OK;
3012 void
3013 VariablesView::_RemoveExpression(ModelNode* node)
3015 if (!fExpressionChildren.HasItem(node->NodeChild()))
3016 return;
3018 FunctionID* id = fStackFrame->Function()->GetFunctionID();
3019 BReference<FunctionID> idReference(id, true);
3021 ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id));
3022 if (entry == NULL)
3023 return;
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;
3035 fVariableTableModel->RemoveSyntheticNode(node);
3039 void
3040 VariablesView::_RestoreExpressionNodes()
3042 FunctionInstance* instance = fStackFrame->Function();
3043 if (instance == NULL)
3044 return;
3046 FunctionID* id = instance->GetFunctionID();
3047 if (id == NULL)
3048 return;
3050 BReference<FunctionID> idReference(id, true);
3052 ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id));
3053 if (entry == NULL)
3054 return;
3056 for (int32 i = 0; i < entry->CountItems(); i++) {
3057 ExpressionInfo* info = entry->ItemAt(i);
3058 fListener->ExpressionEvaluationRequested(info, fStackFrame, fThread);
3063 void
3064 VariablesView::_AddExpressionNode(ExpressionInfo* info, status_t result,
3065 ExpressionResult* value)
3067 bool temporaryExpression = (info == fTemporaryExpression);
3068 Variable* variable = NULL;
3069 BReference<Variable> variableReference;
3070 BVariant valueData;
3072 ExpressionVariableID* id
3073 = new(std::nothrow) ExpressionVariableID(info);
3074 if (id == NULL)
3075 return;
3076 BReference<ObjectID> idReference(id, true);
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);
3089 location = new(std::nothrow) ValueLocation();
3090 if (location == NULL)
3091 return;
3092 locationReference.SetTo(location, true);
3094 if (valueData.IsNumber()) {
3096 ValuePieceLocation piece;
3097 if (!piece.SetToValue(valueData.Bytes(), valueData.Size())
3098 || !location->AddPiece(piece)) {
3099 return;
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);
3110 variable = new(std::nothrow) Variable(id,
3111 info->Expression(), type, location);
3112 if (variable == NULL)
3113 return;
3114 variableReference.SetTo(variable, true);
3116 status_t error = fVariableTableModel->AddSyntheticNode(variable, child,
3117 info->Expression());
3118 if (error != B_OK)
3119 return;
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;
3129 child->Node()->SetLocationAndValue(NULL, explicitValue, B_OK);
3132 if (temporaryExpression || fExpressionChildren.AddItem(child)) {
3133 child->AcquireReference();
3135 if (temporaryExpression)
3136 return;
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);
3157 void
3158 VariablesView::_HandleTypecastResult(status_t result, ExpressionResult* value)
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);
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);
3182 } else if (value->Kind() != EXPRESSION_RESULT_KIND_TYPE) {
3183 errorMessage.SetToFormat("Expression \"%s\" does not evaluate to a"
3184 " type.", fPendingTypecastInfo->Expression().String());
3187 if (!errorMessage.IsEmpty()) {
3188 BAlert* alert = new(std::nothrow) BAlert("Typecast error",
3189 errorMessage, "Close");
3190 if (alert != NULL)
3191 alert->Go();
3193 return;
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;
3205 node->NodeChild()->SetNode(valueNode);
3206 node->SetCastedType(type);
3207 fVariableTableModel->NotifyNodeChanged(node);
3211 void
3212 VariablesView::_HandleEditVariableRequest(ModelNode* node, Value* value)
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;
3221 ValueNode* valueNode = node->NodeChild()->Node();
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;
3231 BReference<TableCellValueEditor> editorReference(editor, true);
3233 try {
3234 fEditWindow = VariableEditWindow::Create(value, valueNode, editor,
3235 this);
3236 } catch (...) {
3237 fEditWindow = NULL;
3238 return;
3241 fEditWindow->Show();
3245 status_t
3246 VariablesView::_GetTypeForTypeCode(int32 type, Type*& _resultType) const
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;
3253 return B_OK;
3256 return B_NOT_SUPPORTED;
3260 // #pragma mark - Listener
3263 VariablesView::Listener::~Listener()