root/src/servers/bluetooth/DeskbarReplicant.cpp
/*
 * Copyright 2009-2021, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Michael Weirauch, dev@m-phasis.de
 */


#include "DeskbarReplicant.h"

#include <Alert.h>
#include <Application.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <Deskbar.h>
#include <IconUtils.h>
#include <MenuItem.h>
#include <Message.h>
#include <PopUpMenu.h>
#include <Resources.h>
#include <Roster.h>
#include <String.h>

#include <bluetoothserver_p.h>


extern "C" _EXPORT BView *instantiate_deskbar_item(float maxWidth, float maxHeight);
status_t our_image(image_info& image);

const uint32 kMsgOpenBluetoothPreferences = 'obtp';
const uint32 kMsgQuitBluetoothServer = 'qbts';

const char* kDeskbarItemName = "BluetoothServerReplicant";
const char* kClassName = "DeskbarReplicant";


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "BluetoothReplicant"


//      #pragma mark -


DeskbarReplicant::DeskbarReplicant(BRect frame, int32 resizingMode)
        : BView(frame, kDeskbarItemName, resizingMode,
                B_WILL_DRAW | B_TRANSPARENT_BACKGROUND | B_FRAME_EVENTS)
{
        _Init();
}


DeskbarReplicant::DeskbarReplicant(BMessage* archive)
        : BView(archive)
{
        _Init();
}


DeskbarReplicant::~DeskbarReplicant()
{
}


void
DeskbarReplicant::_Init()
{
        fIcon = NULL;

        image_info info;
        if (our_image(info) != B_OK)
                return;

        BFile file(info.name, B_READ_ONLY);
        if (file.InitCheck() < B_OK)
                return;

        BResources resources(&file);
        if (resources.InitCheck() < B_OK)
                return;

        size_t size;
        const void* data = resources.LoadResource(B_VECTOR_ICON_TYPE,
                "tray_icon", &size);
        if (data != NULL) {
                BBitmap* icon = new BBitmap(Bounds(), B_RGBA32);
                if (icon->InitCheck() == B_OK
                        && BIconUtils::GetVectorIcon((const uint8 *)data,
                                size, icon) == B_OK) {
                        fIcon = icon;
                } else
                        delete icon;
        }
}


DeskbarReplicant *
DeskbarReplicant::Instantiate(BMessage* archive)
{
        if (!validate_instantiation(archive, kClassName))
                return NULL;

        return new DeskbarReplicant(archive);
}


status_t
DeskbarReplicant::Archive(BMessage* archive, bool deep) const
{
        status_t status = BView::Archive(archive, deep);
        if (status == B_OK)
                status = archive->AddString("add_on", BLUETOOTH_SIGNATURE);
        if (status == B_OK)
                status = archive->AddString("class", kClassName);

        return status;
}


void
DeskbarReplicant::AttachedToWindow()
{
        BView::AttachedToWindow();
        AdoptParentColors();

        if (ViewUIColor() == B_NO_COLOR)
                SetLowColor(ViewColor());
        else
                SetLowUIColor(ViewUIColor());
}


void
DeskbarReplicant::Draw(BRect updateRect)
{
        if (!fIcon) {
                /* At least display something... */
                rgb_color lowColor = LowColor();
                SetLowColor(0, 113, 187, 255);
                FillRoundRect(Bounds().InsetBySelf(3.f, 0.f), 5.f, 7.f, B_SOLID_LOW);
                SetLowColor(lowColor);
        } else {
                SetDrawingMode(B_OP_ALPHA);
                DrawBitmap(fIcon);
                SetDrawingMode(B_OP_COPY);
        }
}


void
DeskbarReplicant::MessageReceived(BMessage* msg)
{
        switch (msg->what) {
                case kMsgOpenBluetoothPreferences:
                        be_roster->Launch(BLUETOOTH_APP_SIGNATURE);
                        break;

                case kMsgQuitBluetoothServer:
                        _QuitBluetoothServer();
                        break;

                default:
                        BView::MessageReceived(msg);
        }
}


void
DeskbarReplicant::MouseDown(BPoint where)
{
        BPoint point;
        uint32 buttons;
        GetMouse(&point, &buttons);
        if (!(buttons & B_SECONDARY_MOUSE_BUTTON)) {
                return;
        }

        BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false);

        menu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS),
                new BMessage(kMsgOpenBluetoothPreferences)));

        // TODO show list of known/paired devices

        menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
                new BMessage(kMsgQuitBluetoothServer)));

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

        delete menu;
}


void
DeskbarReplicant::_QuitBluetoothServer()
{
        if (!be_roster->IsRunning(BLUETOOTH_SIGNATURE)) {
                // The server isn't running, so remove ourself
                BDeskbar deskbar;
                deskbar.RemoveItem(kDeskbarItemName);

                return;
        }
        status_t status = BMessenger(BLUETOOTH_SIGNATURE).SendMessage(
                B_QUIT_REQUESTED);
        if (status < B_OK) {
                _ShowErrorAlert(B_TRANSLATE("Stopping the Bluetooth server failed."),
                        status);
        }
}


void
DeskbarReplicant::_ShowErrorAlert(BString msg, status_t status)
{
        BString error = B_TRANSLATE("Error: %status%");
        error.ReplaceFirst("%status%", strerror(status));
        msg << "\n\n" << error;
        BAlert* alert = new BAlert(B_TRANSLATE("Bluetooth error"), msg.String(),
                B_TRANSLATE("OK"));
        alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
        alert->Go(NULL);
}


//      #pragma mark -


extern "C" _EXPORT BView *
instantiate_deskbar_item(float maxWidth, float maxHeight)
{
        return new DeskbarReplicant(BRect(0, 0, maxHeight - 1, maxHeight - 1),
                B_FOLLOW_NONE);
}


//      #pragma mark -


status_t
our_image(image_info& image)
{
        int32 cookie = 0;
        while (get_next_image_info(B_CURRENT_TEAM, &cookie, &image) == B_OK) {
                if ((char *)our_image >= (char *)image.text
                        && (char *)our_image <= (char *)image.text + image.text_size)
                        return B_OK;
        }

        return B_ERROR;
}