root/src/kits/shared/IconButton.cpp
/*
 * Copyright 2006-2011, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Stephan Aßmus <superstippi@gmx.de>
 *              Axel Dörfler, axeld@pinc-software.de.
 */


#include "IconButton.h"

#include <new>
#include <stdio.h>

#include <Application.h>
#include <Bitmap.h>
#include <Control.h>
#include <ControlLook.h>
#include <Entry.h>
#include <IconUtils.h>
#include <Looper.h>
#include <Message.h>
#include <Mime.h>
#include <Path.h>
#include <Region.h>
#include <Resources.h>
#include <Roster.h>
#include <TranslationUtils.h>
#include <Window.h>


namespace BPrivate {


enum {
        STATE_NONE                      = 0x0000,
        STATE_PRESSED           = 0x0002,
        STATE_INSIDE            = 0x0008,
        STATE_FORCE_PRESSED     = 0x0010,
};



BIconButton::BIconButton(const char* name, const char* label,
        BMessage* message, BHandler* target)
        :
        BControl(name, label, message, B_WILL_DRAW),
        fButtonState(0),
        fNormalBitmap(NULL),
        fDisabledBitmap(NULL),
        fClickedBitmap(NULL),
        fDisabledClickedBitmap(NULL),
        fTargetCache(target)
{
        SetTarget(target);
        SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
        SetViewColor(B_TRANSPARENT_32_BIT);
}


BIconButton::~BIconButton()
{
        _DeleteBitmaps();
}


void
BIconButton::MessageReceived(BMessage* message)
{
        switch (message->what) {
                default:
                        BView::MessageReceived(message);
                        break;
        }
}


void
BIconButton::AttachedToWindow()
{
        AdoptParentColors();

        if (ViewUIColor() != B_NO_COLOR)
                SetLowUIColor(ViewUIColor());

        SetTarget(fTargetCache);
        if (!Target())
                SetTarget(Window());
}


void
BIconButton::Draw(BRect updateRect)
{
        rgb_color background = LowColor();

        BRect r(Bounds());

        uint32 flags = 0;
        BBitmap* bitmap = fNormalBitmap;
        if (!IsEnabled()) {
                flags |= BControlLook::B_DISABLED;
                bitmap = fDisabledBitmap;
        }
        if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED))
                flags |= BControlLook::B_ACTIVATED;

        if (ShouldDrawBorder()) {
                DrawBorder(r, updateRect, background, flags);
                DrawBackground(r, updateRect, background, flags);
        } else {
                SetHighColor(background);
                FillRect(r);
        }

        if (bitmap && bitmap->IsValid()) {
                if (bitmap->ColorSpace() == B_RGBA32
                        || bitmap->ColorSpace() == B_RGBA32_BIG) {
                        SetDrawingMode(B_OP_ALPHA);
                        SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
                }
                float x = r.left + floorf((r.Width()
                        - bitmap->Bounds().Width()) / 2.0 + 0.5);
                float y = r.top + floorf((r.Height()
                        - bitmap->Bounds().Height()) / 2.0 + 0.5);
                DrawBitmap(bitmap, BPoint(x, y));
        }
}


bool
BIconButton::ShouldDrawBorder() const
{
        return (IsEnabled() && (IsInside() || IsTracking()))
                || _HasFlags(STATE_FORCE_PRESSED);
}


void
BIconButton::DrawBorder(BRect& frame, const BRect& updateRect,
        const rgb_color& backgroundColor, uint32 flags)
{
        be_control_look->DrawButtonFrame(this, frame, updateRect, backgroundColor,
                backgroundColor, flags);
}


void
BIconButton::DrawBackground(BRect& frame, const BRect& updateRect,
        const rgb_color& backgroundColor, uint32 flags)
{
        be_control_look->DrawButtonBackground(this, frame, updateRect,
                backgroundColor, flags);
}


void
BIconButton::MouseDown(BPoint where)
{
        if (!IsValid())
                return;

        if (IsEnabled()) {
                if (Bounds().Contains(where)) {
                        SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
                        _SetFlags(STATE_PRESSED, true);
                        _SetTracking(true);
                } else {
                        _SetFlags(STATE_PRESSED, false);
                        _SetTracking(false);
                }
        }
}


