root/src/apps/pulse/CPUButton.cpp
//*****************************************************************************
//
//      File:           CPUButton.cpp
//
//      Written by:     Daniel Switkin
//
//      Copyright 1999, Be Incorporated
//
//*****************************************************************************

#include "CPUButton.h"

#include <stdlib.h>
#include <string.h>

#include <Alert.h>
#include <Catalog.h>
#include <Dragger.h>
#include <PopUpMenu.h>
#include <TextView.h>
#include <ViewPrivate.h>

#include <syscalls.h>

#include "PulseApp.h"
#include "PulseView.h"
#include "Common.h"

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "CPUButton"


CPUButton::CPUButton(BRect rect, const char *name, const char *label, BMessage *message)
        : BControl(rect, name, label, message, B_FOLLOW_NONE, B_WILL_DRAW)
{
        SetValue(B_CONTROL_ON);
        SetViewColor(B_TRANSPARENT_COLOR);
        fReplicant = false;

        _InitData();
}


CPUButton::CPUButton(BMessage *message)
        : BControl(message)
{
        fReplicant = true;

        /* We remove the dragger if we are in deskbar */
        if (CountChildren() > 1)
                RemoveChild(ChildAt(1));

        ResizeTo(CPUBUTTON_WIDTH, CPUBUTTON_HEIGHT);

        _InitData();
}


CPUButton::~CPUButton()
{
}


void
CPUButton::_InitData()
{
        fOffColor.red = fOffColor.green = fOffColor.blue = 184;
        fOffColor.alpha = 255;

        fCPU = atoi(Label()) - 1;
}


