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