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