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