void
BIconButton::MouseUp(BPoint where)
{
        if (!IsValid())
                return;

        if (IsEnabled() && _HasFlags(STATE_PRESSED)
                && Bounds().Contains(where)) {
                Invoke();
        } else if (Bounds().Contains(where))
                SetInside(true);

        _SetFlags(STATE_PRESSED, false);
        _SetTracking(false);
}


void
BIconButton::MouseMoved(BPoint where, uint32 transit, const BMessage* message)
{
        if (!IsValid())
                return;

        uint32 buttons = 0;
        Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
        // catch a mouse up event that we might have missed
        if (!buttons && _HasFlags(STATE_PRESSED)) {
                MouseUp(where);
                return;
        }
        if (buttons != 0 && !IsTracking())
                return;

        SetInside((transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW)
                && IsEnabled());
        if (IsTracking())
                _SetFlags(STATE_PRESSED, Bounds().Contains(where));
}


void
BIconButton::GetPreferredSize(float* width, float* height)
{
        float minWidth = 0.0f;
        float minHeight = 0.0f;
        if (IsValid()) {
                minWidth += fNormalBitmap->Bounds().IntegerWidth() + 1.0f;
                minHeight += fNormalBitmap->Bounds().IntegerHeight() + 1.0f;
        }

        const float kMinSpace = 15.0f;
        if (minWidth < kMinSpace)
                minWidth = kMinSpace;
        if (minHeight < kMinSpace)
                minHeight = kMinSpace;

        float hPadding = max_c(6.0f, ceilf(minHeight / 4.0f));
        float vPadding = max_c(6.0f, ceilf(minWidth / 4.0f));

        if (Label() != NULL && Label()[0] != '\0') {
                font_height fh;
                GetFontHeight(&fh);
                minHeight += ceilf(fh.ascent + fh.descent) + vPadding;
                minWidth += StringWidth(Label()) + vPadding;
        }

        if (width)
                *width = minWidth + hPadding;
        if (height)
                *height = minHeight + vPadding;
}


BSize
BIconButton::MinSize()
{
        BSize size;
        GetPreferredSize(&size.width, &size.height);
        return size;
}


BSize
BIconButton::MaxSize()
{
        return MinSize();
}


status_t
BIconButton::Invoke(BMessage* message)
{
        if (message == NULL)
                message = Message();
        if (message != NULL) {
                BMessage clone(*message);
                clone.AddInt64("be:when", system_time());
                clone.AddPointer("be:source", (BView*)this);
                clone.AddInt32("be:value", Value());
                return BInvoker::Invoke(&clone);
        }
        return BInvoker::Invoke(message);
}


void
BIconButton::SetPressed(bool pressed)
{
        _SetFlags(STATE_FORCE_PRESSED, pressed);
}


bool
BIconButton::IsPressed() const
{
        return _HasFlags(STATE_FORCE_PRESSED);
}


status_t
BIconButton::SetIcon(int32 resourceID)
{
        app_info info;
        status_t status = be_app->GetAppInfo(&info);
        if (status != B_OK)
                return status;

        BResources resources(&info.ref);
        status = resources.InitCheck();
        if (status != B_OK)
                return status;

        size_t size;
        const void* data = resources.LoadResource(B_VECTOR_ICON_TYPE, resourceID,
                &size);
        if (data != NULL) {
                const BRect bitmapRect(BPoint(0, 0), be_control_look->ComposeIconSize(32));
                BBitmap bitmap(bitmapRect, B_BITMAP_NO_SERVER_LINK, B_RGBA32);
                status = bitmap.InitCheck();
                if (status != B_OK)
                        return status;
                status = BIconUtils::GetVectorIcon(reinterpret_cast<const uint8*>(data),
                        size, &bitmap);
                if (status != B_OK)
                        return status;
                return SetIcon(&bitmap);
        }
//      const void* data = resources.LoadResource(B_BITMAP_TYPE, resourceID, &size);
        return B_ERROR;
}


