#include "BreakpointListView.h"
#include <stdio.h>
#include <new>
#include <MessageFilter.h>
#include <AutoLocker.h>
#include <ObjectList.h>
#include "Architecture.h"
#include "FunctionID.h"
#include "GuiSettingsUtils.h"
#include "LocatableFile.h"
#include "MessageCodes.h"
#include "table/TableColumns.h"
#include "TargetAddressTableColumn.h"
#include "Team.h"
#include "UserBreakpoint.h"
#include "Watchpoint.h"
BreakpointProxy::BreakpointProxy(UserBreakpoint* breakpoint,
Watchpoint* watchpoint)
:
fBreakpoint(breakpoint),
fWatchpoint(watchpoint)
{
if (fBreakpoint != NULL)
fBreakpoint->AcquireReference();
if (fWatchpoint != NULL)
fWatchpoint->AcquireReference();
}
BreakpointProxy::~BreakpointProxy()
{
if (fBreakpoint != NULL)
fBreakpoint->ReleaseReference();
if (fWatchpoint != NULL)
fWatchpoint->ReleaseReference();
}
breakpoint_proxy_type
BreakpointProxy::Type() const
{
return fBreakpoint != NULL ? BREAKPOINT_PROXY_TYPE_BREAKPOINT
: BREAKPOINT_PROXY_TYPE_WATCHPOINT;
}
class BreakpointListView::ListInputFilter : public BMessageFilter {
public:
ListInputFilter(BView* view)
:
BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, B_KEY_DOWN),
fTargetView(view)
{
}
~ListInputFilter()
{
}
filter_result Filter(BMessage* message, BHandler** target)
{
const char* bytes;
if (message->FindString("bytes", &bytes) == B_OK
&& bytes[0] == B_DELETE) {
BMessenger(fTargetView).SendMessage(MSG_CLEAR_BREAKPOINT);
}
return B_DISPATCH_MESSAGE;
}
private:
BView* fTargetView;
};
class BreakpointListView::BreakpointsTableModel : public TableModel {
public:
BreakpointsTableModel(Team* team)
:
fTeam(team)
{
UpdateBreakpoint(NULL);
}
~BreakpointsTableModel()
{
fTeam = NULL;
UpdateBreakpoint(NULL);
}
bool UpdateBreakpoint(BreakpointProxy* proxy)
{
if (fTeam == NULL) {
for (int32 i = 0;
BreakpointProxy* proxy = fBreakpointProxies.ItemAt(i);
i++) {
proxy->ReleaseReference();
}
fBreakpointProxies.MakeEmpty();
return true;
}
AutoLocker<Team> locker(fTeam);
UserBreakpointList::ConstIterator it
= fTeam->UserBreakpoints().GetIterator();
int32 watchpointIndex = 0;
UserBreakpoint* newBreakpoint = it.Next();
Watchpoint* newWatchpoint = fTeam->WatchpointAt(watchpointIndex);
int32 index = 0;
bool remove;
while (BreakpointProxy* oldProxy = fBreakpointProxies.ItemAt(index)) {
remove = false;
switch (oldProxy->Type()) {
case BREAKPOINT_PROXY_TYPE_BREAKPOINT:
{
UserBreakpoint* breakpoint = oldProxy->GetBreakpoint();
if (breakpoint == newBreakpoint) {
if (breakpoint == proxy->GetBreakpoint())
NotifyRowsChanged(index, 1);
++index;
newBreakpoint = it.Next();
} else
remove = true;
}
break;
case BREAKPOINT_PROXY_TYPE_WATCHPOINT:
{
Watchpoint* watchpoint = oldProxy->GetWatchpoint();
if (watchpoint == newWatchpoint) {
if (watchpoint == proxy->GetWatchpoint())
NotifyRowsChanged(index, 1);
++watchpointIndex;
++index;
newWatchpoint = fTeam->WatchpointAt(watchpointIndex);
} else
remove = true;
}
break;
}
if (remove) {
fBreakpointProxies.RemoveItemAt(index);
oldProxy->ReleaseReference();
NotifyRowsRemoved(index, 1);
}
}
int32 countBefore = fBreakpointProxies.CountItems();
BreakpointProxy* newProxy = NULL;
BReference<BreakpointProxy> proxyReference;
while (newBreakpoint != NULL) {
newProxy = new(std::nothrow) BreakpointProxy(newBreakpoint, NULL);
if (newProxy == NULL)
return false;
proxyReference.SetTo(newProxy, true);
if (!fBreakpointProxies.AddItem(newProxy))
return false;
proxyReference.Detach();
newBreakpoint = it.Next();
}
while (newWatchpoint != NULL) {
newProxy = new(std::nothrow) BreakpointProxy(NULL, newWatchpoint);
if (newProxy == NULL)
return false;
proxyReference.SetTo(newProxy, true);
if (!fBreakpointProxies.AddItem(newProxy))
return false;
proxyReference.Detach();
newWatchpoint = fTeam->WatchpointAt(++watchpointIndex);
}
int32 count = fBreakpointProxies.CountItems();
if (count > countBefore)
NotifyRowsAdded(countBefore, count - countBefore);
return true;
}
virtual int32 CountColumns() const
{
return 4;
}
virtual int32 CountRows() const
{
return fBreakpointProxies.CountItems();
}
virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value)
{
BreakpointProxy* proxy = fBreakpointProxies.ItemAt(rowIndex);
if (proxy == NULL)
return false;
if (proxy->Type() == BREAKPOINT_PROXY_TYPE_BREAKPOINT) {
return _GetBreakpointValueAt(proxy->GetBreakpoint(), rowIndex,
columnIndex, value);
}
return _GetWatchpointValueAt(proxy->GetWatchpoint(), rowIndex,
columnIndex, value);
}
BreakpointProxy* BreakpointProxyAt(int32 index) const
{
return fBreakpointProxies.ItemAt(index);
}
private:
bool _GetBreakpointValueAt(UserBreakpoint* breakpoint, int32 rowIndex,
int32 columnIndex, BVariant &value)
{
const UserBreakpointLocation& location = breakpoint->Location();
switch (columnIndex) {
case 0:
value.SetTo((int32)breakpoint->IsEnabled());
return true;
case 1:
value.SetTo(location.GetFunctionID()->FunctionName(),
B_VARIANT_DONT_COPY_DATA);
return true;
case 2:
{
LocatableFile* sourceFile = location.SourceFile();
BString data;
if (sourceFile != NULL) {
data.SetToFormat("%s:%" B_PRId32, sourceFile->Name(),
location.GetSourceLocation().Line() + 1);
} else {
AutoLocker<Team> teamLocker(fTeam);
if (UserBreakpointInstance* instance
= breakpoint->InstanceAt(0)) {
data.SetToFormat("%#" B_PRIx64, instance->Address());
}
}
value.SetTo(data);
return true;
}
case 3:
{
value.SetTo(breakpoint->Condition(),
B_VARIANT_DONT_COPY_DATA);
return true;
}
default:
return false;
}
}
bool _GetWatchpointValueAt(Watchpoint* watchpoint, int32 rowIndex,
int32 columnIndex, BVariant &value)
{
switch (columnIndex) {
case 0:
value.SetTo((int32)watchpoint->IsEnabled());
return true;
case 1:
{
BString data;
data.SetToFormat("%s at 0x%" B_PRIx64 " (%" B_PRId32 " bytes)",
_WatchpointTypeToString(watchpoint->Type()),
watchpoint->Address(), watchpoint->Length());
value.SetTo(data);
return true;
}
case 2:
{
return false;
}
default:
return false;
}
}
const char* _WatchpointTypeToString(uint32 type) const
{
switch (type) {
case WATCHPOINT_CAPABILITY_FLAG_READ:
{
return "read";
}
case WATCHPOINT_CAPABILITY_FLAG_WRITE:
{
return "write";
}
case WATCHPOINT_CAPABILITY_FLAG_READ_WRITE:
{
return "read/write";
}
default:
return NULL;
}
}
private:
Team* fTeam;
BreakpointProxyList fBreakpointProxies;
};
BreakpointListView::BreakpointListView(Team* team, Listener* listener)
:
BGroupView(B_VERTICAL),
fTeam(team),
fBreakpointsTable(NULL),
fBreakpointsTableModel(NULL),
fListener(listener)
{
}
BreakpointListView::~BreakpointListView()
{
fBreakpointsTable->SetTableModel(NULL);
delete fBreakpointsTableModel;
}
BreakpointListView*
BreakpointListView::Create(Team* team, Listener* listener, BView* filterTarget)
{
BreakpointListView* self = new BreakpointListView(team, listener);
try {
self->_Init(filterTarget);
} catch (...) {
delete self;
throw;
}
return self;
}
void
BreakpointListView::UnsetListener()
{
fListener = NULL;
}
void
BreakpointListView::UserBreakpointChanged(UserBreakpoint* breakpoint)
{
if (breakpoint->IsHidden())
return;
BreakpointProxy proxy(breakpoint, NULL);
fBreakpointsTableModel->UpdateBreakpoint(&proxy);
}
void
BreakpointListView::WatchpointChanged(Watchpoint* watchpoint)
{
BreakpointProxy proxy(NULL, watchpoint);
fBreakpointsTableModel->UpdateBreakpoint(&proxy);
}
void
BreakpointListView::LoadSettings(const BMessage& settings)
{
BMessage tableSettings;
if (settings.FindMessage("breakpointsTable", &tableSettings) == B_OK) {
GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
fBreakpointsTable);
}
}
status_t
BreakpointListView::SaveSettings(BMessage& settings)
{
settings.MakeEmpty();
BMessage tableSettings;
status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
fBreakpointsTable);
if (result == B_OK)
result = settings.AddMessage("breakpointsTable", &tableSettings);
return result;
}
void
BreakpointListView::TableSelectionChanged(Table* table)
{
if (fListener == NULL)
return;
TableSelectionModel* selectionModel = table->SelectionModel();
BreakpointProxyList proxyList;
for (int32 i = 0; i < selectionModel->CountRows(); i++) {
BreakpointProxy* proxy = fBreakpointsTableModel->BreakpointProxyAt(
selectionModel->RowAt(i));
if (proxy == NULL)
continue;
if (!proxyList.AddItem(proxy))
return;
}
fListener->BreakpointSelectionChanged(proxyList);
}
void
BreakpointListView::_Init(BView* filterTarget)
{
fBreakpointsTable = new Table("breakpoints list", 0, B_FANCY_BORDER);
fBreakpointsTable->SetFont(B_FONT_ROW, be_fixed_font);
AddChild(fBreakpointsTable->ToView());
fBreakpointsTable->AddColumn(new BoolStringTableColumn(0, "State", 70, 20,
1000, "Enabled", "Disabled"));
fBreakpointsTable->AddColumn(new StringTableColumn(1, "Location", 250, 40,
1000, B_TRUNCATE_END, B_ALIGN_LEFT));
fBreakpointsTable->AddColumn(new StringTableColumn(2, "File:Line/Address",
250, 40, 1000, B_TRUNCATE_END, B_ALIGN_LEFT));
fBreakpointsTable->AddColumn(new StringTableColumn(3, "Condition",
250, 40, 1000, B_TRUNCATE_END, B_ALIGN_LEFT));
fBreakpointsTable->SetSelectionMode(B_MULTIPLE_SELECTION_LIST);
fBreakpointsTable->AddTableListener(this);
fBreakpointsTable->AddFilter(new ListInputFilter(filterTarget));
fBreakpointsTableModel = new BreakpointsTableModel(fTeam);
fBreakpointsTable->SetTableModel(fBreakpointsTableModel);
}
BreakpointListView::Listener::~Listener()
{
}