void
CPUButton::_AddDragger()
{
        BRect rect(Bounds());
        rect.top = rect.bottom - 7;
        rect.left = rect.right - 7;
        BDragger* dragger = new BDragger(rect, this,
                B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
        AddChild(dragger);
}


//! Redraw the button depending on whether it's up or down
void
CPUButton::Draw(BRect rect)
{
        bool value = (bool)Value();
        SetHighColor(value ? fOnColor : fOffColor);

        if (!fReplicant) {
                SetLowColor(Parent()->LowColor());
                FillRect(Bounds(), B_SOLID_LOW);
        }

        BRect bounds = Bounds();
        if (fReplicant && !fReplicantInDeskbar) {
                bounds.bottom -= 4;
                bounds.right -= 4;
        } else if (!fReplicant) {
                bounds.bottom -= 7;
                bounds.right -= 7;
        }
        BRect color_rect(bounds);
        color_rect.InsetBy(2, 2);
        if (value) {
                color_rect.bottom -= 1;
                color_rect.right -= 1;
        }
        FillRect(bounds);

        if (value)
                SetHighColor(80, 80, 80);
        else
                SetHighColor(255, 255, 255);

        BPoint start(0, 0);
        BPoint end(bounds.right, 0);
        StrokeLine(start, end);
        end.Set(0, bounds.bottom);
        StrokeLine(start, end);

        if (value)
                SetHighColor(32, 32, 32);
        else
                SetHighColor(216, 216, 216);

        start.Set(1, 1);
        end.Set(bounds.right - 1, 1);
        StrokeLine(start, end);
        end.Set(1, bounds.bottom - 1);
        StrokeLine(start, end);

        if (value)
                SetHighColor(216, 216, 216);
        else
                SetHighColor(80, 80, 80);

        start.Set(bounds.left + 1, bounds.bottom - 1);
        end.Set(bounds.right - 1, bounds.bottom - 1);
        StrokeLine(start, end);
        start.Set(bounds.right - 1, bounds.top + 1);
        StrokeLine(start, end);

        if (value)
                SetHighColor(255, 255, 255);
        else
                SetHighColor(32, 32, 32);

        start.Set(bounds.left, bounds.bottom);
        end.Set(bounds.right, bounds.bottom);
        StrokeLine(start, end);
        start.Set(bounds.right, bounds.top);
        StrokeLine(start, end);

        if (value) {
                SetHighColor(0, 0, 0);
                start.Set(bounds.left + 2, bounds.bottom - 2);
                end.Set(bounds.right - 2, bounds.bottom - 2);
                StrokeLine(start, end);
                start.Set(bounds.right - 2, bounds.top + 2);
                StrokeLine(start, end);
        }

        // Try to keep the text centered
        BFont font;
        GetFont(&font);
        int label_width = (int)font.StringWidth(Label());
        int rect_width = bounds.IntegerWidth() - 1;
        int rect_height = bounds.IntegerHeight();
        font_height fh;
        font.GetHeight(&fh);
        int label_height = (int)fh.ascent;
        int x_pos = (int)(((double)(rect_width - label_width) / 2.0) + 0.5);
        int y_pos = (rect_height - label_height) / 2 + label_height;

        MovePenTo(x_pos, y_pos);
        SetHighColor(0, 0, 0);
        SetDrawingMode(B_OP_OVER);
        DrawString(Label());
}


//! Track the mouse without blocking the window
void
CPUButton::MouseDown(BPoint point)
{
        BPoint mousePosition;
        uint32 mouseButtons;

        GetMouse(&mousePosition, &mouseButtons);

        if ((B_PRIMARY_MOUSE_BUTTON & mouseButtons) != 0) {
                SetValue(!Value());
                SetTracking(true);
                SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
        } else if ((B_SECONDARY_MOUSE_BUTTON & mouseButtons) != 0
                && fReplicantInDeskbar) {
                BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Deskbar menu"));
                BString menuLabel(B_TRANSLATE("About %appname%" B_UTF8_ELLIPSIS));
                menuLabel.ReplaceFirst("%appname%", B_TRANSLATE_SYSTEM_NAME("Pulse"));
                menu->AddItem(new BMenuItem(menuLabel, new BMessage(B_ABOUT_REQUESTED)));
                menu->AddSeparatorItem();
                menu->AddItem(new BMenuItem(B_TRANSLATE("Remove replicant"),
                        new BMessage(kDeleteReplicant)));
                menu->SetTargetForItems(this);

                ConvertToScreen(&point);
                menu->Go(point, true, true, true);
        }
}


void
CPUButton::MouseUp(BPoint point)
{
        if (IsTracking()) {
                if (Bounds().Contains(point))
                        Invoke();

                SetTracking(false);
        }
}


void
CPUButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
{
        if (IsTracking()) {
                if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
                        SetValue(!Value());
        }
}


status_t
CPUButton::Invoke(BMessage *message)
{
        if (!LastEnabledCPU(fCPU)) {
                _kern_set_cpu_enabled(fCPU, Value());
        } else {
                BAlert *alert = new BAlert(B_TRANSLATE("Info"),
                        B_TRANSLATE("You can't disable the last active CPU."),
                        B_TRANSLATE("OK"));
                alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
                alert->Go(NULL);
                SetValue(!Value());
        }

        return B_OK;
}


CPUButton *
CPUButton::Instantiate(BMessage *data)
{
        if (!validate_instantiation(data, "CPUButton"))
                return NULL;

        return new CPUButton(data);
}


status_t
CPUButton::Archive(BMessage *data, bool deep) const
{
        BControl::Archive(data, deep);
        data->AddString("add_on", APP_SIGNATURE);
        data->AddString("class", "CPUButton");
        return B_OK;
}


void
CPUButton::MessageReceived(BMessage *message)
{
        switch (message->what) {
                case B_ABOUT_REQUESTED: {
                        be_app->PostMessage(B_ABOUT_REQUESTED);
                        break;
                }
                case PV_REPLICANT_PULSE: {
                        // Make sure we're consistent with our CPU
                        if (_kern_cpu_enabled(fCPU) != Value() && !IsTracking())
                                SetValue(!Value());
                        break;
                }
                case kDeleteReplicant: {
                        Window()->PostMessage(kDeleteReplicant, this, NULL);
                        break;
                }
                default:
                        BControl::MessageReceived(message);
                        break;
        }
}


void
CPUButton::UpdateColors(int32 color)
{
        fOnColor.red = (color & 0xff000000) >> 24;
        fOnColor.green = (color & 0x00ff0000) >> 16;
        fOnColor.blue = (color & 0x0000ff00) >> 8;
        Draw(Bounds());
}


void
CPUButton::AttachedToWindow()
{
        SetTarget(this);
        SetFont(be_plain_font);
        SetFontSize(10);

        fReplicantInDeskbar = false;

        if (fReplicant) {
                if (strcmp(Window()->Title(), B_TRANSLATE("Deskbar"))) {
                        // Make room for dragger
                        ResizeBy(4, 4);

                        _AddDragger();
                } else
                        fReplicantInDeskbar = true;

                Prefs *prefs = new Prefs();
                UpdateColors(prefs->normal_bar_color);
                delete prefs;
        } else {
                PulseApp *pulseapp = (PulseApp *)be_app;
                UpdateColors(pulseapp->fPrefs->normal_bar_color);
                _AddDragger();
        }

        BMessenger messenger(this);
        fPulseRunner = new BMessageRunner(messenger, new BMessage(PV_REPLICANT_PULSE),
                200000, -1);
}


void
CPUButton::DetachedFromWindow()
{
        delete fPulseRunner;
}