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