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


#include "BreakpointsView.h"

#include <new>

#include <Button.h>
#include <CheckBox.h>
#include <LayoutBuilder.h>

#include <AutoLocker.h>
#include <ObjectList.h>

#include "AppMessageCodes.h"
#include "MessageCodes.h"
#include "Team.h"
#include "UserBreakpoint.h"


// #pragma mark - BreakpointsView


BreakpointsView::BreakpointsView(Team* team, Listener* listener)
        :
        BGroupView(B_HORIZONTAL, 4.0f),
        fTeam(team),
        fListView(NULL),
        fToggleBreakpointButton(NULL),
        fEditBreakpointButton(NULL),
        fRemoveBreakpointButton(NULL),
        fListener(listener)
{
        SetName("Breakpoints");
}


BreakpointsView::~BreakpointsView()
{
        if (fListView != NULL)
                fListView->UnsetListener();
}


/*static*/ BreakpointsView*
BreakpointsView::Create(Team* team, Listener* listener)
{
        BreakpointsView* self = new BreakpointsView(team, listener);

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

        return self;
}


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


void
BreakpointsView::UserBreakpointChanged(UserBreakpoint* breakpoint)
{
        fListView->UserBreakpointChanged(breakpoint);

        _UpdateButtons();
}


void
BreakpointsView::WatchpointChanged(Watchpoint* watchpoint)
{
        fListView->WatchpointChanged(watchpoint);

        _UpdateButtons();
}


void
BreakpointsView::MessageReceived(BMessage* message)
{
        switch (message->what) {
                case MSG_ENABLE_BREAKPOINT:
                case MSG_DISABLE_BREAKPOINT:
                case MSG_CLEAR_BREAKPOINT:
                        _HandleBreakpointAction(message->what);
                        break;

                case MSG_SHOW_BREAKPOINT_EDIT_WINDOW:
                        message->AddPointer("breakpoint",
                                fSelectedBreakpoints.ItemAt(0)->GetBreakpoint());
                        Window()->PostMessage(message);
                        break;

                default:
                        BGroupView::MessageReceived(message);
                        break;
        }
}


void
BreakpointsView::AttachedToWindow()
{
        fEditBreakpointButton->SetTarget(this);
        fToggleBreakpointButton->SetTarget(this);
        fRemoveBreakpointButton->SetTarget(this);
}


void
BreakpointsView::LoadSettings(const BMessage& settings)
{
        BMessage breakpointListSettings;
        if (settings.FindMessage("breakpointList", &breakpointListSettings)
                == B_OK)
                fListView->LoadSettings(breakpointListSettings);
}


status_t
BreakpointsView::SaveSettings(BMessage& settings)
{
        BMessage breakpointListSettings;
        if (fListView->SaveSettings(breakpointListSettings) != B_OK)
                return B_NO_MEMORY;

        if (settings.AddMessage("breakpointList", &breakpointListSettings) != B_OK)
                return B_NO_MEMORY;

        return B_OK;
}


void
BreakpointsView::BreakpointSelectionChanged(BreakpointProxyList& proxies)
{
        if (fListener != NULL)
                fListener->BreakpointSelectionChanged(proxies);

        _SetSelection(proxies);
}


void
BreakpointsView::_Init()
{
        BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f)
                .Add(fListView = BreakpointListView::Create(fTeam, this, this))
                .AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING)
                        .SetInsets(B_USE_SMALL_SPACING)
                        .AddGlue()
                        .Add(fRemoveBreakpointButton = new BButton("Remove"))
                        .Add(fEditBreakpointButton = new BButton("Edit" B_UTF8_ELLIPSIS))
                        .Add(fToggleBreakpointButton = new BButton("Toggle"))
                .End();

        fToggleBreakpointButton->SetMessage(new BMessage(MSG_ENABLE_BREAKPOINT));
        fRemoveBreakpointButton->SetMessage(new BMessage(MSG_CLEAR_BREAKPOINT));
        fEditBreakpointButton->SetMessage(
                new BMessage(MSG_SHOW_BREAKPOINT_EDIT_WINDOW));

        _UpdateButtons();
}