status_t
BIconButton::SetIcon(const char* pathToBitmap)
{
        if (pathToBitmap == NULL)
                return B_BAD_VALUE;

        status_t status = B_BAD_VALUE;
        BBitmap* fileBitmap = NULL;
        // try to load bitmap from either relative or absolute path
        BEntry entry(pathToBitmap, true);
        if (!entry.Exists()) {
                app_info info;
                status = be_app->GetAppInfo(&info);
                if (status == B_OK) {
                        BEntry app_entry(&info.ref, true);
                        BPath path;
                        app_entry.GetPath(&path);
                        status = path.InitCheck();
                        if (status == B_OK) {
                                status = path.GetParent(&path);
                                if (status == B_OK) {
                                        status = path.Append(pathToBitmap, true);
                                        if (status == B_OK)
                                                fileBitmap = BTranslationUtils::GetBitmap(path.Path());
                                        else {
                                                printf("BIconButton::SetIcon() - path.Append() failed: "
                                                        "%s\n", strerror(status));
                                        }
                                } else {
                                        printf("BIconButton::SetIcon() - path.GetParent() failed: "
                                                "%s\n", strerror(status));
                                }
                        } else {
                                printf("BIconButton::SetIcon() - path.InitCheck() failed: "
                                        "%s\n", strerror(status));
                        }
                } else {
                        printf("BIconButton::SetIcon() - be_app->GetAppInfo() failed: "
                                "%s\n", strerror(status));
                }
        } else
                fileBitmap = BTranslationUtils::GetBitmap(pathToBitmap);
        if (fileBitmap) {
                status = _MakeBitmaps(fileBitmap);
                delete fileBitmap;
        } else
                status = B_ERROR;
        return status;
}


status_t
BIconButton::SetIcon(const BBitmap* bitmap, uint32 flags)
{
        if (bitmap && bitmap->ColorSpace() == B_CMAP8) {
                status_t status = bitmap->InitCheck();
                if (status >= B_OK) {
                        if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap)) {
                                status = _MakeBitmaps(rgb32Bitmap);
                                delete rgb32Bitmap;
                        } else
                                status = B_NO_MEMORY;
                }
                return status;
        } else
                return _MakeBitmaps(bitmap);
}


status_t
BIconButton::SetIcon(const BMimeType* fileType, bool small)
{
        status_t status = fileType ? fileType->InitCheck() : B_BAD_VALUE;
        if (status >= B_OK) {
                BBitmap* mimeBitmap = new(std::nothrow) BBitmap(BRect(0.0, 0.0, 15.0,
                        15.0), B_CMAP8);
                if (mimeBitmap && mimeBitmap->IsValid()) {
                        status = fileType->GetIcon(mimeBitmap, small ? B_MINI_ICON
                                : B_LARGE_ICON);
                        if (status >= B_OK) {
                                if (BBitmap* bitmap = _ConvertToRGB32(mimeBitmap)) {
                                        status = _MakeBitmaps(bitmap);
                                        delete bitmap;
                                } else {
                                        printf("BIconButton::SetIcon() - B_RGB32 bitmap is not "
                                                "valid\n");
                                }
                        } else {
                                printf("BIconButton::SetIcon() - fileType->GetIcon() failed: "
                                        "%s\n", strerror(status));
                        }
                } else
                        printf("BIconButton::SetIcon() - B_CMAP8 bitmap is not valid\n");
                delete mimeBitmap;
        } else {
                printf("BIconButton::SetIcon() - fileType is not valid: %s\n",
                        strerror(status));
        }
        return status;
}


