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