1/*
2 * Copyright 2006-2011, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 *		Axel D��rfler, axeld@pinc-software.de.
8 */
9
10
11#include "IconButton.h"
12
13#include <new>
14#include <stdio.h>
15
16#include <Application.h>
17#include <Bitmap.h>
18#include <Control.h>
19#include <ControlLook.h>
20#include <Entry.h>
21#include <IconUtils.h>
22#include <Looper.h>
23#include <Message.h>
24#include <Mime.h>
25#include <Path.h>
26#include <Region.h>
27#include <Resources.h>
28#include <Roster.h>
29#include <TranslationUtils.h>
30#include <Window.h>
31
32
33namespace BPrivate {
34
35
36enum {
37	STATE_NONE			= 0x0000,
38	STATE_PRESSED		= 0x0002,
39	STATE_INSIDE		= 0x0008,
40	STATE_FORCE_PRESSED	= 0x0010,
41};
42
43
44
45BIconButton::BIconButton(const char* name, const char* label,
46	BMessage* message, BHandler* target)
47	:
48	BControl(name, label, message, B_WILL_DRAW),
49	fButtonState(0),
50	fNormalBitmap(NULL),
51	fDisabledBitmap(NULL),
52	fClickedBitmap(NULL),
53	fDisabledClickedBitmap(NULL),
54	fTargetCache(target)
55{
56	SetTarget(target);
57	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
58	SetViewColor(B_TRANSPARENT_32_BIT);
59}
60
61
62BIconButton::~BIconButton()
63{
64	_DeleteBitmaps();
65}
66
67
68void
69BIconButton::MessageReceived(BMessage* message)
70{
71	switch (message->what) {
72		default:
73			BView::MessageReceived(message);
74			break;
75	}
76}
77
78
79void
80BIconButton::AttachedToWindow()
81{
82	AdoptParentColors();
83
84	if (ViewUIColor() != B_NO_COLOR)
85		SetLowUIColor(ViewUIColor());
86
87	SetTarget(fTargetCache);
88	if (!Target())
89		SetTarget(Window());
90}
91
92
93void
94BIconButton::Draw(BRect updateRect)
95{
96	rgb_color background = LowColor();
97
98	BRect r(Bounds());
99
100	uint32 flags = 0;
101	BBitmap* bitmap = fNormalBitmap;
102	if (!IsEnabled()) {
103		flags |= BControlLook::B_DISABLED;
104		bitmap = fDisabledBitmap;
105	}
106	if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED))
107		flags |= BControlLook::B_ACTIVATED;
108
109	if (ShouldDrawBorder()) {
110		DrawBorder(r, updateRect, background, flags);
111		DrawBackground(r, updateRect, background, flags);
112	} else {
113		SetHighColor(background);
114		FillRect(r);
115	}
116
117	if (bitmap && bitmap->IsValid()) {
118		if (bitmap->ColorSpace() == B_RGBA32
119			|| bitmap->ColorSpace() == B_RGBA32_BIG) {
120			SetDrawingMode(B_OP_ALPHA);
121			SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
122		}
123		float x = r.left + floorf((r.Width()
124			- bitmap->Bounds().Width()) / 2.0 + 0.5);
125		float y = r.top + floorf((r.Height()
126			- bitmap->Bounds().Height()) / 2.0 + 0.5);
127		DrawBitmap(bitmap, BPoint(x, y));
128	}
129}
130
131
132bool
133BIconButton::ShouldDrawBorder() const
134{
135	return (IsEnabled() && (IsInside() || IsTracking()))
136		|| _HasFlags(STATE_FORCE_PRESSED);
137}
138
139
140void
141BIconButton::DrawBorder(BRect& frame, const BRect& updateRect,
142	const rgb_color& backgroundColor, uint32 flags)
143{
144	be_control_look->DrawButtonFrame(this, frame, updateRect, backgroundColor,
145		backgroundColor, flags);
146}
147
148
149void
150BIconButton::DrawBackground(BRect& frame, const BRect& updateRect,
151	const rgb_color& backgroundColor, uint32 flags)
152{
153	be_control_look->DrawButtonBackground(this, frame, updateRect,
154		backgroundColor, flags);
155}
156
157
158void
159BIconButton::MouseDown(BPoint where)
160{
161	if (!IsValid())
162		return;
163
164	if (IsEnabled()) {
165		if (Bounds().Contains(where)) {
166			SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
167			_SetFlags(STATE_PRESSED, true);
168			_SetTracking(true);
169		} else {
170			_SetFlags(STATE_PRESSED, false);
171			_SetTracking(false);
172		}
173	}
174}
175
176
177void
178BIconButton::MouseUp(BPoint where)
179{
180	if (!IsValid())
181		return;
182
183	if (IsEnabled() && _HasFlags(STATE_PRESSED)
184		&& Bounds().Contains(where)) {
185		Invoke();
186	} else if (Bounds().Contains(where))
187		SetInside(true);
188
189	_SetFlags(STATE_PRESSED, false);
190	_SetTracking(false);
191}
192
193
194void
195BIconButton::MouseMoved(BPoint where, uint32 transit, const BMessage* message)
196{
197	if (!IsValid())
198		return;
199
200	uint32 buttons = 0;
201	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
202	// catch a mouse up event that we might have missed
203	if (!buttons && _HasFlags(STATE_PRESSED)) {
204		MouseUp(where);
205		return;
206	}
207	if (buttons != 0 && !IsTracking())
208		return;
209
210	SetInside((transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW)
211		&& IsEnabled());
212	if (IsTracking())
213		_SetFlags(STATE_PRESSED, Bounds().Contains(where));
214}
215
216
217void
218BIconButton::GetPreferredSize(float* width, float* height)
219{
220	float minWidth = 0.0f;
221	float minHeight = 0.0f;
222	if (IsValid()) {
223		minWidth += fNormalBitmap->Bounds().IntegerWidth() + 1.0f;
224		minHeight += fNormalBitmap->Bounds().IntegerHeight() + 1.0f;
225	}
226
227	const float kMinSpace = 15.0f;
228	if (minWidth < kMinSpace)
229		minWidth = kMinSpace;
230	if (minHeight < kMinSpace)
231		minHeight = kMinSpace;
232
233	float hPadding = max_c(6.0f, ceilf(minHeight / 4.0f));
234	float vPadding = max_c(6.0f, ceilf(minWidth / 4.0f));
235
236	if (Label() != NULL && Label()[0] != '\0') {
237		font_height fh;
238		GetFontHeight(&fh);
239		minHeight += ceilf(fh.ascent + fh.descent) + vPadding;
240		minWidth += StringWidth(Label()) + vPadding;
241	}
242
243	if (width)
244		*width = minWidth + hPadding;
245	if (height)
246		*height = minHeight + vPadding;
247}
248
249
250BSize
251BIconButton::MinSize()
252{
253	BSize size;
254	GetPreferredSize(&size.width, &size.height);
255	return size;
256}
257
258
259BSize
260BIconButton::MaxSize()
261{
262	return MinSize();
263}
264
265
266status_t
267BIconButton::Invoke(BMessage* message)
268{
269	if (message == NULL)
270		message = Message();
271	if (message != NULL) {
272		BMessage clone(*message);
273		clone.AddInt64("be:when", system_time());
274		clone.AddPointer("be:source", (BView*)this);
275		clone.AddInt32("be:value", Value());
276		return BInvoker::Invoke(&clone);
277	}
278	return BInvoker::Invoke(message);
279}
280
281
282void
283BIconButton::SetPressed(bool pressed)
284{
285	_SetFlags(STATE_FORCE_PRESSED, pressed);
286}
287
288
289bool
290BIconButton::IsPressed() const
291{
292	return _HasFlags(STATE_FORCE_PRESSED);
293}
294
295
296status_t
297BIconButton::SetIcon(int32 resourceID)
298{
299	app_info info;
300	status_t status = be_app->GetAppInfo(&info);
301	if (status != B_OK)
302		return status;
303
304	BResources resources(&info.ref);
305	status = resources.InitCheck();
306	if (status != B_OK)
307		return status;
308
309	size_t size;
310	const void* data = resources.LoadResource(B_VECTOR_ICON_TYPE, resourceID,
311		&size);
312	if (data != NULL) {
313		const BRect bitmapRect(BPoint(0, 0), be_control_look->ComposeIconSize(32));
314		BBitmap bitmap(bitmapRect, B_BITMAP_NO_SERVER_LINK, B_RGBA32);
315		status = bitmap.InitCheck();
316		if (status != B_OK)
317			return status;
318		status = BIconUtils::GetVectorIcon(reinterpret_cast<const uint8*>(data),
319			size, &bitmap);
320		if (status != B_OK)
321			return status;
322		return SetIcon(&bitmap);
323	}
324//	const void* data = resources.LoadResource(B_BITMAP_TYPE, resourceID, &size);
325	return B_ERROR;
326}
327
328
329status_t
330BIconButton::SetIcon(const char* pathToBitmap)
331{
332	if (pathToBitmap == NULL)
333		return B_BAD_VALUE;
334
335	status_t status = B_BAD_VALUE;
336	BBitmap* fileBitmap = NULL;
337	// try to load bitmap from either relative or absolute path
338	BEntry entry(pathToBitmap, true);
339	if (!entry.Exists()) {
340		app_info info;
341		status = be_app->GetAppInfo(&info);
342		if (status == B_OK) {
343			BEntry app_entry(&info.ref, true);
344			BPath path;
345			app_entry.GetPath(&path);
346			status = path.InitCheck();
347			if (status == B_OK) {
348				status = path.GetParent(&path);
349				if (status == B_OK) {
350					status = path.Append(pathToBitmap, true);
351					if (status == B_OK)
352						fileBitmap = BTranslationUtils::GetBitmap(path.Path());
353					else {
354						printf("BIconButton::SetIcon() - path.Append() failed: "
355							"%s\n", strerror(status));
356					}
357				} else {
358					printf("BIconButton::SetIcon() - path.GetParent() failed: "
359						"%s\n", strerror(status));
360				}
361			} else {
362				printf("BIconButton::SetIcon() - path.InitCheck() failed: "
363					"%s\n", strerror(status));
364			}
365		} else {
366			printf("BIconButton::SetIcon() - be_app->GetAppInfo() failed: "
367				"%s\n", strerror(status));
368		}
369	} else
370		fileBitmap = BTranslationUtils::GetBitmap(pathToBitmap);
371	if (fileBitmap) {
372		status = _MakeBitmaps(fileBitmap);
373		delete fileBitmap;
374	} else
375		status = B_ERROR;
376	return status;
377}
378
379
380status_t
381BIconButton::SetIcon(const BBitmap* bitmap, uint32 flags)
382{
383	if (bitmap && bitmap->ColorSpace() == B_CMAP8) {
384		status_t status = bitmap->InitCheck();
385		if (status >= B_OK) {
386			if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap)) {
387				status = _MakeBitmaps(rgb32Bitmap);
388				delete rgb32Bitmap;
389			} else
390				status = B_NO_MEMORY;
391		}
392		return status;
393	} else
394		return _MakeBitmaps(bitmap);
395}
396
397
398status_t
399BIconButton::SetIcon(const BMimeType* fileType, bool small)
400{
401	status_t status = fileType ? fileType->InitCheck() : B_BAD_VALUE;
402	if (status >= B_OK) {
403		BBitmap* mimeBitmap = new(std::nothrow) BBitmap(BRect(0.0, 0.0, 15.0,
404			15.0), B_CMAP8);
405		if (mimeBitmap && mimeBitmap->IsValid()) {
406			status = fileType->GetIcon(mimeBitmap, small ? B_MINI_ICON
407				: B_LARGE_ICON);
408			if (status >= B_OK) {
409				if (BBitmap* bitmap = _ConvertToRGB32(mimeBitmap)) {
410					status = _MakeBitmaps(bitmap);
411					delete bitmap;
412				} else {
413					printf("BIconButton::SetIcon() - B_RGB32 bitmap is not "
414						"valid\n");
415				}
416			} else {
417				printf("BIconButton::SetIcon() - fileType->GetIcon() failed: "
418					"%s\n", strerror(status));
419			}
420		} else
421			printf("BIconButton::SetIcon() - B_CMAP8 bitmap is not valid\n");
422		delete mimeBitmap;
423	} else {
424		printf("BIconButton::SetIcon() - fileType is not valid: %s\n",
425			strerror(status));
426	}
427	return status;
428}
429
430
431status_t
432BIconButton::SetIcon(const unsigned char* bitsFromQuickRes,
433	uint32 width, uint32 height, color_space format, bool convertToBW)
434{
435	status_t status = B_BAD_VALUE;
436	if (bitsFromQuickRes && width > 0 && height > 0) {
437		BBitmap* quickResBitmap = new(std::nothrow) BBitmap(BRect(0.0, 0.0,
438			width - 1.0, height - 1.0), format);
439		status = quickResBitmap ? quickResBitmap->InitCheck() : B_ERROR;
440		if (status >= B_OK) {
441			// It doesn't look right to copy BitsLength() bytes, but bitmaps
442			// exported from QuickRes still contain their padding, so it is
443			// all right.
444			memcpy(quickResBitmap->Bits(), bitsFromQuickRes,
445				quickResBitmap->BitsLength());
446			if (format != B_RGB32 && format != B_RGBA32
447				&& format != B_RGB32_BIG && format != B_RGBA32_BIG) {
448				// colorspace needs conversion
449				BBitmap* bitmap = new(std::nothrow) BBitmap(
450					quickResBitmap->Bounds(), B_RGB32, true);
451				if (bitmap && bitmap->IsValid()) {
452					if (bitmap->Lock()) {
453						BView* helper = new BView(bitmap->Bounds(), "helper",
454							B_FOLLOW_NONE, B_WILL_DRAW);
455						bitmap->AddChild(helper);
456						helper->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
457						helper->FillRect(helper->Bounds());
458						helper->SetDrawingMode(B_OP_OVER);
459						helper->DrawBitmap(quickResBitmap, BPoint(0.0, 0.0));
460						helper->Sync();
461						bitmap->Unlock();
462					}
463					status = _MakeBitmaps(bitmap);
464				} else {
465					printf("BIconButton::SetIcon() - B_RGB32 bitmap is not "
466						"valid\n");
467				}
468				delete bitmap;
469			} else {
470				// native colorspace (32 bits)
471				if (convertToBW) {
472					// convert to gray scale icon
473					uint8* bits = (uint8*)quickResBitmap->Bits();
474					uint32 bpr = quickResBitmap->BytesPerRow();
475					for (uint32 y = 0; y < height; y++) {
476						uint8* handle = bits;
477						uint8 gray;
478						for (uint32 x = 0; x < width; x++) {
479							gray = uint8((116 * handle[0] + 600 * handle[1]
480								+ 308 * handle[2]) / 1024);
481							handle[0] = gray;
482							handle[1] = gray;
483							handle[2] = gray;
484							handle += 4;
485						}
486						bits += bpr;
487					}
488				}
489				status = _MakeBitmaps(quickResBitmap);
490			}
491		} else {
492			printf("BIconButton::SetIcon() - error allocating bitmap: "
493				"%s\n", strerror(status));
494		}
495		delete quickResBitmap;
496	}
497	return status;
498}
499
500
501void
502BIconButton::ClearIcon()
503{
504	_DeleteBitmaps();
505	_Update();
506}
507
508
509void
510BIconButton::TrimIcon(bool keepAspect)
511{
512	if (fNormalBitmap == NULL)
513		return;
514
515	uint8* bits = (uint8*)fNormalBitmap->Bits();
516	uint32 bpr = fNormalBitmap->BytesPerRow();
517	uint32 width = fNormalBitmap->Bounds().IntegerWidth() + 1;
518	uint32 height = fNormalBitmap->Bounds().IntegerHeight() + 1;
519	BRect trimmed(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN);
520	for (uint32 y = 0; y < height; y++) {
521		uint8* b = bits + 3;
522		bool rowHasAlpha = false;
523		for (uint32 x = 0; x < width; x++) {
524			if (*b) {
525				rowHasAlpha = true;
526				if (x < trimmed.left)
527					trimmed.left = x;
528				if (x > trimmed.right)
529					trimmed.right = x;
530			}
531			b += 4;
532		}
533		if (rowHasAlpha) {
534			if (y < trimmed.top)
535				trimmed.top = y;
536			if (y > trimmed.bottom)
537				trimmed.bottom = y;
538		}
539		bits += bpr;
540	}
541	if (!trimmed.IsValid())
542		return;
543	if (keepAspect) {
544		float minInset = trimmed.left;
545		minInset = min_c(minInset, trimmed.top);
546		minInset = min_c(minInset, fNormalBitmap->Bounds().right
547			- trimmed.right);
548		minInset = min_c(minInset, fNormalBitmap->Bounds().bottom
549			- trimmed.bottom);
550		trimmed = fNormalBitmap->Bounds().InsetByCopy(minInset, minInset);
551	}
552	trimmed = trimmed & fNormalBitmap->Bounds();
553	BBitmap trimmedBitmap(trimmed.OffsetToCopy(B_ORIGIN),
554		B_BITMAP_NO_SERVER_LINK, B_RGBA32);
555	bits = (uint8*)fNormalBitmap->Bits();
556	bits += 4 * (int32)trimmed.left + bpr * (int32)trimmed.top;
557	uint8* dst = (uint8*)trimmedBitmap.Bits();
558	uint32 trimmedWidth = trimmedBitmap.Bounds().IntegerWidth() + 1;
559	uint32 trimmedHeight = trimmedBitmap.Bounds().IntegerHeight() + 1;
560	uint32 trimmedBPR = trimmedBitmap.BytesPerRow();
561	for (uint32 y = 0; y < trimmedHeight; y++) {
562		memcpy(dst, bits, trimmedWidth * 4);
563		dst += trimmedBPR;
564		bits += bpr;
565	}
566	SetIcon(&trimmedBitmap);
567}
568
569
570bool
571BIconButton::IsValid() const
572{
573	return (fNormalBitmap && fDisabledBitmap && fClickedBitmap
574		&& fDisabledClickedBitmap
575		&& fNormalBitmap->IsValid()
576		&& fDisabledBitmap->IsValid()
577		&& fClickedBitmap->IsValid()
578		&& fDisabledClickedBitmap->IsValid());
579}
580
581
582BBitmap*
583BIconButton::Bitmap() const
584{
585	BBitmap* bitmap = NULL;
586	if (fNormalBitmap && fNormalBitmap->IsValid()) {
587		bitmap = new(std::nothrow) BBitmap(fNormalBitmap);
588		if (bitmap != NULL && bitmap->IsValid()) {
589			// TODO: remove this functionality when we use real transparent
590			// bitmaps
591			uint8* bits = (uint8*)bitmap->Bits();
592			uint32 bpr = bitmap->BytesPerRow();
593			uint32 width = bitmap->Bounds().IntegerWidth() + 1;
594			uint32 height = bitmap->Bounds().IntegerHeight() + 1;
595			color_space format = bitmap->ColorSpace();
596			if (format == B_CMAP8) {
597				// replace gray with magic transparent index
598			} else if (format == B_RGB32) {
599				for (uint32 y = 0; y < height; y++) {
600					uint8* bitsHandle = bits;
601					for (uint32 x = 0; x < width; x++) {
602						if (bitsHandle[0] == 216
603							&& bitsHandle[1] == 216
604							&& bitsHandle[2] == 216) {
605							// make this pixel completely transparent
606							bitsHandle[3] = 0;
607						}
608						bitsHandle += 4;
609					}
610					bits += bpr;
611				}
612			}
613		} else {
614			delete bitmap;
615			bitmap = NULL;
616		}
617	}
618	return bitmap;
619}
620
621
622void
623BIconButton::SetValue(int32 value)
624{
625	BControl::SetValue(value);
626	_SetFlags(STATE_PRESSED, value != 0);
627}
628
629
630void
631BIconButton::SetEnabled(bool enabled)
632{
633	BControl::SetEnabled(enabled);
634	if (!enabled) {
635		SetInside(false);
636		_SetTracking(false);
637	}
638}
639
640
641// #pragma mark - protected
642
643
644bool
645BIconButton::IsInside() const
646{
647	return _HasFlags(STATE_INSIDE);
648}
649
650
651void
652BIconButton::SetInside(bool inside)
653{
654	_SetFlags(STATE_INSIDE, inside);
655}
656
657
658// #pragma mark - private
659
660
661BBitmap*
662BIconButton::_ConvertToRGB32(const BBitmap* bitmap) const
663{
664	BBitmap* convertedBitmap = new(std::nothrow) BBitmap(bitmap->Bounds(),
665		B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
666	if (convertedBitmap && convertedBitmap->IsValid()) {
667		memset(convertedBitmap->Bits(), 0, convertedBitmap->BitsLength());
668		if (convertedBitmap->Lock()) {
669			BView* helper = new BView(bitmap->Bounds(), "helper",
670				B_FOLLOW_NONE, B_WILL_DRAW);
671			convertedBitmap->AddChild(helper);
672			helper->SetDrawingMode(B_OP_OVER);
673			helper->DrawBitmap(bitmap, BPoint(0.0, 0.0));
674			helper->Sync();
675			convertedBitmap->Unlock();
676		}
677	} else {
678		delete convertedBitmap;
679		convertedBitmap = NULL;
680	}
681	return convertedBitmap;
682}
683
684
685status_t
686BIconButton::_MakeBitmaps(const BBitmap* bitmap)
687{
688	status_t status = bitmap ? bitmap->InitCheck() : B_BAD_VALUE;
689	if (status == B_OK) {
690		// make our own versions of the bitmap
691		BRect b(bitmap->Bounds());
692		_DeleteBitmaps();
693		color_space format = bitmap->ColorSpace();
694		fNormalBitmap = new(std::nothrow) BBitmap(b, format);
695		fDisabledBitmap = new(std::nothrow) BBitmap(b, format);
696		fClickedBitmap = new(std::nothrow) BBitmap(b, format);
697		fDisabledClickedBitmap = new(std::nothrow) BBitmap(b, format);
698		if (IsValid()) {
699			// copy bitmaps from file bitmap
700			uint8* nBits = (uint8*)fNormalBitmap->Bits();
701			uint8* dBits = (uint8*)fDisabledBitmap->Bits();
702			uint8* cBits = (uint8*)fClickedBitmap->Bits();
703			uint8* dcBits = (uint8*)fDisabledClickedBitmap->Bits();
704			uint8* fBits = (uint8*)bitmap->Bits();
705			int32 nbpr = fNormalBitmap->BytesPerRow();
706			int32 fbpr = bitmap->BytesPerRow();
707			int32 pixels = b.IntegerWidth() + 1;
708			int32 lines = b.IntegerHeight() + 1;
709			// nontransparent version:
710			if (format == B_RGB32 || format == B_RGB32_BIG) {
711				// iterate over color components
712				for (int32 y = 0; y < lines; y++) {
713					for (int32 x = 0; x < pixels; x++) {
714						int32 nOffset = 4 * x;
715						int32 fOffset = 4 * x;
716						nBits[nOffset + 0] = fBits[fOffset + 0];
717						nBits[nOffset + 1] = fBits[fOffset + 1];
718						nBits[nOffset + 2] = fBits[fOffset + 2];
719						nBits[nOffset + 3] = 255;
720						// clicked bits are darker (lame method...)
721						cBits[nOffset + 0] = (uint8)((float)nBits[nOffset + 0]
722							* 0.8);
723						cBits[nOffset + 1] = (uint8)((float)nBits[nOffset + 1]
724							* 0.8);
725						cBits[nOffset + 2] = (uint8)((float)nBits[nOffset + 2]
726							* 0.8);
727						cBits[nOffset + 3] = 255;
728						// disabled bits have less contrast (lame method...)
729						uint8 grey = 216;
730						float dist = (nBits[nOffset + 0] - grey) * 0.4;
731						dBits[nOffset + 0] = (uint8)(grey + dist);
732						dist = (nBits[nOffset + 1] - grey) * 0.4;
733						dBits[nOffset + 1] = (uint8)(grey + dist);
734						dist = (nBits[nOffset + 2] - grey) * 0.4;
735						dBits[nOffset + 2] = (uint8)(grey + dist);
736						dBits[nOffset + 3] = 255;
737						// disabled bits have less contrast (lame method...)
738						grey = 188;
739						dist = (nBits[nOffset + 0] - grey) * 0.4;
740						dcBits[nOffset + 0] = (uint8)(grey + dist);
741						dist = (nBits[nOffset + 1] - grey) * 0.4;
742						dcBits[nOffset + 1] = (uint8)(grey + dist);
743						dist = (nBits[nOffset + 2] - grey) * 0.4;
744						dcBits[nOffset + 2] = (uint8)(grey + dist);
745						dcBits[nOffset + 3] = 255;
746					}
747					nBits += nbpr;
748					dBits += nbpr;
749					cBits += nbpr;
750					dcBits += nbpr;
751					fBits += fbpr;
752				}
753			// transparent version:
754			} else if (format == B_RGBA32 || format == B_RGBA32_BIG) {
755				// iterate over color components
756				for (int32 y = 0; y < lines; y++) {
757					for (int32 x = 0; x < pixels; x++) {
758						int32 nOffset = 4 * x;
759						int32 fOffset = 4 * x;
760						nBits[nOffset + 0] = fBits[fOffset + 0];
761						nBits[nOffset + 1] = fBits[fOffset + 1];
762						nBits[nOffset + 2] = fBits[fOffset + 2];
763						nBits[nOffset + 3] = fBits[fOffset + 3];
764						// clicked bits are darker (lame method...)
765						cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8);
766						cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8);
767						cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8);
768						cBits[nOffset + 3] = fBits[fOffset + 3];
769						// disabled bits have less opacity
770
771						uint8 grey = ((uint16)nBits[nOffset + 0] * 10
772						    + nBits[nOffset + 1] * 60
773							+ nBits[nOffset + 2] * 30) / 100;
774						float dist = (nBits[nOffset + 0] - grey) * 0.3;
775						dBits[nOffset + 0] = (uint8)(grey + dist);
776						dist = (nBits[nOffset + 1] - grey) * 0.3;
777						dBits[nOffset + 1] = (uint8)(grey + dist);
778						dist = (nBits[nOffset + 2] - grey) * 0.3;
779						dBits[nOffset + 2] = (uint8)(grey + dist);
780						dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3);
781						// disabled bits have less contrast (lame method...)
782						dcBits[nOffset + 0] = (uint8)(dBits[nOffset + 0] * 0.8);
783						dcBits[nOffset + 1] = (uint8)(dBits[nOffset + 1] * 0.8);
784						dcBits[nOffset + 2] = (uint8)(dBits[nOffset + 2] * 0.8);
785						dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3);
786					}
787					nBits += nbpr;
788					dBits += nbpr;
789					cBits += nbpr;
790					dcBits += nbpr;
791					fBits += fbpr;
792				}
793			// unsupported format
794			} else {
795				printf("BIconButton::_MakeBitmaps() - bitmap has unsupported "
796					"colorspace\n");
797				status = B_MISMATCHED_VALUES;
798				_DeleteBitmaps();
799			}
800		} else {
801			printf("BIconButton::_MakeBitmaps() - error allocating local "
802				"bitmaps\n");
803			status = B_NO_MEMORY;
804			_DeleteBitmaps();
805		}
806	} else
807		printf("BIconButton::_MakeBitmaps() - bitmap is not valid\n");
808	return status;
809}
810
811
812void
813BIconButton::_DeleteBitmaps()
814{
815	delete fNormalBitmap;
816	fNormalBitmap = NULL;
817	delete fDisabledBitmap;
818	fDisabledBitmap = NULL;
819	delete fClickedBitmap;
820	fClickedBitmap = NULL;
821	delete fDisabledClickedBitmap;
822	fDisabledClickedBitmap = NULL;
823}
824
825
826void
827BIconButton::_Update()
828{
829	if (LockLooper()) {
830		Invalidate();
831		UnlockLooper();
832	}
833}
834
835
836void
837BIconButton::_SetFlags(uint32 flags, bool set)
838{
839	if (_HasFlags(flags) != set) {
840		if (set)
841			fButtonState |= flags;
842		else
843			fButtonState &= ~flags;
844
845		if ((flags & STATE_PRESSED) != 0)
846			SetValueNoUpdate(set ? B_CONTROL_ON : B_CONTROL_OFF);
847		_Update();
848	}
849}
850
851
852bool
853BIconButton::_HasFlags(uint32 flags) const
854{
855	return (fButtonState & flags) != 0;
856}
857
858
859//!	This one calls _Update() if needed; BControl::SetTracking() isn't virtual.
860void
861BIconButton::_SetTracking(bool tracking)
862{
863	if (IsTracking() == tracking)
864		return;
865
866	SetTracking(tracking);
867	_Update();
868}
869
870
871}	// namespace BPrivate
872