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