xref: /haiku/src/kits/interface/TextControl.cpp (revision 959ff00ddee8411dabb09211f3bfbd52d87229da)
1 /*
2  * Copyright 2001-2008, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Frans van Nispen (xlr8@tref.nl)
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *		Ingo Weinhold <bonefish@cs.tu-berlin.de>
9  */
10 
11 /*!	BTextControl displays text that can act like a control. */
12 
13 
14 #include <stdio.h>
15 
16 #include <AbstractLayoutItem.h>
17 #include <LayoutUtils.h>
18 #include <Message.h>
19 #include <Region.h>
20 #include <TextControl.h>
21 #include <Window.h>
22 
23 #include "TextInput.h"
24 
25 
26 class BTextControl::LabelLayoutItem : public BAbstractLayoutItem {
27 public:
28 								LabelLayoutItem(BTextControl* parent);
29 
30 	virtual	bool				IsVisible();
31 	virtual	void				SetVisible(bool visible);
32 
33 	virtual	BRect				Frame();
34 	virtual	void				SetFrame(BRect frame);
35 
36 	virtual	BView*				View();
37 
38 	virtual	BSize				BaseMinSize();
39 	virtual	BSize				BaseMaxSize();
40 	virtual	BSize				BasePreferredSize();
41 	virtual	BAlignment			BaseAlignment();
42 
43 private:
44 			BTextControl*		fParent;
45 			BRect				fFrame;
46 };
47 
48 
49 class BTextControl::TextViewLayoutItem : public BAbstractLayoutItem {
50 public:
51 								TextViewLayoutItem(BTextControl* parent);
52 
53 	virtual	bool				IsVisible();
54 	virtual	void				SetVisible(bool visible);
55 
56 	virtual	BRect				Frame();
57 	virtual	void				SetFrame(BRect frame);
58 
59 	virtual	BView*				View();
60 
61 	virtual	BSize				BaseMinSize();
62 	virtual	BSize				BaseMaxSize();
63 	virtual	BSize				BasePreferredSize();
64 	virtual	BAlignment			BaseAlignment();
65 
66 private:
67 			BTextControl*		fParent;
68 			BRect				fFrame;
69 };
70 
71 
72 // #pragma mark -
73 
74 
75 BTextControl::BTextControl(BRect frame, const char* name, const char* label,
76 		const char* text, BMessage* message, uint32 mask, uint32 flags)
77 	: BControl(frame, name, label, message, mask, flags | B_FRAME_EVENTS)
78 {
79 	_InitData(label, text);
80 	_ValidateLayout();
81 }
82 
83 
84 BTextControl::BTextControl(const char* name, const char* label,
85 		const char* text, BMessage* message, uint32 flags)
86 	: BControl(BRect(0, 0, -1, -1), name, label, message, B_FOLLOW_NONE,
87 		flags | B_FRAME_EVENTS | B_SUPPORTS_LAYOUT)
88 {
89 	_InitData(label, text);
90 	_ValidateLayout();
91 }
92 
93 
94 BTextControl::BTextControl(const char* label, const char* text,
95 		BMessage* message)
96 	: BControl(BRect(0, 0, -1, -1), NULL, label, message, B_FOLLOW_NONE,
97 		B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS | B_SUPPORTS_LAYOUT)
98 {
99 	_InitData(label, text);
100 	_ValidateLayout();
101 }
102 
103 
104 BTextControl::~BTextControl()
105 {
106 	SetModificationMessage(NULL);
107 }
108 
109 
110 BTextControl::BTextControl(BMessage* archive)
111 	: BControl(archive)
112 {
113 	_InitData(Label(), NULL, archive);
114 
115 	int32 labelAlignment = B_ALIGN_LEFT;
116 	int32 textAlignment = B_ALIGN_LEFT;
117 
118 	if (archive->HasInt32("_a_label"))
119 		archive->FindInt32("_a_label", &labelAlignment);
120 
121 	if (archive->HasInt32("_a_text"))
122 		archive->FindInt32("_a_text", &textAlignment);
123 
124 	SetAlignment((alignment)labelAlignment, (alignment)textAlignment);
125 
126 	if (archive->HasFloat("_divide"))
127 		archive->FindFloat("_divide", &fDivider);
128 
129 	if (archive->HasMessage("_mod_msg")) {
130 		BMessage* message = new BMessage;
131 		archive->FindMessage("_mod_msg", message);
132 		SetModificationMessage(message);
133 	}
134 }
135 
136 
137 BArchivable*
138 BTextControl::Instantiate(BMessage* archive)
139 {
140 	if (validate_instantiation(archive, "BTextControl"))
141 		return new BTextControl(archive);
142 
143 	return NULL;
144 }
145 
146 
147 status_t
148 BTextControl::Archive(BMessage *data, bool deep) const
149 {
150 	status_t ret = BControl::Archive(data, deep);
151 	alignment labelAlignment, textAlignment;
152 
153 	GetAlignment(&labelAlignment, &textAlignment);
154 
155 	if (ret == B_OK)
156 		ret = data->AddInt32("_a_label", labelAlignment);
157 	if (ret == B_OK)
158 		ret = data->AddInt32("_a_text", textAlignment);
159 	if (ret == B_OK)
160 		ret = data->AddFloat("_divide", Divider());
161 
162 	if (ModificationMessage() && (ret == B_OK))
163 		ret = data->AddMessage("_mod_msg", ModificationMessage());
164 
165 	return ret;
166 }
167 
168 
169 void
170 BTextControl::SetText(const char *text)
171 {
172 	if (InvokeKind() != B_CONTROL_INVOKED)
173 		return;
174 
175 	fText->SetText(text);
176 
177 	if (IsFocus())
178 		fText->SetInitialText();
179 
180 	fText->Invalidate();
181 }
182 
183 
184 const char *
185 BTextControl::Text() const
186 {
187 	return fText->Text();
188 }
189 
190 
191 void
192 BTextControl::SetValue(int32 value)
193 {
194 	BControl::SetValue(value);
195 }
196 
197 
198 status_t
199 BTextControl::Invoke(BMessage *message)
200 {
201 	return BControl::Invoke(message);
202 }
203 
204 
205 BTextView *
206 BTextControl::TextView() const
207 {
208 	return fText;
209 }
210 
211 
212 void
213 BTextControl::SetModificationMessage(BMessage *message)
214 {
215 	delete fModificationMessage;
216 	fModificationMessage = message;
217 }
218 
219 
220 BMessage *
221 BTextControl::ModificationMessage() const
222 {
223 	return fModificationMessage;
224 }
225 
226 
227 void
228 BTextControl::SetAlignment(alignment labelAlignment, alignment textAlignment)
229 {
230 	fText->SetAlignment(textAlignment);
231 	fText->AlignTextRect();
232 
233 	if (fLabelAlign != labelAlignment) {
234 		fLabelAlign = labelAlignment;
235 		Invalidate();
236 	}
237 }
238 
239 
240 void
241 BTextControl::GetAlignment(alignment* _label, alignment* _text) const
242 {
243 	if (_label)
244 		*_label = fLabelAlign;
245 	if (_text)
246 		*_text = fText->Alignment();
247 }
248 
249 
250 void
251 BTextControl::SetDivider(float dividingLine)
252 {
253 	fDivider = floorf(dividingLine + 0.5);
254 
255 	_LayoutTextView();
256 
257 	if (Window()) {
258 		fText->Invalidate();
259 		Invalidate();
260 	}
261 }
262 
263 
264 float
265 BTextControl::Divider() const
266 {
267 	return fDivider;
268 }
269 
270 
271 void
272 BTextControl::Draw(BRect updateRect)
273 {
274 	rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
275 	rgb_color lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT);
276 	rgb_color lighten2 = tint_color(noTint, B_LIGHTEN_2_TINT);
277 	rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
278 	rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT);
279 	rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT);
280 	rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT);
281 	rgb_color navigationColor = ui_color(B_KEYBOARD_NAVIGATION_COLOR);
282 
283 	bool enabled = IsEnabled();
284 	bool active = false;
285 
286 	if (fText->IsFocus() && Window()->IsActive())
287 		active = true;
288 
289 	// outer bevel
290 
291 	BRect rect = fText->Frame();
292 	rect.InsetBy(-2, -2);
293 
294 	if (enabled)
295 		SetHighColor(darken1);
296 	else
297 		SetHighColor(noTint);
298 
299 	StrokeLine(rect.LeftBottom(), rect.LeftTop());
300 	StrokeLine(rect.RightTop());
301 
302 	if (enabled)
303 		SetHighColor(lighten2);
304 	else
305 		SetHighColor(lighten1);
306 
307 	StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
308 	StrokeLine(BPoint(rect.right, rect.top + 1.0f), rect.RightBottom());
309 
310 	// inner bevel
311 
312 	rect.InsetBy(1.0f, 1.0f);
313 
314 	if (active) {
315 		SetHighColor(navigationColor);
316 		StrokeRect(rect);
317 	} else {
318 		if (enabled)
319 			SetHighColor(darken4);
320 		else
321 			SetHighColor(darken2);
322 
323 		StrokeLine(rect.LeftTop(), rect.LeftBottom());
324 		StrokeLine(rect.LeftTop(), rect.RightTop());
325 
326 		SetHighColor(noTint);
327 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
328 		StrokeLine(BPoint(rect.right, rect.top + 1.0f));
329 	}
330 
331 	// label
332 
333 	if (Label()) {
334 		font_height fontHeight;
335 		GetFontHeight(&fontHeight);
336 
337 		float y = Bounds().top + (Bounds().Height() + 1 - fontHeight.ascent
338 			- fontHeight.descent) / 2 + fontHeight.ascent;
339 		float x;
340 
341 		float labelWidth = StringWidth(Label());
342 		switch (fLabelAlign) {
343 			case B_ALIGN_RIGHT:
344 				x = fDivider - labelWidth - 3.0;
345 				break;
346 
347 			case B_ALIGN_CENTER:
348 				x = fDivider - labelWidth / 2.0;
349 				break;
350 
351 			default:
352 				x = 0.0;
353 				break;
354 		}
355 
356 		BRect labelArea(x, Bounds().top, x + labelWidth, Bounds().bottom);
357 		if (x < fDivider && updateRect.Intersects(labelArea)) {
358 			labelArea.right = fText->Frame().left - 3;
359 
360 			BRegion clipRegion(labelArea);
361 			ConstrainClippingRegion(&clipRegion);
362 			SetHighColor(IsEnabled() ? ui_color(B_CONTROL_TEXT_COLOR)
363 				: tint_color(noTint, B_DISABLED_LABEL_TINT));
364 			DrawString(Label(), BPoint(x, y));
365 		}
366 	}
367 }
368 
369 
370 void
371 BTextControl::MouseDown(BPoint where)
372 {
373 	if (!fText->IsFocus()) {
374 		fText->MakeFocus(true);
375 	}
376 }
377 
378 
379 void
380 BTextControl::AttachedToWindow()
381 {
382 	BControl::AttachedToWindow();
383 
384 	_UpdateTextViewColors(IsEnabled());
385 	fText->MakeEditable(IsEnabled());
386 }
387 
388 
389 void
390 BTextControl::MakeFocus(bool state)
391 {
392 	if (state != fText->IsFocus()) {
393 		fText->MakeFocus(state);
394 
395 		if (state)
396 			fText->SelectAll();
397 	}
398 }
399 
400 
401 void
402 BTextControl::SetEnabled(bool enabled)
403 {
404 	if (IsEnabled() == enabled)
405 		return;
406 
407 	if (Window()) {
408 		fText->MakeEditable(enabled);
409 
410 		_UpdateTextViewColors(enabled);
411 
412 		fText->Invalidate();
413 		Window()->UpdateIfNeeded();
414 	}
415 
416 	BControl::SetEnabled(enabled);
417 }
418 
419 
420 void
421 BTextControl::GetPreferredSize(float *_width, float *_height)
422 {
423 	if (_height) {
424 		// we need enough space for the label and the child text view
425 		font_height fontHeight;
426 		GetFontHeight(&fontHeight);
427 		float labelHeight = ceil(fontHeight.ascent + fontHeight.descent
428 			+ fontHeight.leading);
429 		float textHeight = ceilf(fText->LineHeight(0) + 1.0) + 4.0;
430 
431 		*_height = max_c(labelHeight, textHeight);
432 	}
433 
434 	if (_width) {
435 		*_width = Bounds().Width();
436 		const char* label = Label();
437 		if (label) {
438 			float width = ceilf(StringWidth(label));
439 			*_width = (width * 1.3) + width + 4.0;
440 		}
441 	}
442 }
443 
444 
445 void
446 BTextControl::ResizeToPreferred()
447 {
448 	BView::ResizeToPreferred();
449 
450 	fDivider = 0.0;
451 	const char* label = Label();
452 	if (label)
453 		fDivider = ceil(StringWidth(label)) + 2.0;
454 
455 	_LayoutTextView();
456 }
457 
458 
459 void
460 BTextControl::SetFlags(uint32 flags)
461 {
462 	if (!fSkipSetFlags) {
463 		// If the textview is navigable, set it to not navigable if needed
464 		// Else if it is not navigable, set it to navigable if needed
465 		if (fText->Flags() & B_NAVIGABLE) {
466 			if (!(flags & B_NAVIGABLE))
467 				fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
468 
469 		} else {
470 			if (flags & B_NAVIGABLE)
471 				fText->SetFlags(fText->Flags() | B_NAVIGABLE);
472 		}
473 
474 		// Don't make this one navigable
475 		flags &= ~B_NAVIGABLE;
476 	}
477 
478 	BView::SetFlags(flags);
479 }
480 
481 
482 void
483 BTextControl::MessageReceived(BMessage *msg)
484 {
485 	switch(msg->what) {
486 		case B_SET_PROPERTY:
487 		case B_GET_PROPERTY:
488 			// TODO
489 			break;
490 		default:
491 			BControl::MessageReceived(msg);
492 			break;
493 	}
494 }
495 
496 
497 BHandler *
498 BTextControl::ResolveSpecifier(BMessage *msg, int32 index,
499 										 BMessage *specifier, int32 form,
500 										 const char *property)
501 {
502 	/*
503 	BPropertyInfo propInfo(prop_list);
504 	BHandler *target = NULL;
505 
506 	if (propInfo.FindMatch(message, 0, specifier, what, property) < B_OK)
507 		return BControl::ResolveSpecifier(message, index, specifier, what,
508 			property);
509 	else
510 		return this;
511 	*/
512 	return BControl::ResolveSpecifier(msg, index, specifier, form, property);
513 }
514 
515 
516 status_t
517 BTextControl::GetSupportedSuites(BMessage *data)
518 {
519 	return BControl::GetSupportedSuites(data);
520 }
521 
522 
523 void
524 BTextControl::MouseUp(BPoint pt)
525 {
526 	BControl::MouseUp(pt);
527 }
528 
529 
530 void
531 BTextControl::MouseMoved(BPoint pt, uint32 code, const BMessage *msg)
532 {
533 	BControl::MouseMoved(pt, code, msg);
534 }
535 
536 
537 void
538 BTextControl::DetachedFromWindow()
539 {
540 	BControl::DetachedFromWindow();
541 }
542 
543 
544 void
545 BTextControl::AllAttached()
546 {
547 	BControl::AllAttached();
548 }
549 
550 
551 void
552 BTextControl::AllDetached()
553 {
554 	BControl::AllDetached();
555 }
556 
557 
558 void
559 BTextControl::FrameMoved(BPoint newPosition)
560 {
561 	BControl::FrameMoved(newPosition);
562 }
563 
564 
565 void
566 BTextControl::FrameResized(float width, float height)
567 {
568 	BControl::FrameResized(width, height);
569 
570 	// changes in width
571 
572 	BRect bounds = Bounds();
573 	const float border = 2.0f;
574 
575 	if (bounds.Width() > fPreviousWidth) {
576 		// invalidate the region between the old and the new right border
577 		BRect rect = bounds;
578 		rect.left += fPreviousWidth - border;
579 		rect.right--;
580 		Invalidate(rect);
581 	} else if (bounds.Width() < fPreviousWidth) {
582 		// invalidate the region of the new right border
583 		BRect rect = bounds;
584 		rect.left = rect.right - border;
585 		Invalidate(rect);
586 	}
587 
588 	// changes in height
589 
590 	if (bounds.Height() > fPreviousHeight) {
591 		// invalidate the region between the old and the new bottom border
592 		BRect rect = bounds;
593 		rect.top += fPreviousHeight - border;
594 		rect.bottom--;
595 		Invalidate(rect);
596 	} else if (bounds.Height() < fPreviousHeight) {
597 		// invalidate the region of the new bottom border
598 		BRect rect = bounds;
599 		rect.top = rect.bottom - border;
600 		Invalidate(rect);
601 	}
602 
603 	fPreviousWidth = uint16(bounds.Width());
604 	fPreviousHeight = uint16(bounds.Height());
605 }
606 
607 
608 void
609 BTextControl::WindowActivated(bool active)
610 {
611 	if (fText->IsFocus()) {
612 		// invalidate to remove/show focus indication
613 		BRect rect = fText->Frame();
614 		rect.InsetBy(-1, -1);
615 		Invalidate(rect);
616 
617 		// help out embedded text view which doesn't
618 		// get notified of this
619 		fText->Invalidate();
620 	}
621 }
622 
623 
624 status_t
625 BTextControl::Perform(perform_code d, void *arg)
626 {
627 	return BControl::Perform(d, arg);
628 }
629 
630 
631 BLayoutItem*
632 BTextControl::CreateLabelLayoutItem()
633 {
634 	if (!fLabelLayoutItem)
635 		fLabelLayoutItem = new LabelLayoutItem(this);
636 	return fLabelLayoutItem;
637 }
638 
639 
640 BLayoutItem*
641 BTextControl::CreateTextViewLayoutItem()
642 {
643 	if (!fTextViewLayoutItem)
644 		fTextViewLayoutItem = new TextViewLayoutItem(this);
645 	return fTextViewLayoutItem;
646 }
647 
648 
649 void BTextControl::_ReservedTextControl1() {}
650 void BTextControl::_ReservedTextControl2() {}
651 void BTextControl::_ReservedTextControl3() {}
652 void BTextControl::_ReservedTextControl4() {}
653 
654 
655 BTextControl &
656 BTextControl::operator=(const BTextControl&)
657 {
658 	return *this;
659 }
660 
661 
662 void
663 BTextControl::_UpdateTextViewColors(bool enabled)
664 {
665 	rgb_color textColor;
666 	rgb_color color;
667 	BFont font;
668 
669 	fText->GetFontAndColor(0, &font);
670 
671 	if (enabled)
672 		textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
673 	else {
674 		textColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
675 			B_DISABLED_LABEL_TINT);
676 	}
677 
678 	fText->SetFontAndColor(&font, B_FONT_ALL, &textColor);
679 
680 	if (enabled) {
681 		color = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
682 	} else {
683 		color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
684 			B_LIGHTEN_2_TINT);
685 	}
686 
687 	fText->SetViewColor(color);
688 	fText->SetLowColor(color);
689 }
690 
691 
692 void
693 BTextControl::_CommitValue()
694 {
695 }
696 
697 
698 void
699 BTextControl::_InitData(const char* label, const char* initialText,
700 	BMessage* archive)
701 {
702 	BRect bounds(Bounds());
703 
704 	fText = NULL;
705 	fModificationMessage = NULL;
706 	fLabelAlign = B_ALIGN_LEFT;
707 	fDivider = 0.0f;
708 	fPreviousWidth = bounds.Width();
709 	fPreviousHeight = bounds.Height();
710 	fLabelLayoutItem = NULL;
711 	fTextViewLayoutItem = NULL;
712 	fSkipSetFlags = false;
713 
714 	int32 flags = 0;
715 
716 	BFont font(be_plain_font);
717 
718 	if (!archive || !archive->HasString("_fname"))
719 		flags |= B_FONT_FAMILY_AND_STYLE;
720 
721 	if (!archive || !archive->HasFloat("_fflt"))
722 		flags |= B_FONT_SIZE;
723 
724 	if (flags != 0)
725 		SetFont(&font, flags);
726 
727 	if (label)
728 		fDivider = floorf(bounds.Width() / 2.0f);
729 
730 	uint32 navigableFlags = Flags() & B_NAVIGABLE;
731 	if (navigableFlags != 0) {
732 		fSkipSetFlags = true;
733 		SetFlags(Flags() & ~B_NAVIGABLE);
734 		fSkipSetFlags = false;
735 	}
736 
737 	if (archive)
738 		fText = static_cast<BPrivate::_BTextInput_*>(FindView("_input_"));
739 
740 	if (fText == NULL) {
741 		BRect frame(fDivider, bounds.top, bounds.right, bounds.bottom);
742 		// we are stroking the frame around the text view, which
743 		// is 2 pixels wide
744 		frame.InsetBy(2.0, 3.0);
745 		BRect textRect(frame.OffsetToCopy(B_ORIGIN));
746 
747 		fText = new BPrivate::_BTextInput_(frame, textRect,
748 			B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
749 			B_WILL_DRAW | B_FRAME_EVENTS | navigableFlags);
750 		AddChild(fText);
751 
752 		SetText(initialText);
753 		fText->SetAlignment(B_ALIGN_LEFT);
754 		fText->AlignTextRect();
755 	}
756 }
757 
758 
759 void
760 BTextControl::_ValidateLayout()
761 {
762 	float height;
763 	BTextControl::GetPreferredSize(NULL, &height);
764 
765 	ResizeTo(Bounds().Width(), height);
766 
767 	_LayoutTextView();
768 
769 	fPreviousHeight = Bounds().Height();
770 }
771 
772 
773 void
774 BTextControl::_LayoutTextView()
775 {
776 	BRect frame = Bounds();
777 	frame.left = fDivider;
778 	// we are stroking the frame around the text view, which
779 	// is 2 pixels wide
780 	frame.InsetBy(2.0, 2.0);
781 	fText->MoveTo(frame.left, frame.top);
782 	fText->ResizeTo(frame.Width(), frame.Height());
783 	fText->AlignTextRect();
784 }
785 
786 
787 void
788 BTextControl::_UpdateFrame()
789 {
790 	if (fLabelLayoutItem && fTextViewLayoutItem) {
791 		BRect labelFrame = fLabelLayoutItem->Frame();
792 		BRect textFrame = fTextViewLayoutItem->Frame();
793 		MoveTo(labelFrame.left, labelFrame.top);
794 		ResizeTo(textFrame.left + textFrame.Width() - labelFrame.left,
795 			textFrame.top + textFrame.Height() - labelFrame.top);
796 		SetDivider(textFrame.left - labelFrame.left);
797 	}
798 }
799 
800 
801 // #pragma mark -
802 
803 
804 BTextControl::LabelLayoutItem::LabelLayoutItem(BTextControl* parent)
805 	: fParent(parent),
806 	  fFrame()
807 {
808 }
809 
810 
811 bool
812 BTextControl::LabelLayoutItem::IsVisible()
813 {
814 	return !fParent->IsHidden(fParent);
815 }
816 
817 
818 void
819 BTextControl::LabelLayoutItem::SetVisible(bool visible)
820 {
821 	// not allowed
822 }
823 
824 
825 BRect
826 BTextControl::LabelLayoutItem::Frame()
827 {
828 	return fFrame;
829 }
830 
831 
832 void
833 BTextControl::LabelLayoutItem::SetFrame(BRect frame)
834 {
835 	fFrame = frame;
836 	fParent->_UpdateFrame();
837 }
838 
839 
840 BView*
841 BTextControl::LabelLayoutItem::View()
842 {
843 	return fParent;
844 }
845 
846 
847 BSize
848 BTextControl::LabelLayoutItem::BaseMinSize()
849 {
850 // TODO: Cache the info. Might be too expensive for this call.
851 	const char* label = fParent->Label();
852 	if (!label)
853 		return BSize(-1, -1);
854 
855 	BSize size;
856 	fParent->GetPreferredSize(NULL, &size.height);
857 
858 	size.width = fParent->StringWidth(label) + 2 * 3 - 1;
859 
860 	return size;
861 }
862 
863 
864 BSize
865 BTextControl::LabelLayoutItem::BaseMaxSize()
866 {
867 	return BaseMinSize();
868 }
869 
870 
871 BSize
872 BTextControl::LabelLayoutItem::BasePreferredSize()
873 {
874 	return BaseMinSize();
875 }
876 
877 
878 BAlignment
879 BTextControl::LabelLayoutItem::BaseAlignment()
880 {
881 	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
882 }
883 
884 
885 // #pragma mark -
886 
887 
888 BTextControl::TextViewLayoutItem::TextViewLayoutItem(BTextControl* parent)
889 	: fParent(parent),
890 	  fFrame()
891 {
892 }
893 
894 
895 bool
896 BTextControl::TextViewLayoutItem::IsVisible()
897 {
898 	return !fParent->IsHidden(fParent);
899 }
900 
901 
902 void
903 BTextControl::TextViewLayoutItem::SetVisible(bool visible)
904 {
905 	// not allowed
906 }
907 
908 
909 BRect
910 BTextControl::TextViewLayoutItem::Frame()
911 {
912 	return fFrame;
913 }
914 
915 
916 void
917 BTextControl::TextViewLayoutItem::SetFrame(BRect frame)
918 {
919 	fFrame = frame;
920 	fParent->_UpdateFrame();
921 }
922 
923 
924 BView*
925 BTextControl::TextViewLayoutItem::View()
926 {
927 	return fParent;
928 }
929 
930 
931 BSize
932 BTextControl::TextViewLayoutItem::BaseMinSize()
933 {
934 // TODO: Cache the info. Might be too expensive for this call.
935 	BSize size;
936 	fParent->GetPreferredSize(NULL, &size.height);
937 
938 	// mmh, some arbitrary minimal width
939 	size.width = 30;
940 
941 	return size;
942 }
943 
944 
945 BSize
946 BTextControl::TextViewLayoutItem::BaseMaxSize()
947 {
948 	BSize size(BaseMinSize());
949 	size.width = B_SIZE_UNLIMITED;
950 	return size;
951 }
952 
953 
954 BSize
955 BTextControl::TextViewLayoutItem::BasePreferredSize()
956 {
957 	BSize size(BaseMinSize());
958 	// puh, no idea...
959 	size.width = 100;
960 	return size;
961 }
962 
963 
964 BAlignment
965 BTextControl::TextViewLayoutItem::BaseAlignment()
966 {
967 	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
968 }
969 
970