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