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