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		BBitmap bitmap(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK, B_RGBA32);
314		status = bitmap.InitCheck();
315		if (status != B_OK)
316			return status;
317		status = BIconUtils::GetVectorIcon(reinterpret_cast<const uint8*>(data),
318			size, &bitmap);
319		if (status != B_OK)
320			return status;
321		return SetIcon(&bitmap);
322	}
323//	const void* data = resources.LoadResource(B_BITMAP_TYPE, resourceID, &size);
324	return B_ERROR;
325}
326
327
328status_t
329BIconButton::SetIcon(const char* pathToBitmap)
330{
331	if (pathToBitmap == NULL)
332		return B_BAD_VALUE;
333
334	status_t status = B_BAD_VALUE;
335	BBitmap* fileBitmap = NULL;
336	// try to load bitmap from either relative or absolute path
337	BEntry entry(pathToBitmap, true);
338	if (!entry.Exists()) {
339		app_info info;
340		status = be_app->GetAppInfo(&info);
341		if (status == B_OK) {
342			BEntry app_entry(&info.ref, true);
343			BPath path;
344			app_entry.GetPath(&path);
345			status = path.InitCheck();
346			if (status == B_OK) {
347				status = path.GetParent(&path);
348				if (status == B_OK) {
349					status = path.Append(pathToBitmap, true);
350					if (status == B_OK)
351						fileBitmap = BTranslationUtils::GetBitmap(path.Path());
352					else {
353						printf("BIconButton::SetIcon() - path.Append() failed: "
354							"%s\n", strerror(status));
355					}
356				} else {
357					printf("BIconButton::SetIcon() - path.GetParent() failed: "
358						"%s\n", strerror(status));
359				}
360			} else {
361				printf("BIconButton::SetIcon() - path.InitCheck() failed: "
362					"%s\n", strerror(status));
363			}
364		} else {
365			printf("BIconButton::SetIcon() - be_app->GetAppInfo() failed: "
366				"%s\n", strerror(status));
367		}
368	} else
369		fileBitmap = BTranslationUtils::GetBitmap(pathToBitmap);
370	if (fileBitmap) {
371		status = _MakeBitmaps(fileBitmap);
372		delete fileBitmap;
373	} else
374		status = B_ERROR;
375	return status;
376}
377
378
379status_t
380BIconButton::SetIcon(const BBitmap* bitmap, uint32 flags)
381{
382	if (bitmap && bitmap->ColorSpace() == B_CMAP8) {
383		status_t status = bitmap->InitCheck();
384		if (status >= B_OK) {
385			if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap)) {
386				status = _MakeBitmaps(rgb32Bitmap);
387				delete rgb32Bitmap;
388			} else
389				status = B_NO_MEMORY;
390		}
391		return status;
392	} else
393		return _MakeBitmaps(bitmap);
394}
395
396
397status_t
398BIconButton::SetIcon(const BMimeType* fileType, bool small)
399{
400	status_t status = fileType ? fileType->InitCheck() : B_BAD_VALUE;
401	if (status >= B_OK) {
402		BBitmap* mimeBitmap = new(std::nothrow) BBitmap(BRect(0.0, 0.0, 15.0,
403			15.0), B_CMAP8);
404		if (mimeBitmap && mimeBitmap->IsValid()) {
405			status = fileType->GetIcon(mimeBitmap, small ? B_MINI_ICON
406				: B_LARGE_ICON);
407			if (status >= B_OK) {
408				if (BBitmap* bitmap = _ConvertToRGB32(mimeBitmap)) {
409					status = _MakeBitmaps(bitmap);
410					delete bitmap;
411				} else {
412					printf("BIconButton::SetIcon() - B_RGB32 bitmap is not "
413						"valid\n");
414				}
415			} else {
416				printf("BIconButton::SetIcon() - fileType->GetIcon() failed: "
417					"%s\n", strerror(status));
418			}
419		} else
420			printf("BIconButton::SetIcon() - B_CMAP8 bitmap is not valid\n");
421		delete mimeBitmap;
422	} else {
423		printf("BIconButton::SetIcon() - fileType is not valid: %s\n",
424			strerror(status));
425	}
426	return status;
427}
428
429
430status_t
431BIconButton::SetIcon(const unsigned char* bitsFromQuickRes,
432	uint32 width, uint32 height, color_space format, bool convertToBW)
433{
434	status_t status = B_BAD_VALUE;
435	if (bitsFromQuickRes && width > 0 && height > 0) {
436		BBitmap* quickResBitmap = new(std::nothrow) BBitmap(BRect(0.0, 0.0,
437			width - 1.0, height - 1.0), format);
438		status = quickResBitmap ? quickResBitmap->InitCheck() : B_ERROR;
439		if (status >= B_OK) {
440			// It doesn't look right to copy BitsLength() bytes, but bitmaps
441			// exported from QuickRes still contain their padding, so it is
442			// all right.
443			memcpy(quickResBitmap->Bits(), bitsFromQuickRes,
444				quickResBitmap->BitsLength());
445			if (format != B_RGB32 && format != B_RGBA32
446				&& format != B_RGB32_BIG && format != B_RGBA32_BIG) {
447				// colorspace needs conversion
448				BBitmap* bitmap = new(std::nothrow) BBitmap(
449					quickResBitmap->Bounds(), B_RGB32, true);
450				if (bitmap && bitmap->IsValid()) {
451					if (bitmap->Lock()) {
452						BView* helper = new BView(bitmap->Bounds(), "helper",
453							B_FOLLOW_NONE, B_WILL_DRAW);
454						bitmap->AddChild(helper);
455						helper->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
456						helper->FillRect(helper->Bounds());
457						helper->SetDrawingMode(B_OP_OVER);
458						helper->DrawBitmap(quickResBitmap, BPoint(0.0, 0.0));
459						helper->Sync();
460						bitmap->Unlock();
461					}
462					status = _MakeBitmaps(bitmap);
463				} else {
464					printf("BIconButton::SetIcon() - B_RGB32 bitmap is not "
465						"valid\n");
466				}
467				delete bitmap;
468			} else {
469				// native colorspace (32 bits)
470				if (convertToBW) {
471					// convert to gray scale icon
472					uint8* bits = (uint8*)quickResBitmap->Bits();
473					uint32 bpr = quickResBitmap->BytesPerRow();
474					for (uint32 y = 0; y < height; y++) {
475						uint8* handle = bits;
476						uint8 gray;
477						for (uint32 x = 0; x < width; x++) {
478							gray = uint8((116 * handle[0] + 600 * handle[1]
479								+ 308 * handle[2]) / 1024);
480							handle[0] = gray;
481							handle[1] = gray;
482							handle[2] = gray;
483							handle += 4;
484						}
485						bits += bpr;
486					}
487				}
488				status = _MakeBitmaps(quickResBitmap);
489			}
490		} else {
491			printf("BIconButton::SetIcon() - error allocating bitmap: "
492				"%s\n", strerror(status));
493		}
494		delete quickResBitmap;
495	}
496	return status;
497}
498
499
500void
501BIconButton::ClearIcon()
502{
503	_DeleteBitmaps();
504	_Update();
505}
506
507
508void
509BIconButton::TrimIcon(bool keepAspect)
510{
511	if (fNormalBitmap == NULL)
512		return;
513
514	uint8* bits = (uint8*)fNormalBitmap->Bits();
515	uint32 bpr = fNormalBitmap->BytesPerRow();
516	uint32 width = fNormalBitmap->Bounds().IntegerWidth() + 1;
517	uint32 height = fNormalBitmap->Bounds().IntegerHeight() + 1;
518	BRect trimmed(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN);
519	for (uint32 y = 0; y < height; y++) {
520		uint8* b = bits + 3;
521		bool rowHasAlpha = false;
522		for (uint32 x = 0; x < width; x++) {
523			if (*b) {
524				rowHasAlpha = true;
525				if (x < trimmed.left)
526					trimmed.left = x;
527				if (x > trimmed.right)
528					trimmed.right = x;
529			}
530			b += 4;
531		}
532		if (rowHasAlpha) {
533			if (y < trimmed.top)
534				trimmed.top = y;
535			if (y > trimmed.bottom)
536				trimmed.bottom = y;
537		}
538		bits += bpr;
539	}
540	if (!trimmed.IsValid())
541		return;
542	if (keepAspect) {
543		float minInset = trimmed.left;
544		minInset = min_c(minInset, trimmed.top);
545		minInset = min_c(minInset, fNormalBitmap->Bounds().right
546			- trimmed.right);
547		minInset = min_c(minInset, fNormalBitmap->Bounds().bottom
548			- trimmed.bottom);
549		trimmed = fNormalBitmap->Bounds().InsetByCopy(minInset, minInset);
550	}
551	trimmed = trimmed & fNormalBitmap->Bounds();
552	BBitmap trimmedBitmap(trimmed.OffsetToCopy(B_ORIGIN),
553		B_BITMAP_NO_SERVER_LINK, B_RGBA32);
554	bits = (uint8*)fNormalBitmap->Bits();
555	bits += 4 * (int32)trimmed.left + bpr * (int32)trimmed.top;
556	uint8* dst = (uint8*)trimmedBitmap.Bits();
557	uint32 trimmedWidth = trimmedBitmap.Bounds().IntegerWidth() + 1;
558	uint32 trimmedHeight = trimmedBitmap.Bounds().IntegerHeight() + 1;
559	uint32 trimmedBPR = trimmedBitmap.BytesPerRow();
560	for (uint32 y = 0; y < trimmedHeight; y++) {
561		memcpy(dst, bits, trimmedWidth * 4);
562		dst += trimmedBPR;
563		bits += bpr;
564	}
565	SetIcon(&trimmedBitmap);
566}
567
568
569bool
570BIconButton::IsValid() const
571{
572	return (fNormalBitmap && fDisabledBitmap && fClickedBitmap
573		&& fDisabledClickedBitmap
574		&& fNormalBitmap->IsValid()
575		&& fDisabledBitmap->IsValid()
576		&& fClickedBitmap->IsValid()
577		&& fDisabledClickedBitmap->IsValid());
578}
579
580
581BBitmap*
582BIconButton::Bitmap() const
583{
584	BBitmap* bitmap = NULL;
585	if (fNormalBitmap && fNormalBitmap->IsValid()) {
586		bitmap = new(std::nothrow) BBitmap(fNormalBitmap);
587		if (bitmap != NULL && bitmap->IsValid()) {
588			// TODO: remove this functionality when we use real transparent
589			// bitmaps
590			uint8* bits = (uint8*)bitmap->Bits();
591			uint32 bpr = bitmap->BytesPerRow();
592			uint32 width = bitmap->Bounds().IntegerWidth() + 1;
593			uint32 height = bitmap->Bounds().IntegerHeight() + 1;
594			color_space format = bitmap->ColorSpace();
595			if (format == B_CMAP8) {
596				// replace gray with magic transparent index
597			} else if (format == B_RGB32) {
598				for (uint32 y = 0; y < height; y++) {
599					uint8* bitsHandle = bits;
600					for (uint32 x = 0; x < width; x++) {
601						if (bitsHandle[0] == 216
602							&& bitsHandle[1] == 216
603							&& bitsHandle[2] == 216) {
604							// make this pixel completely transparent
605							bitsHandle[3] = 0;
606						}
607						bitsHandle += 4;
608					}
609					bits += bpr;
610				}
611			}
612		} else {
613			delete bitmap;
614			bitmap = NULL;
615		}
616	}
617	return bitmap;
618}
619
620
621void
622BIconButton::SetValue(int32 value)
623{
624	BControl::SetValue(value);
625	_SetFlags(STATE_PRESSED, value != 0);
626}
627
628
629void
630BIconButton::SetEnabled(bool enabled)
631{
632	BControl::SetEnabled(enabled);
633	if (!enabled) {
634		SetInside(false);
635		_SetTracking(false);
636	}
637}
638
639
640// #pragma mark - protected
641
642
643bool
644BIconButton::IsInside() const
645{
646	return _HasFlags(STATE_INSIDE);
647}
648
649
650void
651BIconButton::SetInside(bool inside)
652{
653	_SetFlags(STATE_INSIDE, inside);
654}
655
656
657// #pragma mark - private
658
659
660BBitmap*
661BIconButton::_ConvertToRGB32(const BBitmap* bitmap) const
662{
663	BBitmap* convertedBitmap = new(std::nothrow) BBitmap(bitmap->Bounds(),
664		B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
665	if (convertedBitmap && convertedBitmap->IsValid()) {
666		memset(convertedBitmap->Bits(), 0, convertedBitmap->BitsLength());
667		if (convertedBitmap->Lock()) {
668			BView* helper = new BView(bitmap->Bounds(), "helper",
669				B_FOLLOW_NONE, B_WILL_DRAW);
670			convertedBitmap->AddChild(helper);
671			helper->SetDrawingMode(B_OP_OVER);
672			helper->DrawBitmap(bitmap, BPoint(0.0, 0.0));
673			helper->Sync();
674			convertedBitmap->Unlock();
675		}
676	} else {
677		delete convertedBitmap;
678		convertedBitmap = NULL;
679	}
680	return convertedBitmap;
681}
682
683
684status_t
685BIconButton::_MakeBitmaps(const BBitmap* bitmap)
686{
687	status_t status = bitmap ? bitmap->InitCheck() : B_BAD_VALUE;
688	if (status == B_OK) {
689		// make our own versions of the bitmap
690		BRect b(bitmap->Bounds());
691		_DeleteBitmaps();
692		color_space format = bitmap->ColorSpace();
693		fNormalBitmap = new(std::nothrow) BBitmap(b, format);
694		fDisabledBitmap = new(std::nothrow) BBitmap(b, format);
695		fClickedBitmap = new(std::nothrow) BBitmap(b, format);
696		fDisabledClickedBitmap = new(std::nothrow) BBitmap(b, format);
697		if (IsValid()) {
698			// copy bitmaps from file bitmap
699			uint8* nBits = (uint8*)fNormalBitmap->Bits();
700			uint8* dBits = (uint8*)fDisabledBitmap->Bits();
701			uint8* cBits = (uint8*)fClickedBitmap->Bits();
702			uint8* dcBits = (uint8*)fDisabledClickedBitmap->Bits();
703			uint8* fBits = (uint8*)bitmap->Bits();
704			int32 nbpr = fNormalBitmap->BytesPerRow();
705			int32 fbpr = bitmap->BytesPerRow();
706			int32 pixels = b.IntegerWidth() + 1;
707			int32 lines = b.IntegerHeight() + 1;
708			// nontransparent version:
709			if (format == B_RGB32 || format == B_RGB32_BIG) {
710				// iterate over color components
711				for (int32 y = 0; y < lines; y++) {
712					for (int32 x = 0; x < pixels; x++) {
713						int32 nOffset = 4 * x;
714						int32 fOffset = 4 * x;
715						nBits[nOffset + 0] = fBits[fOffset + 0];
716						nBits[nOffset + 1] = fBits[fOffset + 1];
717						nBits[nOffset + 2] = fBits[fOffset + 2];
718						nBits[nOffset + 3] = 255;
719						// clicked bits are darker (lame method...)
720						cBits[nOffset + 0] = (uint8)((float)nBits[nOffset + 0]
721							* 0.8);
722						cBits[nOffset + 1] = (uint8)((float)nBits[nOffset + 1]
723							* 0.8);
724						cBits[nOffset + 2] = (uint8)((float)nBits[nOffset + 2]
725							* 0.8);
726						cBits[nOffset + 3] = 255;
727						// disabled bits have less contrast (lame method...)
728						uint8 grey = 216;
729						float dist = (nBits[nOffset + 0] - grey) * 0.4;
730						dBits[nOffset + 0] = (uint8)(grey + dist);
731						dist = (nBits[nOffset + 1] - grey) * 0.4;
732						dBits[nOffset + 1] = (uint8)(grey + dist);
733						dist = (nBits[nOffset + 2] - grey) * 0.4;
734						dBits[nOffset + 2] = (uint8)(grey + dist);
735						dBits[nOffset + 3] = 255;
736						// disabled bits have less contrast (lame method...)
737						grey = 188;
738						dist = (nBits[nOffset + 0] - grey) * 0.4;
739						dcBits[nOffset + 0] = (uint8)(grey + dist);
740						dist = (nBits[nOffset + 1] - grey) * 0.4;
741						dcBits[nOffset + 1] = (uint8)(grey + dist);
742						dist = (nBits[nOffset + 2] - grey) * 0.4;
743						dcBits[nOffset + 2] = (uint8)(grey + dist);
744						dcBits[nOffset + 3] = 255;
745					}
746					nBits += nbpr;
747					dBits += nbpr;
748					cBits += nbpr;
749					dcBits += nbpr;
750					fBits += fbpr;
751				}
752			// transparent version:
753			} else if (format == B_RGBA32 || format == B_RGBA32_BIG) {
754				// iterate over color components
755				for (int32 y = 0; y < lines; y++) {
756					for (int32 x = 0; x < pixels; x++) {
757						int32 nOffset = 4 * x;
758						int32 fOffset = 4 * x;
759						nBits[nOffset + 0] = fBits[fOffset + 0];
760						nBits[nOffset + 1] = fBits[fOffset + 1];
761						nBits[nOffset + 2] = fBits[fOffset + 2];
762						nBits[nOffset + 3] = fBits[fOffset + 3];
763						// clicked bits are darker (lame method...)
764						cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8);
765						cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8);
766						cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8);
767						cBits[nOffset + 3] = fBits[fOffset + 3];
768						// disabled bits have less opacity
769
770						uint8 grey = ((uint16)nBits[nOffset + 0] * 10
771						    + nBits[nOffset + 1] * 60
772							+ nBits[nOffset + 2] * 30) / 100;
773						float dist = (nBits[nOffset + 0] - grey) * 0.3;
774						dBits[nOffset + 0] = (uint8)(grey + dist);
775						dist = (nBits[nOffset + 1] - grey) * 0.3;
776						dBits[nOffset + 1] = (uint8)(grey + dist);
777						dist = (nBits[nOffset + 2] - grey) * 0.3;
778						dBits[nOffset + 2] = (uint8)(grey + dist);
779						dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3);
780						// disabled bits have less contrast (lame method...)
781						dcBits[nOffset + 0] = (uint8)(dBits[nOffset + 0] * 0.8);
782						dcBits[nOffset + 1] = (uint8)(dBits[nOffset + 1] * 0.8);
783						dcBits[nOffset + 2] = (uint8)(dBits[nOffset + 2] * 0.8);
784						dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3);
785					}
786					nBits += nbpr;
787					dBits += nbpr;
788					cBits += nbpr;
789					dcBits += nbpr;
790					fBits += fbpr;
791				}
792			// unsupported format
793			} else {
794				printf("BIconButton::_MakeBitmaps() - bitmap has unsupported "
795					"colorspace\n");
796				status = B_MISMATCHED_VALUES;
797				_DeleteBitmaps();
798			}
799		} else {
800			printf("BIconButton::_MakeBitmaps() - error allocating local "
801				"bitmaps\n");
802			status = B_NO_MEMORY;
803			_DeleteBitmaps();
804		}
805	} else
806		printf("BIconButton::_MakeBitmaps() - bitmap is not valid\n");
807	return status;
808}
809
810
811void
812BIconButton::_DeleteBitmaps()
813{
814	delete fNormalBitmap;
815	fNormalBitmap = NULL;
816	delete fDisabledBitmap;
817	fDisabledBitmap = NULL;
818	delete fClickedBitmap;
819	fClickedBitmap = NULL;
820	delete fDisabledClickedBitmap;
821	fDisabledClickedBitmap = NULL;
822}
823
824
825void
826BIconButton::_Update()
827{
828	if (LockLooper()) {
829		Invalidate();
830		UnlockLooper();
831	}
832}
833
834
835void
836BIconButton::_SetFlags(uint32 flags, bool set)
837{
838	if (_HasFlags(flags) != set) {
839		if (set)
840			fButtonState |= flags;
841		else
842			fButtonState &= ~flags;
843
844		if ((flags & STATE_PRESSED) != 0)
845			SetValueNoUpdate(set ? B_CONTROL_ON : B_CONTROL_OFF);
846		_Update();
847	}
848}
849
850
851bool
852BIconButton::_HasFlags(uint32 flags) const
853{
854	return (fButtonState & flags) != 0;
855}
856
857
858//!	This one calls _Update() if needed; BControl::SetTracking() isn't virtual.
859void
860BIconButton::_SetTracking(bool tracking)
861{
862	if (IsTracking() == tracking)
863		return;
864
865	SetTracking(tracking);
866	_Update();
867}
868
869
870}	// namespace BPrivate
871