xref: /haiku/src/kits/interface/Button.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
1 /*
2  *	Copyright 2001-2008, Haiku.
3  *  Distributed under the terms of the MIT License.
4  *
5  *	Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Mike Wilber
8  *		Stefano Ceccherini (burton666@libero.it)
9  *		Ivan Tonizza
10  *		Stephan Aßmus, <superstippi@gmx.de>
11  */
12 
13 
14 #include <Button.h>
15 
16 #include <new>
17 
18 #include <Font.h>
19 #include <LayoutUtils.h>
20 #include <String.h>
21 #include <Window.h>
22 
23 #include <binary_compatibility/Interface.h>
24 
25 
26 BButton::BButton(BRect frame, const char* name, const char* label,
27 		BMessage* message, uint32 resizingMode, uint32 flags)
28 	: BControl(frame, name, label, message, resizingMode,
29 			flags | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
30 	fPreferredSize(-1, -1),
31 	fDrawAsDefault(false)
32 {
33 	// Resize to minimum height if needed
34 	font_height fh;
35 	GetFontHeight(&fh);
36 	float minHeight = 12.0f + (float)ceil(fh.ascent + fh.descent);
37 	if (Bounds().Height() < minHeight)
38 		ResizeTo(Bounds().Width(), minHeight);
39 }
40 
41 
42 BButton::BButton(const char* name, const char* label, BMessage* message,
43 		uint32 flags)
44 	: BControl(name, label, message,
45 			flags | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
46 	fPreferredSize(-1, -1),
47 	fDrawAsDefault(false)
48 {
49 }
50 
51 
52 BButton::BButton(const char* label, BMessage* message)
53 	: BControl(NULL, label, message,
54 			B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE),
55 	fPreferredSize(-1, -1),
56 	fDrawAsDefault(false)
57 {
58 }
59 
60 
61 BButton::~BButton()
62 {
63 }
64 
65 
66 BButton::BButton(BMessage* archive)
67 	: BControl(archive),
68 	fPreferredSize(-1, -1)
69 {
70 	if (archive->FindBool("_default", &fDrawAsDefault) != B_OK)
71 		fDrawAsDefault = false;
72 	// NOTE: Default button state will be synchronized with the window
73 	// in AttachedToWindow().
74 }
75 
76 
77 BArchivable*
78 BButton::Instantiate(BMessage* archive)
79 {
80 	if (validate_instantiation(archive, "BButton"))
81 		return new(std::nothrow) BButton(archive);
82 
83 	return NULL;
84 }
85 
86 
87 status_t
88 BButton::Archive(BMessage* archive, bool deep) const
89 {
90 	status_t err = BControl::Archive(archive, deep);
91 
92 	if (err != B_OK)
93 		return err;
94 
95 	if (IsDefault())
96 		err = archive->AddBool("_default", true);
97 
98 	return err;
99 }
100 
101 
102 void
103 BButton::Draw(BRect updateRect)
104 {
105 	font_height fh;
106 	GetFontHeight(&fh);
107 
108 	const BRect bounds = Bounds();
109 	BRect rect = bounds;
110 
111 	const bool enabled = IsEnabled();
112 	const bool pushed = Value() == B_CONTROL_ON;
113 #if 0
114 	// Default indicator
115 	if (IsDefault())
116 		rect = DrawDefault(rect, enabled);
117 	else
118 		rect.InsetBy(1.0f, 1.0f);
119 
120 	BRect fillArea = rect;
121 	fillArea.InsetBy(3.0f, 3.0f);
122 
123 	BString text = Label();
124 
125 #if 1
126 	// Label truncation
127 	BFont font;
128 	GetFont(&font);
129 	font.TruncateString(&text, B_TRUNCATE_END, fillArea.Width());
130 #endif
131 
132 	// Label position
133 	const float stringWidth = StringWidth(text.String());
134 	const float x = (bounds.right - stringWidth) / 2.0f;
135 	const float labelY = bounds.top
136 		+ ((bounds.Height() - fh.ascent - fh.descent) / 2.0f)
137 		+ fh.ascent +  1.0f;
138 	const float focusLineY = labelY + fh.descent;
139 
140 	/* speed trick:
141 	   if the focus changes but the button is not pressed then we can
142 	   redraw only the focus line,
143 	   if the focus changes and the button is pressed invert the internal rect
144 	   this block takes care of all the focus changes
145 	*/
146 	if (IsFocusChanging()) {
147 		if (pushed) {
148 			rect.InsetBy(2.0, 2.0);
149 			InvertRect(rect);
150 		} else
151 			DrawFocusLine(x, focusLineY, stringWidth, IsFocus() && Window()->IsActive());
152 
153 		return;
154 	}
155 
156 	// Colors
157 	const rgb_color panelBgColor = ui_color(B_PANEL_BACKGROUND_COLOR);
158 	const rgb_color buttonBgColor=tint_color(panelBgColor, B_LIGHTEN_1_TINT);
159 	const rgb_color maxLightColor=tint_color(panelBgColor, B_LIGHTEN_MAX_TINT);
160 	const rgb_color maxShadowColor=tint_color(panelBgColor, B_DARKEN_MAX_TINT);
161 	const rgb_color darkBorderColor = tint_color(panelBgColor,
162 		enabled ? B_DARKEN_4_TINT : B_DARKEN_2_TINT);
163 	const rgb_color firstBevelColor = enabled ? tint_color(panelBgColor, B_DARKEN_2_TINT)
164 		: panelBgColor;
165 	const rgb_color cornerColor = IsDefault() ? firstBevelColor : panelBgColor;
166 
167 	// Fill the button area
168 	SetHighColor(buttonBgColor);
169 	FillRect(fillArea);
170 
171 	// external border
172 	SetHighColor(darkBorderColor);
173 	StrokeRect(rect);
174 
175 	BeginLineArray(14);
176 
177 	// Corners
178 	AddLine(rect.LeftTop(), rect.LeftTop(), cornerColor);
179 	AddLine(rect.LeftBottom(), rect.LeftBottom(), cornerColor);
180 	AddLine(rect.RightTop(), rect.RightTop(), cornerColor);
181 	AddLine(rect.RightBottom(), rect.RightBottom(), cornerColor);
182 
183 	rect.InsetBy(1.0f,1.0f);
184 
185 	// Shadow
186 	AddLine(rect.LeftBottom(), rect.RightBottom(), firstBevelColor);
187 	AddLine(rect.RightBottom(), rect.RightTop(), firstBevelColor);
188 	// Light
189 	AddLine(rect.LeftTop(), rect.LeftBottom(),buttonBgColor);
190 	AddLine(rect.LeftTop(), rect.RightTop(), buttonBgColor);
191 
192 	rect.InsetBy(1.0f, 1.0f);
193 
194 	// Shadow
195 	AddLine(rect.LeftBottom(), rect.RightBottom(), panelBgColor);
196 	AddLine(rect.RightBottom(), rect.RightTop(), panelBgColor);
197 	// Light
198 	AddLine(rect.LeftTop(), rect.LeftBottom(),maxLightColor);
199 	AddLine(rect.LeftTop(), rect.RightTop(), maxLightColor);
200 
201 	rect.InsetBy(1.0f,1.0f);
202 
203 	// Light
204 	AddLine(rect.LeftTop(), rect.LeftBottom(),maxLightColor);
205 	AddLine(rect.LeftTop(), rect.RightTop(), maxLightColor);
206 
207 	EndLineArray();
208 
209 	// Invert if clicked
210 	if (enabled && pushed) {
211 		rect.InsetBy(-2.0f, -2.0f);
212 		InvertRect(rect);
213 	}
214 
215 	// Label color
216 	if (enabled) {
217 		if (pushed) {
218 			SetHighColor(maxLightColor);
219 			SetLowColor(maxShadowColor);
220 		} else {
221 			SetHighColor(maxShadowColor);
222 			SetLowColor(tint_color(panelBgColor, B_LIGHTEN_2_TINT));
223 		}
224 	} else {
225 		SetHighColor(tint_color(panelBgColor, B_DISABLED_LABEL_TINT));
226 		SetLowColor(tint_color(panelBgColor, B_LIGHTEN_2_TINT));
227 	}
228 
229 	// Draw the label
230 	DrawString(text.String(), BPoint(x, labelY));
231 
232 	// Focus line
233 	if (enabled && IsFocus() && Window()->IsActive() && !pushed)
234 		DrawFocusLine(x,focusLineY,stringWidth,true);
235 #else
236 	// Default indicator
237 	if (IsDefault())
238 		rect = DrawDefault(rect, enabled);
239 
240 	BRect fillArea = rect;
241 	fillArea.InsetBy(3.0, 3.0);
242 
243 	BString text = Label();
244 
245 #if 1
246 	// Label truncation
247 	BFont font;
248 	GetFont(&font);
249 	font.TruncateString(&text, B_TRUNCATE_END, fillArea.Width() - 4);
250 #endif
251 
252 	// Label position
253 	const float stringWidth = StringWidth(text.String());
254 	const float x = (rect.right - stringWidth) / 2.0;
255 	const float labelY = bounds.top
256 		+ ((bounds.Height() - fh.ascent - fh.descent) / 2.0)
257 		+ fh.ascent + 1.0;
258 	const float focusLineY = labelY + fh.descent;
259 
260 	/* speed trick:
261 	   if the focus changes but the button is not pressed then we can
262 	   redraw only the focus line,
263 	   if the focus changes and the button is pressed invert the internal rect
264 	   this block takes care of all the focus changes
265 	*/
266 	if (IsFocusChanging()) {
267 		if (pushed) {
268 			rect.InsetBy(2.0, 2.0);
269 			InvertRect(rect);
270 		} else {
271 			DrawFocusLine(x, focusLineY, stringWidth, IsFocus()
272 				&& Window()->IsActive());
273 		}
274 
275 		return;
276 	}
277 
278 	// colors
279 	rgb_color panelBgColor = ui_color(B_PANEL_BACKGROUND_COLOR);
280 	rgb_color buttonBgColor = tint_color(panelBgColor, B_LIGHTEN_1_TINT);
281 	rgb_color lightColor;
282 	rgb_color maxLightColor;
283 	rgb_color maxShadowColor = tint_color(panelBgColor, B_DARKEN_MAX_TINT);
284 
285 	rgb_color dark1BorderColor;
286 	rgb_color dark2BorderColor;
287 
288 	rgb_color bevelColor1;
289 	rgb_color bevelColor2;
290 	rgb_color bevelColorRBCorner;
291 
292 	rgb_color borderBevelShadow;
293 	rgb_color borderBevelLight;
294 
295 	if (enabled) {
296 		lightColor = tint_color(panelBgColor, B_LIGHTEN_2_TINT);
297 		maxLightColor = tint_color(panelBgColor, B_LIGHTEN_MAX_TINT);
298 
299 		dark1BorderColor = tint_color(panelBgColor, B_DARKEN_3_TINT);
300 		dark2BorderColor = tint_color(panelBgColor, B_DARKEN_4_TINT);
301 
302 		bevelColor1 = tint_color(panelBgColor, B_DARKEN_2_TINT);
303 		bevelColor2 = panelBgColor;
304 
305 		if (IsDefault()) {
306 			borderBevelShadow = tint_color(dark1BorderColor,
307 				(B_NO_TINT + B_DARKEN_1_TINT) / 2);
308 			borderBevelLight = tint_color(dark1BorderColor, B_LIGHTEN_1_TINT);
309 
310 			borderBevelLight.red = (borderBevelLight.red + panelBgColor.red)
311 				/ 2;
312 			borderBevelLight.green = (borderBevelLight.green
313 				+ panelBgColor.green) / 2;
314 			borderBevelLight.blue = (borderBevelLight.blue
315 				+ panelBgColor.blue) / 2;
316 
317 			dark1BorderColor = tint_color(dark1BorderColor, B_DARKEN_3_TINT);
318 			dark2BorderColor = tint_color(dark1BorderColor, B_DARKEN_4_TINT);
319 
320 			bevelColorRBCorner = borderBevelShadow;
321 		} else {
322 			borderBevelShadow = tint_color(panelBgColor,
323 				(B_NO_TINT + B_DARKEN_1_TINT) / 2);
324 			borderBevelLight = buttonBgColor;
325 
326 			bevelColorRBCorner = dark1BorderColor;
327 		}
328 	} else {
329 		lightColor = tint_color(panelBgColor, B_LIGHTEN_2_TINT);
330 		maxLightColor = tint_color(panelBgColor, B_LIGHTEN_1_TINT);
331 
332 		dark1BorderColor = tint_color(panelBgColor, B_DARKEN_1_TINT);
333 		dark2BorderColor = tint_color(panelBgColor, B_DARKEN_2_TINT);
334 
335 		bevelColor1 = panelBgColor;
336 		bevelColor2 = buttonBgColor;
337 
338 		if (IsDefault()) {
339 			borderBevelShadow = dark1BorderColor;
340 			borderBevelLight = panelBgColor;
341 			dark1BorderColor = tint_color(dark1BorderColor, B_DARKEN_1_TINT);
342 			dark2BorderColor = tint_color(dark1BorderColor, 1.16);
343 
344 		} else {
345 			borderBevelShadow = panelBgColor;
346 			borderBevelLight = panelBgColor;
347 		}
348 
349 		bevelColorRBCorner = tint_color(panelBgColor, 1.08);;
350 	}
351 
352 	// fill the button area
353 	SetHighColor(buttonBgColor);
354 	FillRect(fillArea);
355 
356 	BeginLineArray(22);
357 	// bevel around external border
358 	AddLine(BPoint(rect.left, rect.bottom),
359 			BPoint(rect.left, rect.top), borderBevelShadow);
360 	AddLine(BPoint(rect.left + 1, rect.top),
361 			BPoint(rect.right, rect.top), borderBevelShadow);
362 
363 	AddLine(BPoint(rect.right, rect.top + 1),
364 			BPoint(rect.right, rect.bottom), borderBevelLight);
365 	AddLine(BPoint(rect.left + 1, rect.bottom),
366 			BPoint(rect.right - 1, rect.bottom), borderBevelLight);
367 
368 	rect.InsetBy(1.0, 1.0);
369 
370 	// external border
371 	AddLine(BPoint(rect.left, rect.bottom),
372 			BPoint(rect.left, rect.top), dark1BorderColor);
373 	AddLine(BPoint(rect.left + 1, rect.top),
374 			BPoint(rect.right, rect.top), dark1BorderColor);
375 	AddLine(BPoint(rect.right, rect.top + 1),
376 			BPoint(rect.right, rect.bottom), dark2BorderColor);
377 	AddLine(BPoint(rect.right - 1, rect.bottom),
378 			BPoint(rect.left + 1, rect.bottom), dark2BorderColor);
379 
380 	rect.InsetBy(1.0, 1.0);
381 
382 	// Light
383 	AddLine(BPoint(rect.left, rect.top),
384 			BPoint(rect.left, rect.top), buttonBgColor);
385 	AddLine(BPoint(rect.left, rect.top + 1),
386 			BPoint(rect.left, rect.bottom - 1), lightColor);
387 	AddLine(BPoint(rect.left, rect.bottom),
388 			BPoint(rect.left, rect.bottom), bevelColor2);
389 	AddLine(BPoint(rect.left + 1, rect.top),
390 			BPoint(rect.right - 1, rect.top), lightColor);
391 	AddLine(BPoint(rect.right, rect.top),
392 			BPoint(rect.right, rect.top), bevelColor2);
393 	// Shadow
394 	AddLine(BPoint(rect.left + 1, rect.bottom),
395 			BPoint(rect.right - 1, rect.bottom), bevelColor1);
396 	AddLine(BPoint(rect.right, rect.bottom),
397 			BPoint(rect.right, rect.bottom), bevelColorRBCorner);
398 	AddLine(BPoint(rect.right, rect.bottom - 1),
399 			BPoint(rect.right, rect.top + 1), bevelColor1);
400 
401 	rect.InsetBy(1.0, 1.0);
402 
403 	// Light
404 	AddLine(BPoint(rect.left, rect.top),
405 			BPoint(rect.left, rect.bottom - 1), maxLightColor);
406 	AddLine(BPoint(rect.left, rect.bottom),
407 			BPoint(rect.left, rect.bottom), buttonBgColor);
408 	AddLine(BPoint(rect.left + 1, rect.top),
409 			BPoint(rect.right - 1, rect.top), maxLightColor);
410 	AddLine(BPoint(rect.right, rect.top),
411 			BPoint(rect.right, rect.top), buttonBgColor);
412 	// Shadow
413 	AddLine(BPoint(rect.left + 1, rect.bottom),
414 			BPoint(rect.right, rect.bottom), bevelColor2);
415 	AddLine(BPoint(rect.right, rect.bottom - 1),
416 			BPoint(rect.right, rect.top + 1), bevelColor2);
417 
418 	rect.InsetBy(1.0,1.0);
419 
420 	EndLineArray();
421 
422 	// Invert if clicked
423 	if (enabled && pushed) {
424 		rect.InsetBy(-2.0, -2.0);
425 		InvertRect(rect);
426 	}
427 
428 	// Label color
429 	if (enabled) {
430 		if (pushed) {
431 			SetHighColor(maxLightColor);
432 			SetLowColor(255 - buttonBgColor.red,
433 						255 - buttonBgColor.green,
434 						255 - buttonBgColor.blue);
435 		} else {
436 			SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
437 			SetLowColor(buttonBgColor);
438 		}
439 	} else {
440 		SetHighColor(tint_color(panelBgColor, B_DISABLED_LABEL_TINT));
441 		SetLowColor(buttonBgColor);
442 	}
443 
444 	// Draw the label
445 	DrawString(text.String(), BPoint(x, labelY));
446 
447 	// Focus line
448 	if (enabled && IsFocus() && Window()->IsActive() && !pushed)
449 		DrawFocusLine(x, focusLineY, stringWidth, true);
450 #endif
451 }
452 
453 
454 void
455 BButton::MouseDown(BPoint point)
456 {
457 	if (!IsEnabled())
458 		return;
459 
460 	SetValue(B_CONTROL_ON);
461 
462 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
463  		SetTracking(true);
464  		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
465  	} else {
466 		BRect bounds = Bounds();
467 		uint32 buttons;
468 
469 		do {
470 			Window()->UpdateIfNeeded();
471 			snooze(40000);
472 
473 			GetMouse(&point, &buttons, true);
474 
475  			bool inside = bounds.Contains(point);
476 
477 			if ((Value() == B_CONTROL_ON) != inside)
478 				SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
479 		} while (buttons != 0);
480 
481 		if (Value() == B_CONTROL_ON)
482 			Invoke();
483 	}
484 }
485 
486 
487 void
488 BButton::AttachedToWindow()
489 {
490 	BControl::AttachedToWindow();
491 		// low color will now be the parents view color
492 
493 	if (IsDefault())
494 		Window()->SetDefaultButton(this);
495 
496 	SetViewColor(B_TRANSPARENT_COLOR);
497 }
498 
499 
500 void
501 BButton::KeyDown(const char *bytes, int32 numBytes)
502 {
503 	if (*bytes == B_ENTER || *bytes == B_SPACE) {
504 		if (!IsEnabled())
505 			return;
506 
507 		SetValue(B_CONTROL_ON);
508 
509 		// make sure the user saw that
510 		Window()->UpdateIfNeeded();
511 		snooze(25000);
512 
513 		Invoke();
514 
515 	} else
516 		BControl::KeyDown(bytes, numBytes);
517 }
518 
519 
520 void
521 BButton::MakeDefault(bool flag)
522 {
523 	BButton *oldDefault = NULL;
524 	BWindow *window = Window();
525 
526 	if (window)
527 		oldDefault = window->DefaultButton();
528 
529 	if (flag) {
530 		if (fDrawAsDefault && oldDefault == this)
531 			return;
532 
533 		if (!fDrawAsDefault) {
534 			fDrawAsDefault = true;
535 
536 			ResizeBy(6.0f, 6.0f);
537 			MoveBy(-3.0f, -3.0f);
538 		}
539 
540 		if (window && oldDefault != this)
541 			window->SetDefaultButton(this);
542 	} else {
543 		if (!fDrawAsDefault)
544 			return;
545 
546 		fDrawAsDefault = false;
547 
548 		ResizeBy(-6.0f, -6.0f);
549 		MoveBy(3.0f, 3.0f);
550 
551 		if (window && oldDefault == this)
552 			window->SetDefaultButton(NULL);
553 	}
554 }
555 
556 
557 void
558 BButton::SetLabel(const char *string)
559 {
560 	BControl::SetLabel(string);
561 }
562 
563 
564 bool
565 BButton::IsDefault() const
566 {
567 	return fDrawAsDefault;
568 }
569 
570 
571 void
572 BButton::MessageReceived(BMessage *message)
573 {
574 	BControl::MessageReceived(message);
575 }
576 
577 
578 void
579 BButton::WindowActivated(bool active)
580 {
581 	BControl::WindowActivated(active);
582 }
583 
584 
585 void
586 BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
587 {
588 	if (!IsTracking())
589 		return;
590 
591 	bool inside = Bounds().Contains(point);
592 
593 	if ((Value() == B_CONTROL_ON) != inside)
594 		SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
595 }
596 
597 
598 void
599 BButton::MouseUp(BPoint point)
600 {
601 	if (!IsTracking())
602 		return;
603 
604 	if (Bounds().Contains(point))
605 		Invoke();
606 
607 	SetTracking(false);
608 }
609 
610 
611 void
612 BButton::DetachedFromWindow()
613 {
614 	BControl::DetachedFromWindow();
615 }
616 
617 
618 void
619 BButton::SetValue(int32 value)
620 {
621 	if (value != Value())
622 		BControl::SetValue(value);
623 }
624 
625 
626 void
627 BButton::GetPreferredSize(float *_width, float *_height)
628 {
629 	_ValidatePreferredSize();
630 
631 	if (_width)
632 		*_width = fPreferredSize.width;
633 
634 	if (_height)
635 		*_height = fPreferredSize.height;
636 }
637 
638 
639 void
640 BButton::ResizeToPreferred()
641 {
642 	 BControl::ResizeToPreferred();
643 }
644 
645 
646 status_t
647 BButton::Invoke(BMessage *message)
648 {
649 	Sync();
650 	snooze(50000);
651 
652 	status_t err = BControl::Invoke(message);
653 
654 	SetValue(B_CONTROL_OFF);
655 
656 	return err;
657 }
658 
659 
660 void
661 BButton::FrameMoved(BPoint newLocation)
662 {
663 	BControl::FrameMoved(newLocation);
664 }
665 
666 
667 void
668 BButton::FrameResized(float width, float height)
669 {
670 	BControl::FrameResized(width, height);
671 }
672 
673 
674 void
675 BButton::MakeFocus(bool focused)
676 {
677 	BControl::MakeFocus(focused);
678 }
679 
680 
681 void
682 BButton::AllAttached()
683 {
684 	BControl::AllAttached();
685 }
686 
687 
688 void
689 BButton::AllDetached()
690 {
691 	BControl::AllDetached();
692 }
693 
694 
695 BHandler *
696 BButton::ResolveSpecifier(BMessage *message, int32 index,
697 									BMessage *specifier, int32 what,
698 									const char *property)
699 {
700 	return BControl::ResolveSpecifier(message, index, specifier, what, property);
701 }
702 
703 
704 status_t
705 BButton::GetSupportedSuites(BMessage *message)
706 {
707 	return BControl::GetSupportedSuites(message);
708 }
709 
710 
711 status_t
712 BButton::Perform(perform_code code, void* _data)
713 {
714 	switch (code) {
715 		case PERFORM_CODE_MIN_SIZE:
716 			((perform_data_min_size*)_data)->return_value
717 				= BButton::MinSize();
718 			return B_OK;
719 		case PERFORM_CODE_MAX_SIZE:
720 			((perform_data_max_size*)_data)->return_value
721 				= BButton::MaxSize();
722 			return B_OK;
723 		case PERFORM_CODE_PREFERRED_SIZE:
724 			((perform_data_preferred_size*)_data)->return_value
725 				= BButton::PreferredSize();
726 			return B_OK;
727 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
728 			((perform_data_layout_alignment*)_data)->return_value
729 				= BButton::LayoutAlignment();
730 			return B_OK;
731 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
732 			((perform_data_has_height_for_width*)_data)->return_value
733 				= BButton::HasHeightForWidth();
734 			return B_OK;
735 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
736 		{
737 			perform_data_get_height_for_width* data
738 				= (perform_data_get_height_for_width*)_data;
739 			BButton::GetHeightForWidth(data->width, &data->min, &data->max,
740 				&data->preferred);
741 			return B_OK;
742 }
743 		case PERFORM_CODE_SET_LAYOUT:
744 		{
745 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
746 			BButton::SetLayout(data->layout);
747 			return B_OK;
748 		}
749 		case PERFORM_CODE_INVALIDATE_LAYOUT:
750 		{
751 			perform_data_invalidate_layout* data
752 				= (perform_data_invalidate_layout*)_data;
753 			BButton::InvalidateLayout(data->descendants);
754 			return B_OK;
755 		}
756 		case PERFORM_CODE_DO_LAYOUT:
757 		{
758 			BButton::DoLayout();
759 			return B_OK;
760 		}
761 	}
762 
763 	return BControl::Perform(code, _data);
764 }
765 
766 
767 void
768 BButton::InvalidateLayout(bool descendants)
769 {
770 	// invalidate cached preferred size
771 	fPreferredSize.Set(-1, -1);
772 
773 	BControl::InvalidateLayout(descendants);
774 }
775 
776 
777 BSize
778 BButton::MinSize()
779 {
780 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
781 		_ValidatePreferredSize());
782 }
783 
784 
785 BSize
786 BButton::MaxSize()
787 {
788 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
789 		_ValidatePreferredSize());
790 }
791 
792 
793 BSize
794 BButton::PreferredSize()
795 {
796 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
797 		_ValidatePreferredSize());
798 }
799 
800 
801 void BButton::_ReservedButton1() {}
802 void BButton::_ReservedButton2() {}
803 void BButton::_ReservedButton3() {}
804 
805 
806 BButton &
807 BButton::operator=(const BButton &)
808 {
809 	return *this;
810 }
811 
812 
813 BRect
814 BButton::DrawDefault(BRect bounds, bool enabled)
815 {
816 #if 0
817 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
818 	lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
819 	darken1 = tint_color(no_tint, B_DARKEN_1_TINT);
820 
821 	rgb_color borderColor;
822 	if (enabled)
823 		borderColor = tint_color(no_tint, B_DARKEN_4_TINT);
824 	else
825 		borderColor = darken1;
826 
827 	// Dark border
828 	BeginLineArray(4);
829 	AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
830 		BPoint(bounds.left, bounds.top + 1.0f), borderColor);
831 	AddLine(BPoint(bounds.left + 1.0f, bounds.top),
832 		BPoint(bounds.right - 1.0f, bounds.top), borderColor);
833 	AddLine(BPoint(bounds.right, bounds.top + 1.0f),
834 		BPoint(bounds.right, bounds.bottom - 1.0f), borderColor);
835 	AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
836 		BPoint(bounds.right - 1.0f, bounds.bottom), borderColor);
837 	EndLineArray();
838 
839 	if (enabled) {
840 		// Bevel
841 		bounds.InsetBy(1.0f, 1.0f);
842 		SetHighColor(darken1);
843 		StrokeRect(bounds);
844 	}
845 
846 	bounds.InsetBy(1.0f, 1.0f);
847 
848 	// Filling
849 	float inset = enabled? 2.0f : 3.0f;
850 	SetHighColor(lighten1);
851 
852 	FillRect(BRect(bounds.left, bounds.top,
853 		bounds.right, bounds.top+inset-1.0f));
854 	FillRect(BRect(bounds.left, bounds.bottom-inset+1.0f,
855 		bounds.right, bounds.bottom));
856 	FillRect(BRect(bounds.left, bounds.top+inset-1.0f,
857 		bounds.left+inset-1.0f, bounds.bottom-inset+1.0f));
858 	FillRect(BRect(bounds.right-inset+1.0f, bounds.top+inset-1.0f,
859 		bounds.right, bounds.bottom-inset+1.0f));
860 
861 	bounds.InsetBy(inset,inset);
862 
863 	return bounds;
864 #else
865 	rgb_color low = LowColor();
866 	rgb_color focusColor = tint_color(low, enabled ? (B_DARKEN_1_TINT + B_DARKEN_2_TINT) / 2
867 												   : (B_NO_TINT + B_DARKEN_1_TINT) / 2);
868 
869 	SetHighColor(focusColor);
870 
871 	StrokeRect(bounds, B_SOLID_LOW);
872 	bounds.InsetBy(1.0, 1.0);
873 	StrokeRect(bounds);
874 	bounds.InsetBy(1.0, 1.0);
875 	StrokeRect(bounds);
876 	bounds.InsetBy(1.0, 1.0);
877 
878 	return bounds;
879 #endif
880 }
881 
882 
883 status_t
884 BButton::Execute()
885 {
886 	if (!IsEnabled())
887 		return B_ERROR;
888 
889 	SetValue(B_CONTROL_ON);
890 	return Invoke();
891 }
892 
893 
894 void
895 BButton::DrawFocusLine(float x, float y, float width, bool visible)
896 {
897 	if (visible)
898 		SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
899 	else {
900 		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
901 			B_LIGHTEN_1_TINT));
902 	}
903 
904 	// Blue Line
905 	StrokeLine(BPoint(x, y), BPoint(x + width, y));
906 
907 	if (visible)
908 		SetHighColor(255, 255, 255);
909 	// White Line
910 	StrokeLine(BPoint(x, y + 1.0f), BPoint(x + width, y + 1.0f));
911 }
912 
913 
914 BSize
915 BButton::_ValidatePreferredSize()
916 {
917 	if (fPreferredSize.width < 0) {
918 		// width
919 		float width = 20.0f + (float)ceil(StringWidth(Label()));
920 		if (width < 75.0f)
921 			width = 75.0f;
922 
923 		if (fDrawAsDefault)
924 			width += 6.0f;
925 
926 		fPreferredSize.width = width;
927 
928 		// height
929 		font_height fontHeight;
930 		GetFontHeight(&fontHeight);
931 
932 		fPreferredSize.height
933 			= ceilf((fontHeight.ascent + fontHeight.descent) * 1.8)
934 				+ (fDrawAsDefault ? 6.0f : 0);
935 	}
936 
937 	return fPreferredSize;
938 }
939 
940