void
BreakpointsView::_UpdateButtons()
{
        AutoLocker<Team> teamLocker(fTeam);

        bool hasEnabled = false;
        bool hasDisabled = false;
        bool valid = false;

        for (int32 i = 0; i < fSelectedBreakpoints.CountItems(); i++) {
                BreakpointProxy* proxy = fSelectedBreakpoints.ItemAt(i);
                switch (proxy->Type()) {
                        case BREAKPOINT_PROXY_TYPE_BREAKPOINT:
                        {
                                UserBreakpoint* breakpoint = proxy->GetBreakpoint();
                                if (breakpoint->IsValid()) {
                                        valid = true;
                                        if (breakpoint->IsEnabled())
                                                hasEnabled = true;
                                        else
                                                hasDisabled = true;

                                }
                                break;
                        }
                        case BREAKPOINT_PROXY_TYPE_WATCHPOINT:
                        {
                                Watchpoint* watchpoint = proxy->GetWatchpoint();
                                valid = true;
                                if (watchpoint->IsEnabled())
                                        hasEnabled = true;
                                else
                                        hasDisabled = true;
                                break;
                        }
                        default:
                                break;
                }
        }

        if (valid) {
                // only allow condition editing if we have a single
                // actual breakpoint selected.
                // TODO: allow using this to modify watchpoints as
                // well.
                if (fSelectedBreakpoints.CountItems() == 1
                        && fSelectedBreakpoints.ItemAt(0)->Type()
                                == BREAKPOINT_PROXY_TYPE_BREAKPOINT) {
                        fEditBreakpointButton->SetEnabled(true);
                } else
                        fEditBreakpointButton->SetEnabled(false);

                // if we have at least one disabled breakpoint in the
                // selection, we leave the button as an Enable button
                if (hasEnabled && !hasDisabled) {
                        fToggleBreakpointButton->SetLabel("Disable");
                        fToggleBreakpointButton->SetMessage(
                                new BMessage(MSG_DISABLE_BREAKPOINT));
                } else {
                        fToggleBreakpointButton->SetLabel("Enable");
                        fToggleBreakpointButton->SetMessage(
                                new BMessage(MSG_ENABLE_BREAKPOINT));
                }

                fToggleBreakpointButton->SetEnabled(true);
                fRemoveBreakpointButton->SetEnabled(true);
        } else {
                fToggleBreakpointButton->SetLabel("Enable");
                fToggleBreakpointButton->SetEnabled(false);
                fEditBreakpointButton->SetEnabled(false);
                fRemoveBreakpointButton->SetEnabled(false);
        }
}


void
BreakpointsView::_SetSelection(BreakpointProxyList& proxies)
{
        for (int32 i = 0; i < fSelectedBreakpoints.CountItems(); i++)
                fSelectedBreakpoints.ItemAt(i)->ReleaseReference();

        fSelectedBreakpoints.MakeEmpty();

        for (int32 i = 0; i < proxies.CountItems(); i++) {
                BreakpointProxy* proxy = proxies.ItemAt(i);
                if (!fSelectedBreakpoints.AddItem(proxy))
                        return;
                proxy->AcquireReference();
        }

        _UpdateButtons();
}


void
BreakpointsView::_HandleBreakpointAction(uint32 action)
{
        if (fListener == NULL)
                return;

        for (int32 i = 0; i < fSelectedBreakpoints.CountItems(); i++) {
                BreakpointProxy* proxy = fSelectedBreakpoints.ItemAt(i);
                if (proxy->Type() == BREAKPOINT_PROXY_TYPE_BREAKPOINT) {
                        UserBreakpoint* breakpoint = proxy->GetBreakpoint();
                        if (action == MSG_ENABLE_BREAKPOINT && !breakpoint->IsEnabled())
                                fListener->SetBreakpointEnabledRequested(breakpoint, true);
                        else if (action == MSG_DISABLE_BREAKPOINT
                                && breakpoint->IsEnabled()) {
                                fListener->SetBreakpointEnabledRequested(breakpoint, false);
                        } else if (action == MSG_CLEAR_BREAKPOINT)
                                fListener->ClearBreakpointRequested(breakpoint);
                } else {
                        Watchpoint* watchpoint = proxy->GetWatchpoint();
                        if (action == MSG_ENABLE_BREAKPOINT && !watchpoint->IsEnabled())
                                fListener->SetWatchpointEnabledRequested(watchpoint, true);
                        else if (action == MSG_DISABLE_BREAKPOINT
                                && watchpoint->IsEnabled()) {
                                fListener->SetWatchpointEnabledRequested(watchpoint, false);
                        } else if (action == MSG_CLEAR_BREAKPOINT)
                                fListener->ClearWatchpointRequested(watchpoint);
                }
        }
}


// #pragma mark - Listener


BreakpointsView::Listener::~Listener()
{
}