status_t
BIconButton::SetIcon(const unsigned char* bitsFromQuickRes,
        uint32 width, uint32 height, color_space format, bool convertToBW)
{
        status_t status = B_BAD_VALUE;
        if (bitsFromQuickRes && width > 0 && height > 0) {
                BBitmap* quickResBitmap = new(std::nothrow) BBitmap(BRect(0.0, 0.0,
                        width - 1.0, height - 1.0), format);
                status = quickResBitmap ? quickResBitmap->InitCheck() : B_ERROR;
                if (status >= B_OK) {
                        // It doesn't look right to copy BitsLength() bytes, but bitmaps
                        // exported from QuickRes still contain their padding, so it is
                        // all right.
                        memcpy(quickResBitmap->Bits(), bitsFromQuickRes,
                                quickResBitmap->BitsLength());
                        if (format != B_RGB32 && format != B_RGBA32
                                && format != B_RGB32_BIG && format != B_RGBA32_BIG) {
                                // colorspace needs conversion
                                BBitmap* bitmap = new(std::nothrow) BBitmap(
                                        quickResBitmap->Bounds(), B_RGB32, true);
                                if (bitmap && bitmap->IsValid()) {
                                        if (bitmap->Lock()) {
                                                BView* helper = new BView(bitmap->Bounds(), "helper",
                                                        B_FOLLOW_NONE, B_WILL_DRAW);
                                                bitmap->AddChild(helper);
                                                helper->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
                                                helper->FillRect(helper->Bounds());
                                                helper->SetDrawingMode(B_OP_OVER);
                                                helper->DrawBitmap(quickResBitmap, BPoint(0.0, 0.0));
                                                helper->Sync();
                                                bitmap->Unlock();
                                        }
                                        status = _MakeBitmaps(bitmap);
                                } else {
                                        printf("BIconButton::SetIcon() - B_RGB32 bitmap is not "
                                                "valid\n");
                                }
                                delete bitmap;
                        } else {
                                // native colorspace (32 bits)
                                if (convertToBW) {
                                        // convert to gray scale icon
                                        uint8* bits = (uint8*)quickResBitmap->Bits();
                                        uint32 bpr = quickResBitmap->BytesPerRow();
                                        for (uint32 y = 0; y < height; y++) {
                                                uint8* handle = bits;
                                                uint8 gray;
                                                for (uint32 x = 0; x < width; x++) {
                                                        gray = uint8((116 * handle[0] + 600 * handle[1]
                                                                + 308 * handle[2]) / 1024);
                                                        handle[0] = gray;
                                                        handle[1] = gray;
                                                        handle[2] = gray;
                                                        handle += 4;
                                                }
                                                bits += bpr;
                                        }
                                }
                                status = _MakeBitmaps(quickResBitmap);
                        }
                } else {
                        printf("BIconButton::SetIcon() - error allocating bitmap: "
                                "%s\n", strerror(status));
                }
                delete quickResBitmap;
        }
        return status;
}


void
BIconButton::ClearIcon()
{
        _DeleteBitmaps();
        _Update();
}


void
BIconButton::TrimIcon(bool keepAspect)
{
        if (fNormalBitmap == NULL)
                return;

        uint8* bits = (uint8*)fNormalBitmap->Bits();
        uint32 bpr = fNormalBitmap->BytesPerRow();
        uint32 width = fNormalBitmap->Bounds().IntegerWidth() + 1;
        uint32 height = fNormalBitmap->Bounds().IntegerHeight() + 1;
        BRect trimmed(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN);
        for (uint32 y = 0; y < height; y++) {
                uint8* b = bits + 3;
                bool rowHasAlpha = false;
                for (uint32 x = 0; x < width; x++) {
                        if (*b) {
                                rowHasAlpha = true;
                                if (x < trimmed.left)
                                        trimmed.left = x;
                                if (x > trimmed.right)
                                        trimmed.right = x;
                        }
                        b += 4;
                }
                if (rowHasAlpha) {
                        if (y < trimmed.top)
                                trimmed.top = y;
                        if (y > trimmed.bottom)
                                trimmed.bottom = y;
                }
                bits += bpr;
        }
        if (!trimmed.IsValid())
                return;
        if (keepAspect) {
                float minInset = trimmed.left;
                minInset = min_c(minInset, trimmed.top);
                minInset = min_c(minInset, fNormalBitmap->Bounds().right
                        - trimmed.right);
                minInset = min_c(minInset, fNormalBitmap->Bounds().bottom
                        - trimmed.bottom);
                trimmed = fNormalBitmap->Bounds().InsetByCopy(minInset, minInset);
        }
        trimmed = trimmed & fNormalBitmap->Bounds();
        BBitmap trimmedBitmap(trimmed.OffsetToCopy(B_ORIGIN),
                B_BITMAP_NO_SERVER_LINK, B_RGBA32);
        bits = (uint8*)fNormalBitmap->Bits();
        bits += 4 * (int32)trimmed.left + bpr * (int32)trimmed.top;
        uint8* dst = (uint8*)trimmedBitmap.Bits();
        uint32 trimmedWidth = trimmedBitmap.Bounds().IntegerWidth() + 1;
        uint32 trimmedHeight = trimmedBitmap.Bounds().IntegerHeight() + 1;
        uint32 trimmedBPR = trimmedBitmap.BytesPerRow();
        for (uint32 y = 0; y < trimmedHeight; y++) {
                memcpy(dst, bits, trimmedWidth * 4);
                dst += trimmedBPR;
                bits += bpr;
        }
        SetIcon(&trimmedBitmap);
}


