root/src/apps/debugger/user_interface/gui/team_window/StackTraceView.cpp
/*
 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2011-2013, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */


#include "StackTraceView.h"

#include <stdio.h>

#include <new>

#include <ControlLook.h>
#include <Window.h>

#include "table/TableColumns.h"

#include "FunctionInstance.h"
#include "GuiSettingsUtils.h"
#include "Image.h"
#include "StackTrace.h"
#include "TargetAddressTableColumn.h"
#include "UiUtils.h"


// #pragma mark - FramesTableModel


class StackTraceView::FramesTableModel : public TableModel {
public:
        FramesTableModel()
                :
                fStackTrace(NULL)
        {
        }

        ~FramesTableModel()
        {
                SetStackTrace(NULL);
        }

        void SetStackTrace(StackTrace* stackTrace)
        {
                // unset old frames
                if (fStackTrace != NULL && fStackTrace->CountFrames())
                        NotifyRowsRemoved(0, fStackTrace->CountFrames());

                fStackTrace = stackTrace;

                // set new frames
                if (fStackTrace != NULL && fStackTrace->CountFrames() > 0)
                        NotifyRowsAdded(0, fStackTrace->CountFrames());
        }

        virtual int32 CountColumns() const
        {
                return 3;
        }

        virtual int32 CountRows() const
        {
                return fStackTrace != NULL ? fStackTrace->CountFrames() : 0;
        }

        virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value)
        {
                StackFrame* frame
                        = fStackTrace != NULL ? fStackTrace->FrameAt(rowIndex) : NULL;
                if (frame == NULL)
                        return false;

                switch (columnIndex) {
                        case 0:
                                value.SetTo(frame->FrameAddress());
                                return true;
                        case 1:
                                value.SetTo(frame->InstructionPointer());
                                return true;
                        case 2:
                        {
                                char buffer[512];
                                value.SetTo(UiUtils::FunctionNameForFrame(frame, buffer,
                                                sizeof(buffer)));
                                return true;
                        }
                        default:
                                return false;
                }
        }

        StackFrame* FrameAt(int32 index) const
        {
                return fStackTrace != NULL ? fStackTrace->FrameAt(index) : NULL;
        }

private:
        StackTrace*                             fStackTrace;
};


// #pragma mark - StackTraceView


StackTraceView::StackTraceView(Listener* listener)
        :
        BGroupView(B_VERTICAL),
        fStackTrace(NULL),
        fFramesTable(NULL),
        fFramesTableModel(NULL),
        fTraceClearPending(false),
        fListener(listener)
{
        SetName("Stack Trace");
}


StackTraceView::~StackTraceView()
{
        SetStackTrace(NULL);
        fFramesTable->SetTableModel(NULL);
        delete fFramesTableModel;
}


/*static*/ StackTraceView*
StackTraceView::Create(Listener* listener)
{
        StackTraceView* self = new StackTraceView(listener);

        try {
                self->_Init();
        } catch (...) {
                delete self;
                throw;
        }

        return self;
}


void
StackTraceView::UnsetListener()
{
        fListener = NULL;
}


void
StackTraceView::SetStackTrace(StackTrace* stackTrace)
{
        fTraceClearPending = false;
        if (stackTrace == fStackTrace)
                return;

        if (fStackTrace != NULL)
                fStackTrace->ReleaseReference();

        fStackTrace = stackTrace;

        if (fStackTrace != NULL)
                fStackTrace->AcquireReference();

        fFramesTableModel->SetStackTrace(fStackTrace);
}


void
StackTraceView::SetStackFrame(StackFrame* stackFrame)
{
        if (fStackTrace != NULL && stackFrame != NULL) {
                for (int32 i = 0; StackFrame* other = fStackTrace->FrameAt(i); i++) {
                        if (stackFrame == other) {
                                fFramesTable->SelectRow(i, false);
                                return;
                        }
                }
        }

        fFramesTable->DeselectAllRows();
}


void
StackTraceView::LoadSettings(const BMessage& settings)
{
        BMessage tableSettings;
        if (settings.FindMessage("framesTable", &tableSettings) == B_OK) {
                GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
                        fFramesTable);
        }
}


status_t
StackTraceView::SaveSettings(BMessage& settings)
{
        settings.MakeEmpty();

        BMessage tableSettings;
        status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
                fFramesTable);
        if (result == B_OK)
                result = settings.AddMessage("framesTable", &tableSettings);

        return result;
}


void
StackTraceView::SetStackTraceClearPending()
{
        fTraceClearPending = true;
}


void
StackTraceView::TableSelectionChanged(Table* table)
{
        if (fListener == NULL || fTraceClearPending)
                return;

        StackFrame* frame
                = fFramesTableModel->FrameAt(table->SelectionModel()->RowAt(0));

        fListener->StackFrameSelectionChanged(frame);
}


void
StackTraceView::_Init()
{
        fFramesTable = new Table("stack trace", 0, B_FANCY_BORDER);
        fFramesTable->SetFont(B_FONT_ROW, be_fixed_font);
        AddChild(fFramesTable->ToView());
        fFramesTable->SetSortingEnabled(false);

        float addressWidth = be_fixed_font->StringWidth("0xffffffff00000000")
                + be_control_look->DefaultLabelSpacing();

        // columns
        fFramesTable->AddColumn(new TargetAddressTableColumn(0, "Frame",
                addressWidth, 40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
        fFramesTable->AddColumn(new TargetAddressTableColumn(1, "IP", addressWidth,
                40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
        fFramesTable->AddColumn(new StringTableColumn(2, "Function", 300, 100, 1000,
                B_TRUNCATE_END, B_ALIGN_LEFT));

        fFramesTableModel = new FramesTableModel();
        fFramesTable->SetTableModel(fFramesTableModel);

        fFramesTable->SetSelectionMode(B_SINGLE_SELECTION_LIST);
        fFramesTable->AddTableListener(this);
}


// #pragma mark - Listener


StackTraceView::Listener::~Listener()
{
}