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