bool
BIconButton::IsValid() const
{
        return (fNormalBitmap && fDisabledBitmap && fClickedBitmap
                && fDisabledClickedBitmap
                && fNormalBitmap->IsValid()
                && fDisabledBitmap->IsValid()
                && fClickedBitmap->IsValid()
                && fDisabledClickedBitmap->IsValid());
}


BBitmap*
BIconButton::Bitmap() const
{
        BBitmap* bitmap = NULL;
        if (fNormalBitmap && fNormalBitmap->IsValid()) {
                bitmap = new(std::nothrow) BBitmap(fNormalBitmap);
                if (bitmap != NULL && bitmap->IsValid()) {
                        // TODO: remove this functionality when we use real transparent
                        // bitmaps
                        uint8* bits = (uint8*)bitmap->Bits();
                        uint32 bpr = bitmap->BytesPerRow();
                        uint32 width = bitmap->Bounds().IntegerWidth() + 1;
                        uint32 height = bitmap->Bounds().IntegerHeight() + 1;
                        color_space format = bitmap->ColorSpace();
                        if (format == B_CMAP8) {
                                // replace gray with magic transparent index
                        } else if (format == B_RGB32) {
                                for (uint32 y = 0; y < height; y++) {
                                        uint8* bitsHandle = bits;
                                        for (uint32 x = 0; x < width; x++) {
                                                if (bitsHandle[0] == 216
                                                        && bitsHandle[1] == 216
                                                        && bitsHandle[2] == 216) {
                                                        // make this pixel completely transparent
                                                        bitsHandle[3] = 0;
                                                }
                                                bitsHandle += 4;
                                        }
                                        bits += bpr;
                                }
                        }
                } else {
                        delete bitmap;
                        bitmap = NULL;
                }
        }
        return bitmap;
}


void
BIconButton::SetValue(int32 value)
{
        BControl::SetValue(value);
        _SetFlags(STATE_PRESSED, value != 0);
}


void
BIconButton::SetEnabled(bool enabled)
{
        BControl::SetEnabled(enabled);
        if (!enabled) {
                SetInside(false);
                _SetTracking(false);
        }
}


// #pragma mark - protected


bool
BIconButton::IsInside() const
{
        return _HasFlags(STATE_INSIDE);
}


void
BIconButton::SetInside(bool inside)
{
        _SetFlags(STATE_INSIDE, inside);
}


// #pragma mark - private


