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