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