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