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