* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "VariablesView.h"
#include <stdio.h>
#include <new>
#include <Looper.h>
#include <PopUpMenu.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include "table/TableColumns.h"
#include "Architecture.h"
#include "FunctionID.h"
#include "FunctionInstance.h"
#include "MessageCodes.h"
#include "SettingsMenu.h"
#include "StackFrame.h"
#include "StackFrameValues.h"
#include "TableCellValueRenderer.h"
#include "Team.h"
#include "Thread.h"
#include "Tracing.h"
#include "TypeComponentPath.h"
#include "TypeHandlerRoster.h"
#include "Value.h"
#include "ValueHandler.h"
#include "ValueHandlerRoster.h"
#include "ValueNode.h"
#include "ValueNodeContainer.h"
#include "Variable.h"
#include "VariableValueNodeChild.h"
#include "VariablesViewState.h"
#include "VariablesViewStateHistory.h"
enum {
VALUE_NODE_TYPE = 'valn'
};
static const uint64 kMaxArrayElementCount = 10;
class VariablesView::ContainerListener : public ValueNodeContainer::Listener {
public:
ContainerListener(BHandler* indirectTarget);
void SetModel(VariableTableModel* model);
virtual void ValueNodeChanged(ValueNodeChild* nodeChild,
ValueNode* oldNode, ValueNode* newNode);
virtual void ValueNodeChildrenCreated(ValueNode* node);
virtual void ValueNodeChildrenDeleted(ValueNode* node);
virtual void ValueNodeValueChanged(ValueNode* node);
private:
BHandler* fIndirectTarget;
VariableTableModel* fModel;
};
class VariablesView::ModelNode : public BReferenceable {
public:
ModelNode(ModelNode* parent, ValueNodeChild* nodeChild,
bool isPresentationNode)
:
fParent(parent),
fNodeChild(nodeChild),
fValue(NULL),
fValueHandler(NULL),
fTableCellRenderer(NULL),
fIsPresentationNode(isPresentationNode),
fHidden(false)
{
fNodeChild->AcquireReference();
}
~ModelNode()
{
SetTableCellRenderer(NULL);
SetValueHandler(NULL);
SetValue(NULL);
for (int32 i = 0; ModelNode* child = fChildren.ItemAt(i); i++)
child->ReleaseReference();
fNodeChild->ReleaseReference();
}
ModelNode* Parent() const
{
return fParent;
}
ValueNodeChild* NodeChild() const
{
return fNodeChild;
}
const BString& Name() const
{
return fNodeChild->Name();
}
Type* GetType() const
{
return fNodeChild->GetType();
}
Value* GetValue() const
{
return fValue;
}
void SetValue(Value* value)
{
if (value == fValue)
return;
if (fValue != NULL)
fValue->ReleaseReference();
fValue = value;
if (fValue != NULL)
fValue->AcquireReference();
}
ValueHandler* GetValueHandler() const
{
return fValueHandler;
}
void SetValueHandler(ValueHandler* handler)
{
if (handler == fValueHandler)
return;
if (fValueHandler != NULL)
fValueHandler->ReleaseReference();
fValueHandler = handler;
if (fValueHandler != NULL)
fValueHandler->AcquireReference();
}
TableCellValueRenderer* TableCellRenderer() const
{
return fTableCellRenderer;
}
void SetTableCellRenderer(TableCellValueRenderer* renderer)
{
if (renderer == fTableCellRenderer)
return;
if (fTableCellRenderer != NULL)
fTableCellRenderer->ReleaseReference();
fTableCellRenderer = renderer;
if (fTableCellRenderer != NULL)
fTableCellRenderer->AcquireReference();
}
bool IsPresentationNode() const
{
return fIsPresentationNode;
}
bool IsHidden() const
{
return fHidden;
}
void SetHidden(bool hidden)
{
fHidden = hidden;
}
int32 CountChildren() const
{
return fChildren.CountItems();
}
ModelNode* ChildAt(int32 index) const
{
return fChildren.ItemAt(index);
}
int32 IndexOf(ModelNode* child) const
{
return fChildren.IndexOf(child);
}
bool AddChild(ModelNode* child)
{
if (!fChildren.AddItem(child))
return false;
child->AcquireReference();
return true;
}
private:
typedef BObjectList<ModelNode> ChildList;
private:
ModelNode* fParent;
ValueNodeChild* fNodeChild;
Value* fValue;
ValueHandler* fValueHandler;
TableCellValueRenderer* fTableCellRenderer;
ChildList fChildren;
bool fIsPresentationNode;
bool fHidden;
public:
ModelNode* fNext;
};
class VariablesView::VariableValueColumn : public StringTableColumn {
public:
VariableValueColumn(int32 modelIndex, const char* title, float width,
float minWidth, float maxWidth, uint32 truncate = B_TRUNCATE_MIDDLE,
alignment align = B_ALIGN_RIGHT)
:
StringTableColumn(modelIndex, title, width, minWidth, maxWidth,
truncate, align)
{
}
protected:
void DrawValue(const BVariant& value, BRect rect, BView* targetView)
{
if (value.Type() == VALUE_NODE_TYPE) {
ModelNode* node = dynamic_cast<ModelNode*>(value.ToReferenceable());
if (node != NULL && node->GetValue() != NULL
&& node->TableCellRenderer() != NULL) {
node->TableCellRenderer()->RenderValue(node->GetValue(), rect,
targetView);
return;
}
}
fField.SetString("");
fField.SetWidth(Width());
fColumn.DrawField(&fField, rect, targetView);
}
float GetPreferredWidth(const BVariant& value, BView* targetView) const
{
if (value.Type() == VALUE_NODE_TYPE) {
ModelNode* node = dynamic_cast<ModelNode*>(value.ToReferenceable());
if (node != NULL && node->GetValue() != NULL
&& node->TableCellRenderer() != NULL) {
return node->TableCellRenderer()->PreferredValueWidth(
node->GetValue(), targetView);
}
}
return fColumn.BTitledColumn::GetPreferredWidth(NULL, targetView);
}
virtual BField* PrepareField(const BVariant& _value) const
{
return NULL;
}
};
class VariablesView::VariableTableModel : public TreeTableModel {
public:
VariableTableModel();
~VariableTableModel();
status_t Init();
void SetContainerListener(
ContainerListener* listener);
void SetStackFrame(Thread* thread,
StackFrame* stackFrame);
void ValueNodeChanged(ValueNodeChild* nodeChild,
ValueNode* oldNode, ValueNode* newNode);
void ValueNodeChildrenCreated(ValueNode* node);
void ValueNodeChildrenDeleted(ValueNode* node);
void ValueNodeValueChanged(ValueNode* node);
virtual int32 CountColumns() const;
virtual void* Root() const;
virtual int32 CountChildren(void* parent) const;
virtual void* ChildAt(void* parent, int32 index) const;
virtual bool GetValueAt(void* object, int32 columnIndex,
BVariant& _value);
void NodeExpanded(ModelNode* node);
void NotifyNodeChanged(ModelNode* node);
private:
struct NodeHashDefinition {
typedef ValueNodeChild* KeyType;
typedef ModelNode ValueType;
size_t HashKey(const ValueNodeChild* key) const
{
return (size_t)key;
}
size_t Hash(const ModelNode* value) const
{
return HashKey(value->NodeChild());
}
bool Compare(const ValueNodeChild* key,
const ModelNode* value) const
{
return value->NodeChild() == key;
}
ModelNode*& GetLink(ModelNode* value) const
{
return value->fNext;
}
};
typedef BObjectList<ModelNode> NodeList;
typedef BOpenHashTable<NodeHashDefinition> NodeTable;
private:
status_t _AddNode(ModelNode* parent,
ValueNodeChild* nodeChild,
bool isPresentationNode = false);
void _AddNode(Variable* variable);
status_t _CreateValueNode(ValueNodeChild* nodeChild);
status_t _AddChildNodes(ValueNodeChild* nodeChild);
bool _GetTreePath(ModelNode* node,
TreeTablePath& _path) const;
private:
Thread* fThread;
StackFrame* fStackFrame;
ValueNodeContainer* fContainer;
ContainerListener* fContainerListener;
NodeList fNodes;
NodeTable fNodeTable;
};
class VariablesView::ContextMenu : public BPopUpMenu {
public:
ContextMenu(const BMessenger& parent, const char* name)
: BPopUpMenu(name, false, false),
fParent(parent)
{
}
virtual void Hide()
{
BPopUpMenu::Hide();
BMessage message(MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE);
message.AddPointer("menu", this);
fParent.SendMessage(&message);
}
private:
BMessenger fParent;
};
class VariablesView::TableCellContextMenuTracker : public BReferenceable,
Settings::Listener {
public:
TableCellContextMenuTracker(ModelNode* node, BLooper* parentLooper,
const BMessenger& parent)
:
fNode(node),
fParentLooper(parentLooper),
fParent(parent),
fRendererSettings(NULL),
fRendererSettingsMenu(NULL),
fRendererMenuAdded(false),
fMenuPreparedToShow(false)
{
fNode->AcquireReference();
}
~TableCellContextMenuTracker()
{
FinishMenu(true);
if (fRendererSettingsMenu != NULL)
fRendererSettingsMenu->ReleaseReference();
if (fRendererSettings != NULL)
fRendererSettings->ReleaseReference();
fNode->ReleaseReference();
}
status_t Init(Settings* rendererSettings,
SettingsMenu* rendererSettingsMenu)
{
fRendererSettings = rendererSettings;
fRendererSettings->AcquireReference();
fRendererSettingsMenu = rendererSettingsMenu;
fRendererSettingsMenu->AcquireReference();
fContextMenu = new(std::nothrow) ContextMenu(fParent,
"table cell settings popup");
if (fContextMenu == NULL)
return B_NO_MEMORY;
status_t error = fRendererSettingsMenu->AddToMenu(fContextMenu, 0);
if (error != B_OK)
return error;
AutoLocker<Settings> settingsLocker(fRendererSettings);
fRendererSettings->AddListener(this);
fRendererMenuAdded = true;
return B_OK;
}
void ShowMenu(BPoint screenWhere)
{
fRendererSettingsMenu->PrepareToShow(fParentLooper);
fMenuPreparedToShow = true;
BRect mouseRect(screenWhere, screenWhere);
mouseRect.InsetBy(-4.0, -4.0);
fContextMenu->Go(screenWhere, true, false, mouseRect, true);
}
bool FinishMenu(bool force)
{
bool stillActive = false;
if (fMenuPreparedToShow) {
stillActive = fRendererSettingsMenu->Finish(fParentLooper, force);
fMenuPreparedToShow = stillActive;
}
if (fRendererMenuAdded) {
fRendererSettingsMenu->RemoveFromMenu();
fRendererSettings->RemoveListener(this);
fRendererMenuAdded = false;
}
if (fContextMenu != NULL) {
delete fContextMenu;
fContextMenu = NULL;
}
return stillActive;
}
private:
virtual void SettingValueChanged(Setting* setting)
{
BMessage message(MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED);
fNode->AcquireReference();
if (message.AddPointer("node", fNode) != B_OK
|| fParent.SendMessage(&message) != B_OK) {
fNode->ReleaseReference();
}
}
private:
ModelNode* fNode;
BLooper* fParentLooper;
BMessenger fParent;
ContextMenu* fContextMenu;
Settings* fRendererSettings;
SettingsMenu* fRendererSettingsMenu;
bool fRendererMenuAdded;
bool fMenuPreparedToShow;
};
VariablesView::ContainerListener::ContainerListener(BHandler* indirectTarget)
:
fIndirectTarget(indirectTarget),
fModel(NULL)
{
}
void
VariablesView::ContainerListener::SetModel(VariableTableModel* model)
{
fModel = model;
}
void
VariablesView::ContainerListener::ValueNodeChanged(ValueNodeChild* nodeChild,
ValueNode* oldNode, ValueNode* newNode)
{
if (fIndirectTarget->Looper()->IsLocked()) {
fModel->ValueNodeChanged(nodeChild, oldNode, newNode);
return;
}
BReference<ValueNodeChild> nodeChildReference(nodeChild);
BReference<ValueNode> oldNodeReference(oldNode);
BReference<ValueNode> newNodeReference(newNode);
BMessage message(MSG_VALUE_NODE_CHANGED);
if (message.AddPointer("nodeChild", nodeChild) == B_OK
&& message.AddPointer("oldNode", oldNode) == B_OK
&& message.AddPointer("newNode", newNode) == B_OK
&& fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget)
== B_OK) {
nodeChildReference.Detach();
oldNodeReference.Detach();
newNodeReference.Detach();
}
}
void
VariablesView::ContainerListener::ValueNodeChildrenCreated(ValueNode* node)
{
if (fIndirectTarget->Looper()->IsLocked()) {
fModel->ValueNodeChildrenCreated(node);
return;
}
BReference<ValueNode> nodeReference(node);
BMessage message(MSG_VALUE_NODE_CHILDREN_CREATED);
if (message.AddPointer("node", node) == B_OK
&& fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget)
== B_OK) {
nodeReference.Detach();
}
}
void
VariablesView::ContainerListener::ValueNodeChildrenDeleted(ValueNode* node)
{
if (fIndirectTarget->Looper()->IsLocked()) {
fModel->ValueNodeChildrenDeleted(node);
return;
}
BReference<ValueNode> nodeReference(node);
BMessage message(MSG_VALUE_NODE_CHILDREN_DELETED);
if (message.AddPointer("node", node) == B_OK
&& fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget)
== B_OK) {
nodeReference.Detach();
}
}
void
VariablesView::ContainerListener::ValueNodeValueChanged(ValueNode* node)
{
if (fIndirectTarget->Looper()->IsLocked()) {
fModel->ValueNodeValueChanged(node);
return;
}
BReference<ValueNode> nodeReference(node);
BMessage message(MSG_VALUE_NODE_VALUE_CHANGED);
if (message.AddPointer("node", node) == B_OK
&& fIndirectTarget->Looper()->PostMessage(&message, fIndirectTarget)
== B_OK) {
nodeReference.Detach();
}
}
VariablesView::VariableTableModel::VariableTableModel()
:
fStackFrame(NULL),
fContainer(NULL),
fContainerListener(NULL),
fNodeTable()
{
}
VariablesView::VariableTableModel::~VariableTableModel()
{
SetStackFrame(NULL, NULL);
}
status_t
VariablesView::VariableTableModel::Init()
{
return fNodeTable.Init();
}
void
VariablesView::VariableTableModel::SetContainerListener(
ContainerListener* listener)
{
if (listener == fContainerListener)
return;
if (fContainerListener != NULL) {
if (fContainer != NULL) {
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
fContainer->RemoveListener(fContainerListener);
}
fContainerListener->SetModel(NULL);
}
fContainerListener = listener;
if (fContainerListener != NULL) {
fContainerListener->SetModel(this);
if (fContainer != NULL) {
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
fContainer->AddListener(fContainerListener);
}
}
}
void
VariablesView::VariableTableModel::SetStackFrame(Thread* thread,
StackFrame* stackFrame)
{
if (fContainer != NULL) {
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
if (fContainerListener != NULL)
fContainer->RemoveListener(fContainerListener);
fContainer->RemoveAllChildren();
containerLocker.Unlock();
fContainer->ReleaseReference();
fContainer = NULL;
}
fNodeTable.Clear(true);
if (!fNodes.IsEmpty()) {
int32 count = fNodes.CountItems();
for (int32 i = 0; i < count; i++)
fNodes.ItemAt(i)->ReleaseReference();
fNodes.MakeEmpty();
NotifyNodesRemoved(TreeTablePath(), 0, count);
}
fStackFrame = stackFrame;
fThread = thread;
if (fStackFrame != NULL) {
fContainer = new(std::nothrow) ValueNodeContainer;
if (fContainer == NULL)
return;
status_t error = fContainer->Init();
if (error != B_OK) {
delete fContainer;
fContainer = NULL;
return;
}
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
if (fContainerListener != NULL)
fContainer->AddListener(fContainerListener);
for (int32 i = 0; Variable* variable = fStackFrame->ParameterAt(i);
i++) {
_AddNode(variable);
}
for (int32 i = 0; Variable* variable
= fStackFrame->LocalVariableAt(i); i++) {
_AddNode(variable);
}
}
}
void
VariablesView::VariableTableModel::ValueNodeChanged(ValueNodeChild* nodeChild,
ValueNode* oldNode, ValueNode* newNode)
{
if (fContainer == NULL)
return;
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
}
void
VariablesView::VariableTableModel::ValueNodeChildrenCreated(
ValueNode* valueNode)
{
if (fContainer == NULL)
return;
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
ValueNodeChild* nodeChild = valueNode->NodeChild();
if (nodeChild == NULL)
return;
ModelNode* modelNode = fNodeTable.Lookup(nodeChild);
if (modelNode == NULL)
return;
int32 childCount = valueNode->CountChildren();
for (int32 i = 0; i < childCount; i++) {
ValueNodeChild* child = valueNode->ChildAt(i);
if (fNodeTable.Lookup(child) == NULL)
_AddNode(modelNode, child, child->IsInternal());
}
}
void
VariablesView::VariableTableModel::ValueNodeChildrenDeleted(ValueNode* node)
{
if (fContainer == NULL)
return;
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
}
void
VariablesView::VariableTableModel::ValueNodeValueChanged(ValueNode* valueNode)
{
if (fContainer == NULL)
return;
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
ValueNodeChild* nodeChild = valueNode->NodeChild();
if (nodeChild == NULL)
return;
ModelNode* modelNode = fNodeTable.Lookup(nodeChild);
if (modelNode == NULL)
return;
Value* value = valueNode->GetValue();
if (value == modelNode->GetValue())
return;
ValueHandler* valueHandler;
status_t error = ValueHandlerRoster::Default()->FindValueHandler(value,
valueHandler);
if (error != B_OK)
return;
BReference<ValueHandler> handlerReference(valueHandler, true);
TableCellValueRenderer* renderer = NULL;
error = valueHandler->GetTableCellValueRenderer(value, renderer);
if (error != B_OK)
return;
modelNode->SetValue(value);
modelNode->SetValueHandler(valueHandler);
modelNode->SetTableCellRenderer(renderer);
NotifyNodeChanged(modelNode);
}
int32
VariablesView::VariableTableModel::CountColumns() const
{
return 2;
}
void*
VariablesView::VariableTableModel::Root() const
{
return (void*)this;
}
int32
VariablesView::VariableTableModel::CountChildren(void* parent) const
{
if (parent == this)
return fNodes.CountItems();
ModelNode* modelNode = (ModelNode*)parent;
int32 childCount = modelNode->CountChildren();
if (childCount == 1) {
ModelNode* child = modelNode->ChildAt(0);
if (child->IsHidden())
return child->CountChildren();
}
return childCount;
}
void*
VariablesView::VariableTableModel::ChildAt(void* parent, int32 index) const
{
if (parent == this)
return fNodes.ItemAt(index);
ModelNode* modelNode = (ModelNode*)parent;
int32 childCount = modelNode->CountChildren();
if (childCount == 1) {
ModelNode* child = modelNode->ChildAt(0);
if (child->IsHidden())
return child->ChildAt(index);
}
return modelNode->ChildAt(index);
}
bool
VariablesView::VariableTableModel::GetValueAt(void* object, int32 columnIndex,
BVariant& _value)
{
ModelNode* node = (ModelNode*)object;
switch (columnIndex) {
case 0:
_value.SetTo(node->Name(), B_VARIANT_DONT_COPY_DATA);
return true;
case 1:
if (node->GetValue() == NULL)
return false;
_value.SetTo(node, VALUE_NODE_TYPE);
return true;
default:
return false;
}
}
void
VariablesView::VariableTableModel::NodeExpanded(ModelNode* node)
{
if (fContainer == NULL)
return;
AutoLocker<ValueNodeContainer> containerLocker(fContainer);
if (node->CountChildren() == 1) {
ModelNode* child = node->ChildAt(0);
if (child->IsHidden())
node = child;
}
for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++)
_AddChildNodes(child->NodeChild());
}
void
VariablesView::VariableTableModel::NotifyNodeChanged(ModelNode* node)
{
if (!node->IsHidden()) {
TreeTablePath treePath;
if (_GetTreePath(node, treePath)) {
int32 index = treePath.RemoveLastComponent();
NotifyNodesChanged(treePath, index, 1);
}
}
}
status_t
VariablesView::VariableTableModel::_AddNode(ModelNode* parent,
ValueNodeChild* nodeChild, bool isPresentationNode)
{
Type* nodeChildRawType = nodeChild->GetType()->ResolveRawType(false);
if (nodeChildRawType->Kind() == TYPE_UNSPECIFIED)
return B_OK;
ModelNode* node = new(std::nothrow) ModelNode(parent, nodeChild,
isPresentationNode);
BReference<ModelNode> nodeReference(node, true);
if (node == NULL)
return B_NO_MEMORY;
int32 childIndex;
if (parent != NULL) {
childIndex = parent->CountChildren();
if (!parent->AddChild(node))
return B_NO_MEMORY;
} else {
childIndex = fNodes.CountItems();
if (!fNodes.AddItem(node))
return B_NO_MEMORY;
nodeReference.Detach();
}
fNodeTable.Insert(node);
if (parent != NULL) {
ValueNode* parentValueNode = parent->NodeChild()->Node();
if (parentValueNode != NULL
&& parentValueNode->GetType()->ResolveRawType(false)->Kind()
== TYPE_ADDRESS
&& nodeChildRawType->Kind() == TYPE_COMPOUND) {
node->SetHidden(true);
}
}
if (!node->IsHidden()) {
TreeTablePath path;
if (parent == NULL || _GetTreePath(parent, path))
NotifyNodesAdded(path, childIndex, 1);
}
if (node->IsHidden())
_AddChildNodes(nodeChild);
return B_OK;
}
void
VariablesView::VariableTableModel::_AddNode(Variable* variable)
{
ValueNodeChild* nodeChild = new (std::nothrow) VariableValueNodeChild(
variable);
BReference<ValueNodeChild> nodeChildReference(nodeChild, true);
if (nodeChild == NULL || !fContainer->AddChild(nodeChild)) {
delete nodeChild;
return;
}
status_t error = _AddNode(NULL, nodeChild, false);
if (error != B_OK)
return;
_AddChildNodes(nodeChild);
}
status_t
VariablesView::VariableTableModel::_CreateValueNode(ValueNodeChild* nodeChild)
{
if (nodeChild->Node() != NULL)
return B_OK;
ValueNode* valueNode;
status_t error;
if (nodeChild->IsInternal()) {
error = nodeChild->CreateInternalNode(valueNode);
} else {
error = TypeHandlerRoster::Default()->CreateValueNode(nodeChild,
nodeChild->GetType(), valueNode);
}
if (error != B_OK)
return error;
nodeChild->SetNode(valueNode);
valueNode->ReleaseReference();
return B_OK;
}
status_t
VariablesView::VariableTableModel::_AddChildNodes(ValueNodeChild* nodeChild)
{
ValueNode* valueNode = nodeChild->Node();
if (valueNode == NULL) {
status_t error = _CreateValueNode(nodeChild);
if (error != B_OK)
return error;
valueNode = nodeChild->Node();
}
if (valueNode->ChildrenCreated())
return B_OK;
return valueNode->CreateChildren();
}
bool
VariablesView::VariableTableModel::_GetTreePath(ModelNode* node,
TreeTablePath& _path) const
{
if (ModelNode* parent = node->Parent()) {
if (!_GetTreePath(parent, _path))
return false;
if (node->IsHidden())
return true;
return _path.AddComponent(parent->IndexOf(node));
}
int32 index = fNodes.IndexOf(node);
_path.Clear();
return index >= 0 && _path.AddComponent(index);
}
VariablesView::VariablesView(Listener* listener)
:
BGroupView(B_VERTICAL),
fThread(NULL),
fStackFrame(NULL),
fVariableTable(NULL),
fVariableTableModel(NULL),
fContainerListener(NULL),
fPreviousViewState(NULL),
fViewStateHistory(NULL),
fTableCellContextMenuTracker(NULL),
fListener(listener)
{
SetName("Variables");
}
VariablesView::~VariablesView()
{
SetStackFrame(NULL, NULL);
fVariableTable->SetTreeTableModel(NULL);
if (fPreviousViewState != NULL)
fPreviousViewState->ReleaseReference();
delete fViewStateHistory;
if (fVariableTableModel != NULL) {
fVariableTableModel->SetContainerListener(NULL);
delete fVariableTableModel;
}
delete fContainerListener;
}
VariablesView*
VariablesView::Create(Listener* listener)
{
VariablesView* self = new VariablesView(listener);
try {
self->_Init();
} catch (...) {
delete self;
throw;
}
return self;
}
void
VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame)
{
if (thread == fThread && stackFrame == fStackFrame)
return;
_FinishContextMenu(true);
if (fThread != NULL)
fThread->ReleaseReference();
if (fStackFrame != NULL)
fStackFrame->ReleaseReference();
fThread = thread;
fStackFrame = stackFrame;
if (fThread != NULL)
fThread->AcquireReference();
if (fStackFrame != NULL)
fStackFrame->AcquireReference();
fVariableTableModel->SetStackFrame(fThread, fStackFrame);
if (fThread != NULL && fStackFrame != NULL) {
AutoLocker<Team> locker(fThread->GetTeam());
void* root = fVariableTableModel->Root();
int32 count = fVariableTableModel->CountChildren(root);
for (int32 i = 0; i < count; i++) {
ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i);
_RequestNodeValue(node);
}
}
}
void
VariablesView::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_VALUE_NODE_CHANGED:
{
ValueNodeChild* nodeChild;
ValueNode* oldNode;
ValueNode* newNode;
if (message->FindPointer("nodeChild", (void**)&nodeChild) == B_OK
&& message->FindPointer("oldNode", (void**)&oldNode) == B_OK
&& message->FindPointer("newNode", (void**)&newNode) == B_OK) {
BReference<ValueNodeChild> nodeChildReference(nodeChild, true);
BReference<ValueNode> oldNodeReference(oldNode, true);
BReference<ValueNode> newNodeReference(newNode, true);
fVariableTableModel->ValueNodeChanged(nodeChild, oldNode,
newNode);
}
break;
}
case MSG_VALUE_NODE_CHILDREN_CREATED:
{
ValueNode* node;
if (message->FindPointer("node", (void**)&node) == B_OK) {
BReference<ValueNode> newNodeReference(node, true);
fVariableTableModel->ValueNodeChildrenCreated(node);
}
break;
}
case MSG_VALUE_NODE_CHILDREN_DELETED:
{
ValueNode* node;
if (message->FindPointer("node", (void**)&node) == B_OK) {
BReference<ValueNode> newNodeReference(node, true);
fVariableTableModel->ValueNodeChildrenDeleted(node);
}
break;
}
case MSG_VALUE_NODE_VALUE_CHANGED:
{
ValueNode* node;
if (message->FindPointer("node", (void**)&node) == B_OK) {
BReference<ValueNode> newNodeReference(node, true);
fVariableTableModel->ValueNodeValueChanged(node);
}
break;
}
case MSG_VARIABLES_VIEW_CONTEXT_MENU_DONE:
{
_FinishContextMenu(false);
break;
}
case MSG_VARIABLES_VIEW_NODE_SETTINGS_CHANGED:
{
ModelNode* node;
if (message->FindPointer("node", (void**)&node) != B_OK)
break;
BReference<ModelNode> nodeReference(node, true);
fVariableTableModel->NotifyNodeChanged(node);
break;
}
default:
BGroupView::MessageReceived(message);
break;
}
}
void
VariablesView::DetachedFromWindow()
{
_FinishContextMenu(true);
}
void
VariablesView::TreeTableNodeExpandedChanged(TreeTable* table,
const TreeTablePath& path, bool expanded)
{
if (expanded) {
ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path);
if (node == NULL)
return;
fVariableTableModel->NodeExpanded(node);
if (node->CountChildren() == 1) {
ModelNode* child = node->ChildAt(0);
if (child->IsHidden())
node = child;
}
for (int32 i = 0; ModelNode* child = node->ChildAt(i); i++) {
if (child->IsPresentationNode())
continue;
_RequestNodeValue(child);
}
}
}
void
VariablesView::TreeTableCellMouseDown(TreeTable* table,
const TreeTablePath& path, int32 columnIndex, BPoint screenWhere,
uint32 buttons)
{
if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0)
return;
_FinishContextMenu(true);
ModelNode* node = (ModelNode*)fVariableTableModel->NodeForPath(path);
if (node == NULL)
return;
TableCellValueRenderer* cellRenderer = node->TableCellRenderer();
if (cellRenderer == NULL)
return;
Settings* settings = cellRenderer->GetSettings();
if (settings == NULL)
return;
SettingsMenu* settingsMenu;
status_t error = node->GetValueHandler()->CreateTableCellValueSettingsMenu(
node->GetValue(), settings, settingsMenu);
BReference<SettingsMenu> settingsMenuReference(settingsMenu, true);
if (error != B_OK)
return;
TableCellContextMenuTracker* tracker = new(std::nothrow)
TableCellContextMenuTracker(node, Looper(), this);
BReference<TableCellContextMenuTracker> trackerReference(tracker);
if (tracker == NULL || tracker->Init(settings, settingsMenu) != B_OK)
return;
fTableCellContextMenuTracker = trackerReference.Detach();
fTableCellContextMenuTracker->ShowMenu(screenWhere);
}
void
VariablesView::_Init()
{
fVariableTable = new TreeTable("variable list", 0, B_FANCY_BORDER);
AddChild(fVariableTable->ToView());
fVariableTable->SetSortingEnabled(false);
fVariableTable->AddColumn(new StringTableColumn(0, "Variable", 80, 40, 1000,
B_TRUNCATE_END, B_ALIGN_LEFT));
fVariableTable->AddColumn(new VariableValueColumn(1, "Value", 80, 40, 1000,
B_TRUNCATE_END, B_ALIGN_RIGHT));
fVariableTableModel = new VariableTableModel;
if (fVariableTableModel->Init() != B_OK)
throw std::bad_alloc();
fVariableTable->SetTreeTableModel(fVariableTableModel);
fContainerListener = new ContainerListener(this);
fVariableTableModel->SetContainerListener(fContainerListener);
fVariableTable->AddTreeTableListener(this);
fViewStateHistory = new VariablesViewStateHistory;
if (fViewStateHistory->Init() != B_OK)
throw std::bad_alloc();
}
void
VariablesView::_RequestNodeValue(ModelNode* node)
{
ValueNodeChild* nodeChild = node->NodeChild();
ValueNodeContainer* container = nodeChild->Container();
BReference<ValueNodeContainer> containerReference(container);
AutoLocker<ValueNodeContainer> containerLocker(container);
if (container == NULL || nodeChild->Container() != container)
return;
ValueNode* valueNode = nodeChild->Node();
if (valueNode == NULL
|| valueNode->LocationAndValueResolutionState()
!= VALUE_NODE_UNRESOLVED) {
return;
}
BReference<ValueNode> valueNodeReference(valueNode);
containerLocker.Unlock();
fListener->ValueNodeValueRequested(fStackFrame->GetCpuState(), container,
valueNode);
}
void
VariablesView::_FinishContextMenu(bool force)
{
if (fTableCellContextMenuTracker != NULL) {
if (!fTableCellContextMenuTracker->FinishMenu(force) || force) {
fTableCellContextMenuTracker->ReleaseReference();
fTableCellContextMenuTracker = NULL;
}
}
}
#if 0
void
VariablesView::_SaveViewState() const
{
if (fThread == NULL || fStackFrame == NULL
|| fStackFrame->Function() == NULL) {
return;
}
FunctionID* functionID = fStackFrame->Function()->GetFunctionID();
if (functionID == NULL)
return;
BReference<FunctionID> functionIDReference(functionID, true);
VariablesViewState* viewState = new(std::nothrow) VariablesViewState;
if (viewState == NULL)
return;
BReference<VariablesViewState> viewStateReference(viewState, true);
if (viewState->Init() != B_OK)
return;
TreeTablePath path;
if (_AddViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(),
path) != B_OK) {
return;
}
fViewStateHistory->SetState(fThread->ID(), functionID, viewState);
}
void
VariablesView::_RestoreViewState()
{
if (fPreviousViewState != NULL) {
fPreviousViewState->ReleaseReference();
fPreviousViewState = NULL;
}
if (fThread == NULL || fStackFrame == NULL
|| fStackFrame->Function() == NULL) {
return;
}
FunctionID* functionID = fStackFrame->Function()->GetFunctionID();
if (functionID == NULL)
return;
BReference<FunctionID> functionIDReference(functionID, true);
VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(),
functionID);
if (viewState == NULL)
return;
TreeTablePath path;
_ApplyViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(),
path);
}
status_t
VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState,
void* parent, TreeTablePath& path) const
{
int32 childCount = fVariableTableModel->CountChildren(parent);
for (int32 i = 0; i < childCount; i++) {
ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i);
if (!path.AddComponent(i))
return B_NO_MEMORY;
VariablesViewNodeInfo nodeInfo;
nodeInfo.SetNodeExpanded(fVariableTable->IsNodeExpanded(path));
status_t error = viewState->SetNodeInfo(node->GetVariable()->ID(),
node->Path(), nodeInfo);
if (error != B_OK)
return error;
error = _AddViewStateDescendentNodeInfos(viewState, node, path);
if (error != B_OK)
return error;
path.RemoveLastComponent();
}
return B_OK;
}
status_t
VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState,
void* parent, TreeTablePath& path)
{
int32 childCount = fVariableTableModel->CountChildren(parent);
for (int32 i = 0; i < childCount; i++) {
ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(parent, i);
if (!path.AddComponent(i))
return B_NO_MEMORY;
const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo(
node->GetVariable()->ID(), node->Path());
if (nodeInfo != NULL) {
fVariableTable->SetNodeExpanded(path, nodeInfo->IsNodeExpanded());
status_t error = _ApplyViewStateDescendentNodeInfos(viewState, node,
path);
if (error != B_OK)
return error;
}
path.RemoveLastComponent();
}
return B_OK;
}
#endif
VariablesView::Listener::~Listener()
{
}