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