⛏️ index : haiku.git

/*
 * 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