xref: /haiku/src/kits/interface/StatusBar.cpp (revision 23d878482ed22e55dad6d1fca1df7bea42eb157c)
1 /*
2  * Copyright 2001-2008, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Stephan Aßmus <superstippi@gmx.de>
9  */
10 
11 /*! BStatusBar displays a "percentage-of-completion" gauge. */
12 #include <StatusBar.h>
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include <ControlLook.h>
19 #include <Layout.h>
20 #include <LayoutUtils.h>
21 #include <Message.h>
22 #include <Region.h>
23 
24 #include <binary_compatibility/Interface.h>
25 
26 
27 static const rgb_color kDefaultBarColor = {50, 150, 255, 255};
28 
29 
30 BStatusBar::BStatusBar(BRect frame, const char *name, const char *label,
31 		const char *trailingLabel)
32 	:
33 	BView(frame, name, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW),
34 	fLabel(label),
35 	fTrailingLabel(trailingLabel)
36 {
37 	_InitObject();
38 }
39 
40 
41 BStatusBar::BStatusBar(const char *name, const char *label,
42 		const char *trailingLabel)
43 	:
44 	BView(BRect(0, 0, -1, -1), name, B_FOLLOW_LEFT | B_FOLLOW_TOP,
45 		B_WILL_DRAW | B_SUPPORTS_LAYOUT),
46 	fLabel(label),
47 	fTrailingLabel(trailingLabel)
48 {
49 	_InitObject();
50 }
51 
52 
53 BStatusBar::BStatusBar(BMessage *archive)
54 	:
55 	BView(archive)
56 {
57 	_InitObject();
58 
59 	archive->FindString("_label", &fLabel);
60 	archive->FindString("_tlabel", &fTrailingLabel);
61 
62 	archive->FindString("_text", &fText);
63 	archive->FindString("_ttext", &fTrailingText);
64 
65 	float floatValue;
66 	if (archive->FindFloat("_high", &floatValue) == B_OK) {
67 		fBarHeight = floatValue;
68 		fCustomBarHeight = true;
69 	}
70 
71 	int32 color;
72 	if (archive->FindInt32("_bcolor", (int32 *)&color) == B_OK)
73 		fBarColor = *(rgb_color *)&color;
74 
75 	if (archive->FindFloat("_val", &floatValue) == B_OK)
76 		fCurrent = floatValue;
77 	if (archive->FindFloat("_max", &floatValue) == B_OK)
78 		fMax = floatValue;
79 }
80 
81 
82 BStatusBar::~BStatusBar()
83 {
84 }
85 
86 
87 BArchivable *
88 BStatusBar::Instantiate(BMessage *archive)
89 {
90 	if (validate_instantiation(archive, "BStatusBar"))
91 		return new BStatusBar(archive);
92 
93 	return NULL;
94 }
95 
96 
97 status_t
98 BStatusBar::Archive(BMessage *archive, bool deep) const
99 {
100 	status_t err = BView::Archive(archive, deep);
101 	if (err < B_OK)
102 		return err;
103 
104 	if (fCustomBarHeight)
105 		err = archive->AddFloat("_high", fBarHeight);
106 
107 	if (err == B_OK && fBarColor != kDefaultBarColor)
108 		err = archive->AddInt32("_bcolor", (const uint32 &)fBarColor);
109 
110 	if (err == B_OK && fCurrent != 0)
111 		err = archive->AddFloat("_val", fCurrent);
112 	if (err == B_OK && fMax != 100 )
113 		err = archive->AddFloat("_max", fMax);
114 
115 	if (err == B_OK && fText.Length())
116 		err = archive->AddString("_text", fText);
117 	if (err == B_OK && fTrailingText.Length())
118 		err = archive->AddString("_ttext", fTrailingText);
119 
120 	if (err == B_OK && fLabel.Length())
121 		err = archive->AddString("_label", fLabel);
122 	if (err == B_OK && fTrailingLabel.Length())
123 		err = archive->AddString ("_tlabel", fTrailingLabel);
124 
125 	return err;
126 }
127 
128 
129 // #pragma mark -
130 
131 
132 void
133 BStatusBar::AttachedToWindow()
134 {
135 	// resize so that the height fits
136 	float width, height;
137 	GetPreferredSize(&width, &height);
138 	ResizeTo(Bounds().Width(), height);
139 
140 	SetViewColor(B_TRANSPARENT_COLOR);
141 	rgb_color lowColor = B_TRANSPARENT_COLOR;
142 
143 	BView* parent = Parent();
144 	if (parent != NULL)
145 		lowColor = parent->ViewColor();
146 
147 	if (lowColor == B_TRANSPARENT_COLOR)
148 		lowColor = ui_color(B_PANEL_BACKGROUND_COLOR);
149 
150 	SetLowColor(lowColor);
151 
152 	fTextDivider = Bounds().Width();
153 }
154 
155 
156 void
157 BStatusBar::DetachedFromWindow()
158 {
159 	BView::DetachedFromWindow();
160 }
161 
162 
163 void
164 BStatusBar::AllAttached()
165 {
166 	BView::AllAttached();
167 }
168 
169 
170 void
171 BStatusBar::AllDetached()
172 {
173 	BView::AllDetached();
174 }
175 
176 
177 // #pragma mark -
178 
179 
180 void
181 BStatusBar::WindowActivated(bool state)
182 {
183 	BView::WindowActivated(state);
184 }
185 
186 
187 void
188 BStatusBar::MakeFocus(bool state)
189 {
190 	BView::MakeFocus(state);
191 }
192 
193 
194 // #pragma mark -
195 
196 
197 void
198 BStatusBar::GetPreferredSize(float* _width, float* _height)
199 {
200 	if (_width) {
201 		// AttachedToWindow() might not have been called yet
202 		*_width = ceilf(StringWidth(fLabel.String()))
203 			+ ceilf(StringWidth(fTrailingLabel.String()))
204 			+ ceilf(StringWidth(fText.String()))
205 			+ ceilf(StringWidth(fTrailingText.String()))
206 			+ 5;
207 	}
208 
209 	if (_height) {
210 		float labelHeight = 0;
211 		if (_HasText()) {
212 			font_height fontHeight;
213 			GetFontHeight(&fontHeight);
214 			labelHeight = ceilf(fontHeight.ascent + fontHeight.descent) + 6;
215 		}
216 
217 		*_height = labelHeight + BarHeight();
218 	}
219 }
220 
221 
222 BSize
223 BStatusBar::MinSize()
224 {
225 	float width, height;
226 	GetPreferredSize(&width, &height);
227 
228 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), BSize(width, height));
229 }
230 
231 
232 BSize
233 BStatusBar::MaxSize()
234 {
235 	float width, height;
236 	GetPreferredSize(&width, &height);
237 
238 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
239 			BSize(B_SIZE_UNLIMITED, height));
240 }
241 
242 
243 BSize
244 BStatusBar::PreferredSize()
245 {
246 	float width, height;
247 	GetPreferredSize(&width, &height);
248 
249 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), BSize(width, height));
250 }
251 
252 
253 void
254 BStatusBar::ResizeToPreferred()
255 {
256 	BView::ResizeToPreferred();
257 }
258 
259 
260 void
261 BStatusBar::FrameMoved(BPoint newPosition)
262 {
263 	BView::FrameMoved(newPosition);
264 }
265 
266 
267 void
268 BStatusBar::FrameResized(float newWidth, float newHeight)
269 {
270 	BView::FrameResized(newWidth, newHeight);
271 	Invalidate();
272 }
273 
274 
275 // #pragma mark -
276 
277 
278 void
279 BStatusBar::Draw(BRect updateRect)
280 {
281 	rgb_color backgroundColor = LowColor();
282 
283 	font_height fontHeight;
284 	GetFontHeight(&fontHeight);
285 	BRect barFrame = _BarFrame(&fontHeight);
286 	BRect outerFrame = barFrame.InsetByCopy(-2, -2);
287 
288 	BRegion background(updateRect);
289 	background.Exclude(outerFrame);
290 	FillRegion(&background, B_SOLID_LOW);
291 
292 	// Draw labels/texts
293 
294 	BRect rect = outerFrame;
295 	rect.top = 0;
296 	rect.bottom = outerFrame.top - 1;
297 
298 	if (updateRect.Intersects(rect)) {
299 		// update labels
300 		BString leftText;
301 		leftText << fLabel << fText;
302 
303 		BString rightText;
304 		rightText << fTrailingText << fTrailingLabel;
305 
306 		float baseLine = ceilf(fontHeight.ascent) + 1;
307 		fTextDivider = rect.right;
308 
309 		BFont font;
310 		GetFont(&font);
311 
312 		if (rightText.Length()) {
313 			font.TruncateString(&rightText, B_TRUNCATE_BEGINNING, rect.Width());
314 			fTextDivider -= StringWidth(rightText.String());
315 		}
316 
317 		if (leftText.Length()) {
318 			float width = max_c(0.0, fTextDivider - rect.left);
319 			font.TruncateString(&leftText, B_TRUNCATE_END, width);
320 		}
321 
322 		SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
323 
324 		if (leftText.Length())
325 			DrawString(leftText.String(), BPoint(rect.left, baseLine));
326 
327 		if (rightText.Length())
328 			DrawString(rightText.String(), BPoint(fTextDivider, baseLine));
329 
330 	}
331 
332 	// Draw bar
333 
334 	if (!updateRect.Intersects(outerFrame))
335 		return;
336 
337 	rect = outerFrame;
338 
339 	if (be_control_look != NULL) {
340 		be_control_look->DrawStatusBar(this, rect, updateRect,
341 			backgroundColor, fBarColor, _BarPosition(barFrame));
342 		return;
343 	}
344 
345 	// First bevel
346 	SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_1_TINT));
347 	StrokeLine(rect.LeftBottom(), rect.LeftTop());
348 	StrokeLine(rect.RightTop());
349 
350 	SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_2_TINT));
351 	StrokeLine(BPoint(rect.left + 1, rect.bottom), rect.RightBottom());
352 	StrokeLine(BPoint(rect.right, rect.top + 1));
353 
354 	rect.InsetBy(1, 1);
355 
356 	// Second bevel
357 	SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_4_TINT));
358 	StrokeLine(rect.LeftBottom(), rect.LeftTop());
359 	StrokeLine(rect.RightTop());
360 
361 	SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
362 	StrokeLine(BPoint(rect.left + 1, rect.bottom), rect.RightBottom());
363 	StrokeLine(BPoint(rect.right, rect.top + 1));
364 
365 	rect = barFrame;
366 	rect.right = _BarPosition(barFrame);
367 
368 	// draw bar itself
369 
370 	if (rect.right >= rect.left) {
371 		// Bevel
372 		SetHighColor(tint_color(fBarColor, B_LIGHTEN_2_TINT));
373 		StrokeLine(rect.LeftBottom(), rect.LeftTop());
374 		StrokeLine(rect.RightTop());
375 
376 		SetHighColor(tint_color(fBarColor, B_DARKEN_2_TINT));
377 		StrokeLine(BPoint(rect.left + 1, rect.bottom), rect.RightBottom());
378 		StrokeLine(BPoint(rect.right, rect.top + 1));
379 
380 		// filling
381 		SetHighColor(fBarColor);
382 		FillRect(rect.InsetByCopy(1, 1));
383 	}
384 
385 	if (rect.right < barFrame.right) {
386 		// empty space
387 		rect.left = rect.right + 1;
388 		rect.right = barFrame.right;
389 		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_MAX_TINT));
390 		FillRect(rect);
391 	}
392 }
393 
394 
395 void
396 BStatusBar::MessageReceived(BMessage *message)
397 {
398 	switch(message->what) {
399 		case B_UPDATE_STATUS_BAR:
400 		{
401 			float delta;
402 			const char *text = NULL, *trailing_text = NULL;
403 
404 			message->FindFloat("delta", &delta);
405 			message->FindString("text", &text);
406 			message->FindString("trailing_text", &trailing_text);
407 
408 			Update(delta, text, trailing_text);
409 
410 			break;
411 		}
412 
413 		case B_RESET_STATUS_BAR:
414 		{
415 			const char *label = NULL, *trailing_label = NULL;
416 
417 			message->FindString("label", &label);
418 			message->FindString("trailing_label", &trailing_label);
419 
420 			Reset(label, trailing_label);
421 
422 			break;
423 		}
424 
425 		default:
426 			BView::MessageReceived(message);
427 			break;
428 	}
429 }
430 
431 
432 void
433 BStatusBar::MouseDown(BPoint point)
434 {
435 	BView::MouseDown(point);
436 }
437 
438 
439 void
440 BStatusBar::MouseUp(BPoint point)
441 {
442 	BView::MouseUp(point);
443 }
444 
445 
446 void
447 BStatusBar::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
448 {
449 	BView::MouseMoved(point, transit, message);
450 }
451 
452 
453 // #pragma mark -
454 
455 
456 void
457 BStatusBar::SetBarColor(rgb_color color)
458 {
459 	fBarColor = color;
460 
461 	Invalidate();
462 }
463 
464 
465 void
466 BStatusBar::SetBarHeight(float barHeight)
467 {
468 	float oldHeight = BarHeight();
469 
470 	fCustomBarHeight = true;
471 	fBarHeight = barHeight;
472 
473 	if (barHeight == oldHeight)
474 		return;
475 
476 	// resize so that the height fits
477 	if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
478 		InvalidateLayout();
479 	else {
480 		float width, height;
481 		GetPreferredSize(&width, &height);
482 		ResizeTo(Bounds().Width(), height);
483 	}
484 }
485 
486 
487 void
488 BStatusBar::SetText(const char* string)
489 {
490 	_SetTextData(fText, string, fLabel, false);
491 }
492 
493 
494 void
495 BStatusBar::SetTrailingText(const char* string)
496 {
497 	_SetTextData(fTrailingText, string, fTrailingLabel, true);
498 }
499 
500 
501 void
502 BStatusBar::SetMaxValue(float max)
503 {
504 	// R5 and/or Zeta's SetMaxValue does not trigger an invalidate here.
505 	// this is probably not ideal behavior, but it does break apps in some cases
506 	// as observed with SpaceMonitor.
507 	// TODO: revisit this when we break binary compatibility
508 	fMax = max;
509 }
510 
511 
512 void
513 BStatusBar::Update(float delta, const char* text, const char* trailingText)
514 {
515 	// If any of these are NULL, the existing text remains (BeBook)
516 	if (text == NULL)
517 		text = fText.String();
518 	if (trailingText == NULL)
519 		trailingText = fTrailingText.String();
520 	BStatusBar::SetTo(fCurrent + delta, text, trailingText);
521 }
522 
523 
524 void
525 BStatusBar::Reset(const char *label, const char *trailingLabel)
526 {
527 	// Reset replaces the label and trailing label with copies of the
528 	// strings passed as arguments. If either argument is NULL, the
529 	// label or trailing label will be deleted and erased.
530 	fLabel = label ? label : "";
531 	fTrailingLabel = trailingLabel ? trailingLabel : "";
532 
533 	// Reset deletes and erases any text or trailing text
534 	fText = "";
535 	fTrailingText = "";
536 
537 	fCurrent = 0;
538 	fMax = 100;
539 
540 	Invalidate();
541 }
542 
543 
544 void
545 BStatusBar::SetTo(float value, const char* text, const char* trailingText)
546 {
547 	SetText(text);
548 	SetTrailingText(trailingText);
549 
550 	if (value > fMax)
551 		value = fMax;
552 	else if (value < 0)
553 		value = 0;
554 	if (value == fCurrent)
555 		return;
556 
557 	BRect barFrame = _BarFrame();
558 	float oldPosition = _BarPosition(barFrame);
559 
560 	fCurrent = value;
561 
562 	float newPosition = _BarPosition(barFrame);
563 	if (oldPosition == newPosition)
564 		return;
565 
566 	// update only the part of the status bar with actual changes
567 	BRect update = barFrame;
568 	if (oldPosition < newPosition) {
569 		update.left = floorf(max_c(oldPosition - 1, update.left));
570 		update.right = ceilf(newPosition);
571 	} else {
572 		update.left = floorf(max_c(newPosition - 1, update.left));
573 		update.right = ceilf(oldPosition);
574 	}
575 
576 	// TODO: Ask the BControlLook in the first place about dirty rect.
577 	if (be_control_look)
578 		update.InsetBy(-1, -1);
579 
580 	Invalidate(update);
581 }
582 
583 
584 float
585 BStatusBar::CurrentValue() const
586 {
587 	return fCurrent;
588 }
589 
590 
591 float
592 BStatusBar::MaxValue() const
593 {
594 	return fMax;
595 }
596 
597 
598 rgb_color
599 BStatusBar::BarColor() const
600 {
601 	return fBarColor;
602 }
603 
604 
605 float
606 BStatusBar::BarHeight() const
607 {
608 	if (!fCustomBarHeight && fBarHeight == -1) {
609 		// the default bar height is as height as the label
610 		font_height fontHeight;
611 		GetFontHeight(&fontHeight);
612 		const_cast<BStatusBar *>(this)->fBarHeight = fontHeight.ascent
613 			+ fontHeight.descent + 5;
614 	}
615 
616 	return ceilf(fBarHeight);
617 }
618 
619 
620 const char *
621 BStatusBar::Text() const
622 {
623 	return fText.String();
624 }
625 
626 
627 const char *
628 BStatusBar::TrailingText() const
629 {
630 	return fTrailingText.String();
631 }
632 
633 
634 const char *
635 BStatusBar::Label() const
636 {
637 	return fLabel.String();
638 }
639 
640 
641 const char *
642 BStatusBar::TrailingLabel() const
643 {
644 	return fTrailingLabel.String();
645 }
646 
647 
648 // #pragma mark -
649 
650 
651 BHandler *
652 BStatusBar::ResolveSpecifier(BMessage* message, int32 index,
653 	BMessage* specifier, int32 what, const char *property)
654 {
655 	return BView::ResolveSpecifier(message, index, specifier, what, property);
656 }
657 
658 
659 status_t
660 BStatusBar::GetSupportedSuites(BMessage* data)
661 {
662 	return BView::GetSupportedSuites(data);
663 }
664 
665 
666 status_t
667 BStatusBar::Perform(perform_code code, void* _data)
668 {
669 	switch (code) {
670 		case PERFORM_CODE_MIN_SIZE:
671 			((perform_data_min_size*)_data)->return_value
672 				= BStatusBar::MinSize();
673 			return B_OK;
674 		case PERFORM_CODE_MAX_SIZE:
675 			((perform_data_max_size*)_data)->return_value
676 				= BStatusBar::MaxSize();
677 			return B_OK;
678 		case PERFORM_CODE_PREFERRED_SIZE:
679 			((perform_data_preferred_size*)_data)->return_value
680 				= BStatusBar::PreferredSize();
681 			return B_OK;
682 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
683 			((perform_data_layout_alignment*)_data)->return_value
684 				= BStatusBar::LayoutAlignment();
685 			return B_OK;
686 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
687 			((perform_data_has_height_for_width*)_data)->return_value
688 				= BStatusBar::HasHeightForWidth();
689 			return B_OK;
690 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
691 		{
692 			perform_data_get_height_for_width* data
693 				= (perform_data_get_height_for_width*)_data;
694 			BStatusBar::GetHeightForWidth(data->width, &data->min, &data->max,
695 				&data->preferred);
696 			return B_OK;
697 		}
698 		case PERFORM_CODE_SET_LAYOUT:
699 		{
700 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
701 			BStatusBar::SetLayout(data->layout);
702 			return B_OK;
703 		}
704 		case PERFORM_CODE_LAYOUT_INVALIDATED:
705 		{
706 			perform_data_layout_invalidated* data
707 				= (perform_data_layout_invalidated*)_data;
708 			BStatusBar::LayoutInvalidated(data->descendants);
709 			return B_OK;
710 		}
711 		case PERFORM_CODE_DO_LAYOUT:
712 		{
713 			BStatusBar::DoLayout();
714 			return B_OK;
715 		}
716 	}
717 
718 	return BView::Perform(code, _data);
719 }
720 
721 
722 // #pragma mark -
723 
724 
725 extern "C" void
726 _ReservedStatusBar1__10BStatusBar(BStatusBar* self, float value,
727 	const char* text, const char* trailingText)
728 {
729 	self->BStatusBar::SetTo(value, text, trailingText);
730 }
731 
732 
733 void BStatusBar::_ReservedStatusBar2() {}
734 void BStatusBar::_ReservedStatusBar3() {}
735 void BStatusBar::_ReservedStatusBar4() {}
736 
737 
738 BStatusBar &
739 BStatusBar::operator=(const BStatusBar& other)
740 {
741 	return *this;
742 }
743 
744 
745 // #pragma mark -
746 
747 
748 void
749 BStatusBar::_InitObject()
750 {
751 	fMax = 100.0;
752 	fCurrent = 0.0;
753 
754 	fBarHeight = -1.0;
755 	fTextDivider = Bounds().Width();
756 
757 	fBarColor = kDefaultBarColor;
758 	fCustomBarHeight = false;
759 
760 	SetFlags(Flags() | B_FRAME_EVENTS);
761 }
762 
763 
764 void
765 BStatusBar::_SetTextData(BString& text, const char* source,
766 	const BString& combineWith, bool rightAligned)
767 {
768 	if (source == NULL)
769 		source = "";
770 
771 	// If there were no changes, we don't have to do anything
772 	if (text == source)
773 		return;
774 
775 	bool oldHasText = _HasText();
776 	text = source;
777 
778 	BString newString;
779 	if (rightAligned)
780 		newString << text << combineWith;
781 	else
782 		newString << combineWith << text;
783 
784 	if (oldHasText != _HasText())
785 		InvalidateLayout();
786 
787 	font_height fontHeight;
788 	GetFontHeight(&fontHeight);
789 
790 //	Invalidate(BRect(position, 0, position + invalidateWidth,
791 //		ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)));
792 // TODO: redrawing the entire area takes care of the edge case
793 // where the left side string changes because of truncation and
794 // part of it needs to be redrawn as well.
795 	Invalidate(BRect(0, 0, Bounds().right,
796 		ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)));
797 }
798 
799 
800 /*!
801 	Returns the inner bar frame without the surrounding bevel.
802 */
803 BRect
804 BStatusBar::_BarFrame(const font_height* fontHeight) const
805 {
806 	float top = 2;
807 	if (_HasText()) {
808 		if (fontHeight == NULL) {
809 			font_height height;
810 			GetFontHeight(&height);
811 			top = ceilf(height.ascent + height.descent) + 6;
812 		} else
813 			top = ceilf(fontHeight->ascent + fontHeight->descent) + 6;
814 	}
815 
816 	return BRect(2, top, Bounds().right - 2, top + BarHeight() - 4);
817 }
818 
819 
820 float
821 BStatusBar::_BarPosition(const BRect& barFrame) const
822 {
823 	if (fCurrent == 0)
824 		return barFrame.left - 1;
825 
826 	return roundf(barFrame.left - 1
827 		+ (fCurrent * (barFrame.Width() + 3) / fMax));
828 }
829 
830 
831 bool
832 BStatusBar::_HasText() const
833 {
834 	// Force BeOS behavior where the size of the BStatusBar always included
835 	// room for labels, even when there weren't any.
836 	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
837 		return true;
838 	return fLabel.Length() > 0 || fTrailingLabel.Length() > 0
839 		|| fTrailingText.Length() > 0 || fText.Length() > 0;
840 }
841