BBitmap*
BIconButton::_ConvertToRGB32(const BBitmap* bitmap) const
{
        BBitmap* convertedBitmap = new(std::nothrow) BBitmap(bitmap->Bounds(),
                B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
        if (convertedBitmap && convertedBitmap->IsValid()) {
                memset(convertedBitmap->Bits(), 0, convertedBitmap->BitsLength());
                if (convertedBitmap->Lock()) {
                        BView* helper = new BView(bitmap->Bounds(), "helper",
                                B_FOLLOW_NONE, B_WILL_DRAW);
                        convertedBitmap->AddChild(helper);
                        helper->SetDrawingMode(B_OP_OVER);
                        helper->DrawBitmap(bitmap, BPoint(0.0, 0.0));
                        helper->Sync();
                        convertedBitmap->Unlock();
                }
        } else {
                delete convertedBitmap;
                convertedBitmap = NULL;
        }
        return convertedBitmap;
}


status_t
BIconButton::_MakeBitmaps(const BBitmap* bitmap)
{
        status_t status = bitmap ? bitmap->InitCheck() : B_BAD_VALUE;
        if (status == B_OK) {
                // make our own versions of the bitmap
                BRect b(bitmap->Bounds());
                _DeleteBitmaps();
                color_space format = bitmap->ColorSpace();
                fNormalBitmap = new(std::nothrow) BBitmap(b, format);
                fDisabledBitmap = new(std::nothrow) BBitmap(b, format);
                fClickedBitmap = new(std::nothrow) BBitmap(b, format);
                fDisabledClickedBitmap = new(std::nothrow) BBitmap(b, format);
                if (IsValid()) {
                        // copy bitmaps from file bitmap
                        uint8* nBits = (uint8*)fNormalBitmap->Bits();
                        uint8* dBits = (uint8*)fDisabledBitmap->Bits();
                        uint8* cBits = (uint8*)fClickedBitmap->Bits();
                        uint8* dcBits = (uint8*)fDisabledClickedBitmap->Bits();
                        uint8* fBits = (uint8*)bitmap->Bits();
                        int32 nbpr = fNormalBitmap->BytesPerRow();
                        int32 fbpr = bitmap->BytesPerRow();
                        int32 pixels = b.IntegerWidth() + 1;
                        int32 lines = b.IntegerHeight() + 1;
                        // nontransparent version:
                        if (format == B_RGB32 || format == B_RGB32_BIG) {
                                // iterate over color components
                                for (int32 y = 0; y < lines; y++) {
                                        for (int32 x = 0; x < pixels; x++) {
                                                int32 nOffset = 4 * x;
                                                int32 fOffset = 4 * x;
                                                nBits[nOffset + 0] = fBits[fOffset + 0];
                                                nBits[nOffset + 1] = fBits[fOffset + 1];
                                                nBits[nOffset + 2] = fBits[fOffset + 2];
                                                nBits[nOffset + 3] = 255;
                                                // clicked bits are darker (lame method...)
                                                cBits[nOffset + 0] = (uint8)((float)nBits[nOffset + 0]
                                                        * 0.8);
                                                cBits[nOffset + 1] = (uint8)((float)nBits[nOffset + 1]
                                                        * 0.8);
                                                cBits[nOffset + 2] = (uint8)((float)nBits[nOffset + 2]
                                                        * 0.8);
                                                cBits[nOffset + 3] = 255;
                                                // disabled bits have less contrast (lame method...)
                                                uint8 grey = 216;
                                                float dist = (nBits[nOffset + 0] - grey) * 0.4;
                                                dBits[nOffset + 0] = (uint8)(grey + dist);
                                                dist = (nBits[nOffset + 1] - grey) * 0.4;
                                                dBits[nOffset + 1] = (uint8)(grey + dist);
                                                dist = (nBits[nOffset + 2] - grey) * 0.4;
                                                dBits[nOffset + 2] = (uint8)(grey + dist);
                                                dBits[nOffset + 3] = 255;
                                                // disabled bits have less contrast (lame method...)
                                                grey = 188;
                                                dist = (nBits[nOffset + 0] - grey) * 0.4;
                                                dcBits[nOffset + 0] = (uint8)(grey + dist);
                                                dist = (nBits[nOffset + 1] - grey) * 0.4;
                                                dcBits[nOffset + 1] = (uint8)(grey + dist);
                                                dist = (nBits[nOffset + 2] - grey) * 0.4;
                                                dcBits[nOffset + 2] = (uint8)(grey + dist);
                                                dcBits[nOffset + 3] = 255;
                                        }
                                        nBits += nbpr;
                                        dBits += nbpr;
                                        cBits += nbpr;
                                        dcBits += nbpr;
                                        fBits += fbpr;
                                }
                        // transparent version:
                        } else if (format == B_RGBA32 || format == B_RGBA32_BIG) {
                                // iterate over color components
                                for (int32 y = 0; y < lines; y++) {
                                        for (int32 x = 0; x < pixels; x++) {
                                                int32 nOffset = 4 * x;
                                                int32 fOffset = 4 * x;
                                                nBits[nOffset + 0] = fBits[fOffset + 0];
                                                nBits[nOffset + 1] = fBits[fOffset + 1];
                                                nBits[nOffset + 2] = fBits[fOffset + 2];
                                                nBits[nOffset + 3] = fBits[fOffset + 3];
                                                // clicked bits are darker (lame method...)
                                                cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8);
                                                cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8);
                                                cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8);
                                                cBits[nOffset + 3] = fBits[fOffset + 3];
                                                // disabled bits have less opacity

                                                uint8 grey = ((uint16)nBits[nOffset + 0] * 10
                                                    + nBits[nOffset + 1] * 60
                                                        + nBits[nOffset + 2] * 30) / 100;
                                                float dist = (nBits[nOffset + 0] - grey) * 0.3;
                                                dBits[nOffset + 0] = (uint8)(grey + dist);
                                                dist = (nBits[nOffset + 1] - grey) * 0.3;
                                                dBits[nOffset + 1] = (uint8)(grey + dist);
                                                dist = (nBits[nOffset + 2] - grey) * 0.3;
                                                dBits[nOffset + 2] = (uint8)(grey + dist);
                                                dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3);
                                                // disabled bits have less contrast (lame method...)
                                                dcBits[nOffset + 0] = (uint8)(dBits[nOffset + 0] * 0.8);
                                                dcBits[nOffset + 1] = (uint8)(dBits[nOffset + 1] * 0.8);
                                                dcBits[nOffset + 2] = (uint8)(dBits[nOffset + 2] * 0.8);
                                                dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3);
                                        }
                                        nBits += nbpr;
                                        dBits += nbpr;
                                        cBits += nbpr;
                                        dcBits += nbpr;
                                        fBits += fbpr;
                                }
                        // unsupported format
                        } else {
                                printf("BIconButton::_MakeBitmaps() - bitmap has unsupported "
                                        "colorspace\n");
                                status = B_MISMATCHED_VALUES;
                                _DeleteBitmaps();
                        }
                } else {
                        printf("BIconButton::_MakeBitmaps() - error allocating local "
                                "bitmaps\n");
                        status = B_NO_MEMORY;
                        _DeleteBitmaps();
                }
        } else
                printf("BIconButton::_MakeBitmaps() - bitmap is not valid\n");
        return status;
}


void
BIconButton::_DeleteBitmaps()
{
        delete fNormalBitmap;
        fNormalBitmap = NULL;
        delete fDisabledBitmap;
        fDisabledBitmap = NULL;
        delete fClickedBitmap;
        fClickedBitmap = NULL;
        delete fDisabledClickedBitmap;
        fDisabledClickedBitmap = NULL;
}


void
BIconButton::_Update()
{
        if (LockLooper()) {
                Invalidate();
                UnlockLooper();
        }
}


void
BIconButton::_SetFlags(uint32 flags, bool set)
{
        if (_HasFlags(flags) != set) {
                if (set)
                        fButtonState |= flags;
                else
                        fButtonState &= ~flags;

                if ((flags & STATE_PRESSED) != 0)
                        SetValueNoUpdate(set ? B_CONTROL_ON : B_CONTROL_OFF);
                _Update();
        }
}


bool
BIconButton::_HasFlags(uint32 flags) const
{
        return (fButtonState & flags) != 0;
}


//!     This one calls _Update() if needed; BControl::SetTracking() isn't virtual.
void
BIconButton::_SetTracking(bool tracking)
{
        if (IsTracking() == tracking)
                return;

        SetTracking(tracking);
        _Update();
}


}       // namespace BPrivate