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