tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / TextControl.cpp
blob3e2707f3faa31b52975c2df2e24670a34011ac79
1 /*
2 * Copyright 2001-2015, Haiku Inc.
3 * Distributed under the terms of the MIT License.
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 */
12 /*! BTextControl displays text that can act like a control. */
15 #include <TextControl.h>
17 #include <string.h>
19 #include <AbstractLayoutItem.h>
20 #include <ControlLook.h>
21 #include <LayoutUtils.h>
22 #include <Message.h>
23 #include <PropertyInfo.h>
24 #include <Region.h>
25 #include <Window.h>
27 #include <binary_compatibility/Interface.h>
28 #include <binary_compatibility/Support.h>
30 #include "TextInput.h"
33 //#define TRACE_TEXT_CONTROL
34 #ifdef TRACE_TEXT_CONTROL
35 # include <stdio.h>
36 # include <FunctionTracer.h>
37 static int32 sFunctionDepth = -1;
38 # define CALLED(x...) FunctionTracer _ft("BTextControl", __FUNCTION__, \
39 sFunctionDepth)
40 # define TRACE(x...) { BString _to; \
41 _to.Append(' ', (sFunctionDepth + 1) * 2); \
42 printf("%s", _to.String()); printf(x); }
43 #else
44 # define CALLED(x...)
45 # define TRACE(x...)
46 #endif
49 namespace {
50 const char* const kFrameField = "BTextControl:layoutitem:frame";
51 const char* const kTextViewItemField = "BTextControl:textViewItem";
52 const char* const kLabelItemField = "BMenuField:labelItem";
56 static property_info sPropertyList[] = {
58 "Value",
59 { B_GET_PROPERTY, B_SET_PROPERTY },
60 { B_DIRECT_SPECIFIER },
61 NULL, 0,
62 { B_STRING_TYPE }
68 class BTextControl::LabelLayoutItem : public BAbstractLayoutItem {
69 public:
70 LabelLayoutItem(BTextControl* parent);
71 LabelLayoutItem(BMessage* from);
73 virtual bool IsVisible();
74 virtual void SetVisible(bool visible);
76 virtual BRect Frame();
77 virtual void SetFrame(BRect frame);
79 void SetParent(BTextControl* parent);
80 virtual BView* View();
82 virtual BSize BaseMinSize();
83 virtual BSize BaseMaxSize();
84 virtual BSize BasePreferredSize();
85 virtual BAlignment BaseAlignment();
87 BRect FrameInParent() const;
89 virtual status_t Archive(BMessage* into, bool deep = true) const;
90 static BArchivable* Instantiate(BMessage* from);
92 private:
93 BTextControl* fParent;
94 BRect fFrame;
98 class BTextControl::TextViewLayoutItem : public BAbstractLayoutItem {
99 public:
100 TextViewLayoutItem(BTextControl* parent);
101 TextViewLayoutItem(BMessage* from);
103 virtual bool IsVisible();
104 virtual void SetVisible(bool visible);
106 virtual BRect Frame();
107 virtual void SetFrame(BRect frame);
109 void SetParent(BTextControl* parent);
110 virtual BView* View();
112 virtual BSize BaseMinSize();
113 virtual BSize BaseMaxSize();
114 virtual BSize BasePreferredSize();
115 virtual BAlignment BaseAlignment();
117 BRect FrameInParent() const;
119 virtual status_t Archive(BMessage* into, bool deep = true) const;
120 static BArchivable* Instantiate(BMessage* from);
121 private:
122 BTextControl* fParent;
123 BRect fFrame;
127 struct BTextControl::LayoutData {
128 LayoutData(float width, float height)
130 label_layout_item(NULL),
131 text_view_layout_item(NULL),
132 previous_width(width),
133 previous_height(height),
134 valid(false)
138 LabelLayoutItem* label_layout_item;
139 TextViewLayoutItem* text_view_layout_item;
140 float previous_width; // used in FrameResized() for
141 float previous_height; // invalidation
142 font_height font_info;
143 float label_width;
144 float label_height;
145 BSize min;
146 BSize text_view_min;
147 bool valid;
151 static const int32 kFrameMargin = 2;
152 static const int32 kLabelInputSpacing = 3;
155 // #pragma mark - BTextControl
158 BTextControl::BTextControl(BRect frame, const char* name, const char* label,
159 const char* text, BMessage* message, uint32 resizeMask, uint32 flags)
161 BControl(frame, name, label, message, resizeMask, flags | B_FRAME_EVENTS)
163 _InitData(label);
164 _InitText(text);
165 _ValidateLayout();
169 BTextControl::BTextControl(const char* name, const char* label,
170 const char* text, BMessage* message, uint32 flags)
172 BControl(name, label, message, flags | B_FRAME_EVENTS)
174 _InitData(label);
175 _InitText(text);
176 _ValidateLayout();
180 BTextControl::BTextControl(const char* label, const char* text,
181 BMessage* message)
183 BControl(NULL, label, message,
184 B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS)
186 _InitData(label);
187 _InitText(text);
188 _ValidateLayout();
192 BTextControl::~BTextControl()
194 SetModificationMessage(NULL);
195 delete fLayoutData;
199 // #pragma mark - Archiving
202 BTextControl::BTextControl(BMessage* archive)
204 BControl(BUnarchiver::PrepareArchive(archive))
206 BUnarchiver unarchiver(archive);
208 _InitData(Label(), archive);
210 if (!BUnarchiver::IsArchiveManaged(archive))
211 _InitText(NULL, archive);
213 status_t err = B_OK;
214 if (archive->HasFloat("_divide"))
215 err = archive->FindFloat("_divide", &fDivider);
217 if (err == B_OK && archive->HasMessage("_mod_msg")) {
218 BMessage* message = new BMessage;
219 err = archive->FindMessage("_mod_msg", message);
220 SetModificationMessage(message);
223 unarchiver.Finish(err);
227 BArchivable*
228 BTextControl::Instantiate(BMessage* archive)
230 if (validate_instantiation(archive, "BTextControl"))
231 return new BTextControl(archive);
233 return NULL;
237 status_t
238 BTextControl::Archive(BMessage* data, bool deep) const
240 BArchiver archiver(data);
241 status_t result = BControl::Archive(data, deep);
243 alignment labelAlignment;
244 alignment textAlignment;
245 if (result == B_OK)
246 GetAlignment(&labelAlignment, &textAlignment);
248 if (result == B_OK)
249 result = data->AddInt32("_a_label", labelAlignment);
251 if (result == B_OK)
252 result = data->AddInt32("_a_text", textAlignment);
254 if (result == B_OK)
255 result = data->AddFloat("_divide", Divider());
257 if (result == B_OK && ModificationMessage() != NULL)
258 result = data->AddMessage("_mod_msg", ModificationMessage());
260 return archiver.Finish(result);
264 status_t
265 BTextControl::AllArchived(BMessage* into) const
267 BArchiver archiver(into);
268 status_t err = B_OK;
270 if (archiver.IsArchived(fLayoutData->text_view_layout_item)) {
271 err = archiver.AddArchivable(kTextViewItemField,
272 fLayoutData->text_view_layout_item);
275 if (err == B_OK && archiver.IsArchived(fLayoutData->label_layout_item)) {
276 err = archiver.AddArchivable(kLabelItemField,
277 fLayoutData->label_layout_item);
280 return err;
284 status_t
285 BTextControl::AllUnarchived(const BMessage* from)
287 status_t err;
288 if ((err = BControl::AllUnarchived(from)) != B_OK)
289 return err;
291 _InitText(NULL, from);
293 BUnarchiver unarchiver(from);
294 if (unarchiver.IsInstantiated(kTextViewItemField)) {
295 err = unarchiver.FindObject(kTextViewItemField,
296 BUnarchiver::B_DONT_ASSUME_OWNERSHIP,
297 fLayoutData->text_view_layout_item);
299 if (err == B_OK)
300 fLayoutData->text_view_layout_item->SetParent(this);
301 else
302 return err;
305 if (unarchiver.IsInstantiated(kLabelItemField)) {
306 err = unarchiver.FindObject(kLabelItemField,
307 BUnarchiver::B_DONT_ASSUME_OWNERSHIP,
308 fLayoutData->label_layout_item);
310 if (err == B_OK)
311 fLayoutData->label_layout_item->SetParent(this);
313 return err;
317 // #pragma mark - Hook methods
320 void
321 BTextControl::AllAttached()
323 BControl::AllAttached();
327 void
328 BTextControl::AllDetached()
330 BControl::AllDetached();
334 void
335 BTextControl::AttachedToWindow()
337 BControl::AttachedToWindow();
339 _UpdateTextViewColors(IsEnabled());
340 fText->MakeEditable(IsEnabled());
344 void
345 BTextControl::DetachedFromWindow()
347 BControl::DetachedFromWindow();
351 void
352 BTextControl::Draw(BRect updateRect)
354 bool enabled = IsEnabled();
355 bool active = fText->IsFocus() && Window()->IsActive();
357 BRect rect = fText->Frame();
358 rect.InsetBy(-2, -2);
360 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
361 uint32 flags = fLook;
362 if (!enabled)
363 flags |= BControlLook::B_DISABLED;
365 if (active)
366 flags |= BControlLook::B_FOCUSED;
368 be_control_look->DrawTextControlBorder(this, rect, updateRect, base,
369 flags);
371 if (Label() != NULL) {
372 if (fLayoutData->label_layout_item != NULL) {
373 rect = fLayoutData->label_layout_item->FrameInParent();
374 } else {
375 rect = Bounds();
376 rect.right = fDivider - kLabelInputSpacing;
379 be_control_look->DrawLabel(this, Label(), rect, updateRect,
380 base, flags, BAlignment(fLabelAlign, B_ALIGN_MIDDLE));
385 void
386 BTextControl::FrameMoved(BPoint newPosition)
388 BControl::FrameMoved(newPosition);
392 void
393 BTextControl::FrameResized(float width, float height)
395 CALLED();
397 BControl::FrameResized(width, height);
399 // TODO: this causes flickering still...
401 // changes in width
403 BRect bounds = Bounds();
405 if (bounds.Width() > fLayoutData->previous_width) {
406 // invalidate the region between the old and the new right border
407 BRect rect = bounds;
408 rect.left += fLayoutData->previous_width - kFrameMargin;
409 rect.right--;
410 Invalidate(rect);
411 } else if (bounds.Width() < fLayoutData->previous_width) {
412 // invalidate the region of the new right border
413 BRect rect = bounds;
414 rect.left = rect.right - kFrameMargin;
415 Invalidate(rect);
418 // changes in height
420 if (bounds.Height() > fLayoutData->previous_height) {
421 // invalidate the region between the old and the new bottom border
422 BRect rect = bounds;
423 rect.top += fLayoutData->previous_height - kFrameMargin;
424 rect.bottom--;
425 Invalidate(rect);
426 // invalidate label area
427 rect = bounds;
428 rect.right = fDivider;
429 Invalidate(rect);
430 } else if (bounds.Height() < fLayoutData->previous_height) {
431 // invalidate the region of the new bottom border
432 BRect rect = bounds;
433 rect.top = rect.bottom - kFrameMargin;
434 Invalidate(rect);
435 // invalidate label area
436 rect = bounds;
437 rect.right = fDivider;
438 Invalidate(rect);
441 fLayoutData->previous_width = bounds.Width();
442 fLayoutData->previous_height = bounds.Height();
444 TRACE("width: %.2f, height: %.2f\n", bounds.Width(), bounds.Height());
448 status_t
449 BTextControl::Invoke(BMessage* message)
451 return BControl::Invoke(message);
455 void
456 BTextControl::LayoutInvalidated(bool descendants)
458 CALLED();
460 fLayoutData->valid = false;
464 void
465 BTextControl::MessageReceived(BMessage* message)
467 if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) {
468 BMessage reply(B_REPLY);
469 bool handled = false;
471 BMessage specifier;
472 int32 index;
473 int32 form;
474 const char* property;
475 if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) {
476 if (strcmp(property, "Value") == 0) {
477 if (message->what == B_GET_PROPERTY) {
478 reply.AddString("result", fText->Text());
479 handled = true;
480 } else {
481 const char* value = NULL;
482 // B_SET_PROPERTY
483 if (message->FindString("data", &value) == B_OK) {
484 fText->SetText(value);
485 reply.AddInt32("error", B_OK);
486 handled = true;
492 if (handled) {
493 message->SendReply(&reply);
494 return;
498 BControl::MessageReceived(message);
502 void
503 BTextControl::MouseDown(BPoint where)
505 if (!fText->IsFocus())
506 fText->MakeFocus(true);
510 void
511 BTextControl::MouseMoved(BPoint where, uint32 transit,
512 const BMessage* dragMessage)
514 BControl::MouseMoved(where, transit, dragMessage);
518 void
519 BTextControl::MouseUp(BPoint where)
521 BControl::MouseUp(where);
525 void
526 BTextControl::WindowActivated(bool active)
528 if (fText->IsFocus()) {
529 // invalidate to remove/show focus indication
530 BRect rect = fText->Frame();
531 rect.InsetBy(-1, -1);
532 Invalidate(rect);
534 // help out embedded text view which doesn't
535 // get notified of this
536 fText->Invalidate();
541 // #pragma mark - Getters and Setters
544 void
545 BTextControl::SetText(const char* text)
547 if (InvokeKind() != B_CONTROL_INVOKED)
548 return;
550 CALLED();
552 fText->SetText(text);
554 if (fText->IsFocus()) {
555 fText->SetInitialText();
556 fText->SelectAll();
559 fText->Invalidate();
563 const char*
564 BTextControl::Text() const
566 return fText->Text();
570 int32
571 BTextControl::TextLength() const
573 return fText->TextLength();
577 void
578 BTextControl::MarkAsInvalid(bool invalid)
580 uint32 look = fLook;
582 if (invalid)
583 fLook |= BControlLook::B_INVALID;
584 else
585 fLook &= ~BControlLook::B_INVALID;
587 if (look != fLook)
588 Invalidate();
592 void
593 BTextControl::SetValue(int32 value)
595 BControl::SetValue(value);
599 BTextView*
600 BTextControl::TextView() const
602 return fText;
606 void
607 BTextControl::SetModificationMessage(BMessage* message)
609 delete fModificationMessage;
610 fModificationMessage = message;
614 BMessage*
615 BTextControl::ModificationMessage() const
617 return fModificationMessage;
621 void
622 BTextControl::SetAlignment(alignment labelAlignment, alignment textAlignment)
624 fText->SetAlignment(textAlignment);
625 fText->AlignTextRect();
627 if (fLabelAlign != labelAlignment) {
628 fLabelAlign = labelAlignment;
629 Invalidate();
634 void
635 BTextControl::GetAlignment(alignment* _label, alignment* _text) const
637 if (_label != NULL)
638 *_label = fLabelAlign;
640 if (_text != NULL)
641 *_text = fText->Alignment();
645 void
646 BTextControl::SetDivider(float position)
648 fDivider = floorf(position + 0.5);
650 _LayoutTextView();
652 if (Window()) {
653 fText->Invalidate();
654 Invalidate();
659 float
660 BTextControl::Divider() const
662 return fDivider;
666 void
667 BTextControl::MakeFocus(bool state)
669 if (state != fText->IsFocus()) {
670 fText->MakeFocus(state);
672 if (state)
673 fText->SelectAll();
678 void
679 BTextControl::SetEnabled(bool enable)
681 if (IsEnabled() == enable)
682 return;
684 if (Window() != NULL) {
685 fText->MakeEditable(enable);
686 if (enable)
687 fText->SetFlags(fText->Flags() | B_NAVIGABLE);
688 else
689 fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
691 _UpdateTextViewColors(enable);
693 fText->Invalidate();
694 Window()->UpdateIfNeeded();
697 BControl::SetEnabled(enable);
701 void
702 BTextControl::GetPreferredSize(float* _width, float* _height)
704 CALLED();
706 _ValidateLayoutData();
708 if (_width) {
709 float minWidth = fLayoutData->min.width;
710 if (Label() == NULL && !(Flags() & B_SUPPORTS_LAYOUT)) {
711 // Indeed, only if there is no label! BeOS backwards compatible
712 // behavior:
713 minWidth = max_c(minWidth, Bounds().Width());
715 *_width = minWidth;
718 if (_height)
719 *_height = fLayoutData->min.height;
723 void
724 BTextControl::ResizeToPreferred()
726 BView::ResizeToPreferred();
728 fDivider = 0.0;
729 const char* label = Label();
730 if (label)
731 fDivider = ceil(StringWidth(label)) + 2.0;
733 _LayoutTextView();
737 void
738 BTextControl::SetFlags(uint32 flags)
740 // If the textview is navigable, set it to not navigable if needed
741 // Else if it is not navigable, set it to navigable if needed
742 if (fText->Flags() & B_NAVIGABLE) {
743 if (!(flags & B_NAVIGABLE))
744 fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
746 } else {
747 if (flags & B_NAVIGABLE)
748 fText->SetFlags(fText->Flags() | B_NAVIGABLE);
751 // Don't make this one navigable
752 flags &= ~B_NAVIGABLE;
754 BView::SetFlags(flags);
758 // #pragma mark - Scripting
761 BHandler*
762 BTextControl::ResolveSpecifier(BMessage* message, int32 index,
763 BMessage* specifier, int32 what, const char* property)
765 BPropertyInfo propInfo(sPropertyList);
767 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
768 return this;
770 return BControl::ResolveSpecifier(message, index, specifier, what,
771 property);
775 status_t
776 BTextControl::GetSupportedSuites(BMessage* data)
778 return BControl::GetSupportedSuites(data);
782 // #pragma mark - Layout
785 BSize
786 BTextControl::MinSize()
788 CALLED();
790 _ValidateLayoutData();
791 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min);
795 BSize
796 BTextControl::MaxSize()
798 CALLED();
800 _ValidateLayoutData();
802 BSize max = fLayoutData->min;
803 max.width = B_SIZE_UNLIMITED;
805 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
809 BSize
810 BTextControl::PreferredSize()
812 CALLED();
814 _ValidateLayoutData();
815 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData->min);
819 BAlignment
820 BTextControl::LayoutAlignment()
822 CALLED();
824 _ValidateLayoutData();
825 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
826 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER));
830 BLayoutItem*
831 BTextControl::CreateLabelLayoutItem()
833 if (!fLayoutData->label_layout_item)
834 fLayoutData->label_layout_item = new LabelLayoutItem(this);
836 return fLayoutData->label_layout_item;
840 BLayoutItem*
841 BTextControl::CreateTextViewLayoutItem()
843 if (!fLayoutData->text_view_layout_item)
844 fLayoutData->text_view_layout_item = new TextViewLayoutItem(this);
846 return fLayoutData->text_view_layout_item;
850 void
851 BTextControl::DoLayout()
853 // Bail out, if we shan't do layout.
854 if (!(Flags() & B_SUPPORTS_LAYOUT))
855 return;
857 CALLED();
859 // If the user set a layout, we let the base class version call its
860 // hook.
861 if (GetLayout()) {
862 BView::DoLayout();
863 return;
866 _ValidateLayoutData();
868 // validate current size
869 BSize size(Bounds().Size());
870 if (size.width < fLayoutData->min.width)
871 size.width = fLayoutData->min.width;
873 if (size.height < fLayoutData->min.height)
874 size.height = fLayoutData->min.height;
876 BRect dirty(fText->Frame());
877 BRect textFrame;
879 // divider
880 float divider = 0;
881 if (fLayoutData->text_view_layout_item != NULL) {
882 if (fLayoutData->label_layout_item != NULL) {
883 // We have layout items. They define the divider location.
884 divider = fabs(fLayoutData->text_view_layout_item->Frame().left
885 - fLayoutData->label_layout_item->Frame().left);
887 textFrame = fLayoutData->text_view_layout_item->FrameInParent();
888 } else {
889 if (fLayoutData->label_width > 0) {
890 divider = fLayoutData->label_width
891 + be_control_look->DefaultLabelSpacing();
893 textFrame.Set(divider, 0, size.width, size.height);
896 // place the text view and set the divider
897 textFrame.InsetBy(kFrameMargin, kFrameMargin);
898 BLayoutUtils::AlignInFrame(fText, textFrame);
900 fDivider = divider;
902 // invalidate dirty region
903 dirty = dirty | fText->Frame();
904 dirty.InsetBy(-kFrameMargin, -kFrameMargin);
906 Invalidate(dirty);
910 // #pragma mark - protected methods
913 status_t
914 BTextControl::SetIcon(const BBitmap* icon, uint32 flags)
916 return BControl::SetIcon(icon, flags);
920 // #pragma mark - private methods
923 status_t
924 BTextControl::Perform(perform_code code, void* _data)
926 switch (code) {
927 case PERFORM_CODE_MIN_SIZE:
928 ((perform_data_min_size*)_data)->return_value
929 = BTextControl::MinSize();
930 return B_OK;
932 case PERFORM_CODE_MAX_SIZE:
933 ((perform_data_max_size*)_data)->return_value
934 = BTextControl::MaxSize();
935 return B_OK;
937 case PERFORM_CODE_PREFERRED_SIZE:
938 ((perform_data_preferred_size*)_data)->return_value
939 = BTextControl::PreferredSize();
940 return B_OK;
942 case PERFORM_CODE_LAYOUT_ALIGNMENT:
943 ((perform_data_layout_alignment*)_data)->return_value
944 = BTextControl::LayoutAlignment();
945 return B_OK;
947 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
948 ((perform_data_has_height_for_width*)_data)->return_value
949 = BTextControl::HasHeightForWidth();
950 return B_OK;
952 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
954 perform_data_get_height_for_width* data
955 = (perform_data_get_height_for_width*)_data;
956 BTextControl::GetHeightForWidth(data->width, &data->min, &data->max,
957 &data->preferred);
958 return B_OK;
961 case PERFORM_CODE_SET_LAYOUT:
963 perform_data_set_layout* data = (perform_data_set_layout*)_data;
964 BTextControl::SetLayout(data->layout);
965 return B_OK;
968 case PERFORM_CODE_LAYOUT_INVALIDATED:
970 perform_data_layout_invalidated* data
971 = (perform_data_layout_invalidated*)_data;
972 BTextControl::LayoutInvalidated(data->descendants);
973 return B_OK;
976 case PERFORM_CODE_DO_LAYOUT:
978 BTextControl::DoLayout();
979 return B_OK;
982 case PERFORM_CODE_SET_ICON:
984 perform_data_set_icon* data = (perform_data_set_icon*)_data;
985 return BTextControl::SetIcon(data->icon, data->flags);
988 case PERFORM_CODE_ALL_UNARCHIVED:
990 perform_data_all_unarchived* data
991 = (perform_data_all_unarchived*)_data;
992 data->return_value = BTextControl::AllUnarchived(data->archive);
993 return B_OK;
996 case PERFORM_CODE_ALL_ARCHIVED:
998 perform_data_all_archived* data
999 = (perform_data_all_archived*)_data;
1000 data->return_value = BTextControl::AllArchived(data->archive);
1001 return B_OK;
1005 return BControl::Perform(code, _data);
1009 // #pragma mark - FBC padding
1012 void BTextControl::_ReservedTextControl1() {}
1013 void BTextControl::_ReservedTextControl2() {}
1014 void BTextControl::_ReservedTextControl3() {}
1015 void BTextControl::_ReservedTextControl4() {}
1018 BTextControl&
1019 BTextControl::operator=(const BTextControl&)
1021 return *this;
1025 void
1026 BTextControl::_UpdateTextViewColors(bool enable)
1028 rgb_color textColor;
1029 rgb_color color;
1030 BFont font;
1032 fText->GetFontAndColor(0, &font);
1034 if (enable)
1035 textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
1036 else {
1037 textColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
1038 B_DISABLED_LABEL_TINT);
1041 fText->SetFontAndColor(&font, B_FONT_ALL, &textColor);
1043 if (enable)
1044 color = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
1045 else {
1046 color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
1047 B_LIGHTEN_2_TINT);
1050 fText->SetViewColor(color);
1051 fText->SetLowColor(color);
1055 void
1056 BTextControl::_CommitValue()
1061 void
1062 BTextControl::_InitData(const char* label, const BMessage* archive)
1064 BRect bounds(Bounds());
1066 fText = NULL;
1067 fModificationMessage = NULL;
1068 fLabelAlign = B_ALIGN_LEFT;
1069 fDivider = 0.0f;
1070 fLayoutData = new LayoutData(bounds.Width(), bounds.Height());
1072 int32 flags = 0;
1074 BFont font(be_plain_font);
1076 if (!archive || !archive->HasString("_fname"))
1077 flags |= B_FONT_FAMILY_AND_STYLE;
1079 if (!archive || !archive->HasFloat("_fflt"))
1080 flags |= B_FONT_SIZE;
1082 if (flags != 0)
1083 SetFont(&font, flags);
1085 if (label != NULL)
1086 fDivider = floorf(bounds.Width() / 2.0f);
1088 fLook = 0;
1092 void
1093 BTextControl::_InitText(const char* initialText, const BMessage* archive)
1095 if (archive)
1096 fText = static_cast<BPrivate::_BTextInput_*>(FindView("_input_"));
1098 if (fText == NULL) {
1099 BRect bounds(Bounds());
1100 BRect frame(fDivider, bounds.top, bounds.right, bounds.bottom);
1101 // we are stroking the frame around the text view, which
1102 // is 2 pixels wide
1103 frame.InsetBy(kFrameMargin, kFrameMargin);
1104 BRect textRect(frame.OffsetToCopy(B_ORIGIN));
1106 fText = new BPrivate::_BTextInput_(frame, textRect,
1107 B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS
1108 | (Flags() & B_NAVIGABLE));
1109 AddChild(fText);
1111 SetText(initialText);
1112 fText->SetAlignment(B_ALIGN_LEFT);
1113 fText->AlignTextRect();
1116 // Although this is not strictly initializing the text view,
1117 // it cannot be done while fText is NULL, so it resides here.
1118 if (archive) {
1119 int32 labelAlignment = B_ALIGN_LEFT;
1120 int32 textAlignment = B_ALIGN_LEFT;
1122 status_t err = B_OK;
1123 if (archive->HasInt32("_a_label"))
1124 err = archive->FindInt32("_a_label", &labelAlignment);
1126 if (err == B_OK && archive->HasInt32("_a_text"))
1127 err = archive->FindInt32("_a_text", &textAlignment);
1129 SetAlignment((alignment)labelAlignment, (alignment)textAlignment);
1132 uint32 navigableFlags = Flags() & B_NAVIGABLE;
1133 if (navigableFlags != 0)
1134 BView::SetFlags(Flags() & ~B_NAVIGABLE);
1138 void
1139 BTextControl::_ValidateLayout()
1141 CALLED();
1143 _ValidateLayoutData();
1145 ResizeTo(Bounds().Width(), fLayoutData->min.height);
1147 _LayoutTextView();
1151 void
1152 BTextControl::_LayoutTextView()
1154 CALLED();
1156 BRect frame;
1157 if (fLayoutData->text_view_layout_item != NULL) {
1158 frame = fLayoutData->text_view_layout_item->FrameInParent();
1159 } else {
1160 frame = Bounds();
1161 frame.left = fDivider;
1164 // we are stroking the frame around the text view, which
1165 // is 2 pixels wide
1166 frame.InsetBy(kFrameMargin, kFrameMargin);
1167 fText->MoveTo(frame.left, frame.top);
1168 fText->ResizeTo(frame.Width(), frame.Height());
1169 fText->AlignTextRect();
1171 TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
1172 TRACE("fDivider: %.2f\n", fDivider);
1173 TRACE("fText frame: (%.2f, %.2f, %.2f, %.2f)\n",
1174 frame.left, frame.top, frame.right, frame.bottom);
1178 void
1179 BTextControl::_UpdateFrame()
1181 CALLED();
1183 if (fLayoutData->text_view_layout_item != NULL) {
1184 BRect textFrame = fLayoutData->text_view_layout_item->Frame();
1185 BRect labelFrame;
1186 if (fLayoutData->label_layout_item != NULL)
1187 labelFrame = fLayoutData->label_layout_item->Frame();
1189 BRect frame;
1190 if (labelFrame.IsValid()) {
1191 frame = textFrame | labelFrame;
1193 // update divider
1194 fDivider = fabs(textFrame.left - labelFrame.left);
1195 } else {
1196 frame = textFrame;
1197 fDivider = 0;
1200 // update our frame
1201 MoveTo(frame.left, frame.top);
1202 BSize oldSize(Bounds().Size());
1203 ResizeTo(frame.Width(), frame.Height());
1204 BSize newSize(Bounds().Size());
1206 // If the size changes, ResizeTo() will trigger a relayout, otherwise
1207 // we need to do that explicitly.
1208 if (newSize != oldSize)
1209 Relayout();
1214 void
1215 BTextControl::_ValidateLayoutData()
1217 CALLED();
1219 if (fLayoutData->valid)
1220 return;
1222 // cache font height
1223 font_height& fh = fLayoutData->font_info;
1224 GetFontHeight(&fh);
1226 const char* label = Label();
1227 if (label != NULL) {
1228 fLayoutData->label_width = ceilf(StringWidth(label));
1229 fLayoutData->label_height = ceilf(fh.ascent) + ceilf(fh.descent);
1230 } else {
1231 fLayoutData->label_width = 0;
1232 fLayoutData->label_height = 0;
1235 // compute the minimal divider
1236 float divider = 0;
1237 if (fLayoutData->label_width > 0) {
1238 divider = fLayoutData->label_width
1239 + be_control_look->DefaultLabelSpacing();
1242 // If we shan't do real layout, we let the current divider take influence.
1243 if (!(Flags() & B_SUPPORTS_LAYOUT))
1244 divider = max_c(divider, fDivider);
1246 // get the minimal (== preferred) text view size
1247 fLayoutData->text_view_min = fText->MinSize();
1249 TRACE("text view min width: %.2f\n", fLayoutData->text_view_min.width);
1251 // compute our minimal (== preferred) size
1252 BSize min(fLayoutData->text_view_min);
1253 min.width += 2 * kFrameMargin;
1254 min.height += 2 * kFrameMargin;
1256 if (divider > 0)
1257 min.width += divider;
1259 if (fLayoutData->label_height > min.height)
1260 min.height = fLayoutData->label_height;
1262 fLayoutData->min = min;
1264 fLayoutData->valid = true;
1265 ResetLayoutInvalidation();
1267 TRACE("width: %.2f, height: %.2f\n", min.width, min.height);
1271 // #pragma mark - BTextControl::LabelLayoutItem
1274 BTextControl::LabelLayoutItem::LabelLayoutItem(BTextControl* parent)
1276 fParent(parent),
1277 fFrame()
1282 BTextControl::LabelLayoutItem::LabelLayoutItem(BMessage* from)
1284 BAbstractLayoutItem(from),
1285 fParent(NULL),
1286 fFrame()
1288 from->FindRect(kFrameField, &fFrame);
1292 bool
1293 BTextControl::LabelLayoutItem::IsVisible()
1295 return !fParent->IsHidden(fParent);
1299 void
1300 BTextControl::LabelLayoutItem::SetVisible(bool visible)
1302 // not allowed
1306 BRect
1307 BTextControl::LabelLayoutItem::Frame()
1309 return fFrame;
1313 void
1314 BTextControl::LabelLayoutItem::SetFrame(BRect frame)
1316 fFrame = frame;
1317 fParent->_UpdateFrame();
1321 void
1322 BTextControl::LabelLayoutItem::SetParent(BTextControl* parent)
1324 fParent = parent;
1328 BView*
1329 BTextControl::LabelLayoutItem::View()
1331 return fParent;
1335 BSize
1336 BTextControl::LabelLayoutItem::BaseMinSize()
1338 fParent->_ValidateLayoutData();
1340 if (!fParent->Label())
1341 return BSize(-1, -1);
1343 return BSize(fParent->fLayoutData->label_width
1344 + be_control_look->DefaultLabelSpacing(),
1345 fParent->fLayoutData->label_height);
1349 BSize
1350 BTextControl::LabelLayoutItem::BaseMaxSize()
1352 return BaseMinSize();
1356 BSize
1357 BTextControl::LabelLayoutItem::BasePreferredSize()
1359 return BaseMinSize();
1363 BAlignment
1364 BTextControl::LabelLayoutItem::BaseAlignment()
1366 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
1370 BRect
1371 BTextControl::LabelLayoutItem::FrameInParent() const
1373 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
1377 status_t
1378 BTextControl::LabelLayoutItem::Archive(BMessage* into, bool deep) const
1380 BArchiver archiver(into);
1381 status_t err = BAbstractLayoutItem::Archive(into, deep);
1382 if (err == B_OK)
1383 err = into->AddRect(kFrameField, fFrame);
1385 return archiver.Finish(err);
1389 BArchivable*
1390 BTextControl::LabelLayoutItem::Instantiate(BMessage* from)
1392 if (validate_instantiation(from, "BTextControl::LabelLayoutItem"))
1393 return new LabelLayoutItem(from);
1394 return NULL;
1398 // #pragma mark - BTextControl::TextViewLayoutItem
1401 BTextControl::TextViewLayoutItem::TextViewLayoutItem(BTextControl* parent)
1403 fParent(parent),
1404 fFrame()
1406 // by default the part right of the divider shall have an unlimited maximum
1407 // width
1408 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
1412 BTextControl::TextViewLayoutItem::TextViewLayoutItem(BMessage* from)
1414 BAbstractLayoutItem(from),
1415 fParent(NULL),
1416 fFrame()
1418 from->FindRect(kFrameField, &fFrame);
1422 bool
1423 BTextControl::TextViewLayoutItem::IsVisible()
1425 return !fParent->IsHidden(fParent);
1429 void
1430 BTextControl::TextViewLayoutItem::SetVisible(bool visible)
1432 // not allowed
1436 BRect
1437 BTextControl::TextViewLayoutItem::Frame()
1439 return fFrame;
1443 void
1444 BTextControl::TextViewLayoutItem::SetFrame(BRect frame)
1446 fFrame = frame;
1447 fParent->_UpdateFrame();
1451 void
1452 BTextControl::TextViewLayoutItem::SetParent(BTextControl* parent)
1454 fParent = parent;
1458 BView*
1459 BTextControl::TextViewLayoutItem::View()
1461 return fParent;
1465 BSize
1466 BTextControl::TextViewLayoutItem::BaseMinSize()
1468 fParent->_ValidateLayoutData();
1470 BSize size = fParent->fLayoutData->text_view_min;
1471 size.width += 2 * kFrameMargin;
1472 size.height += 2 * kFrameMargin;
1474 return size;
1478 BSize
1479 BTextControl::TextViewLayoutItem::BaseMaxSize()
1481 BSize size(BaseMinSize());
1482 size.width = B_SIZE_UNLIMITED;
1484 return size;
1488 BSize
1489 BTextControl::TextViewLayoutItem::BasePreferredSize()
1491 BSize size(BaseMinSize());
1492 // puh, no idea...
1493 size.width = 100;
1495 return size;
1499 BAlignment
1500 BTextControl::TextViewLayoutItem::BaseAlignment()
1502 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
1506 BRect
1507 BTextControl::TextViewLayoutItem::FrameInParent() const
1509 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
1513 status_t
1514 BTextControl::TextViewLayoutItem::Archive(BMessage* into, bool deep) const
1516 BArchiver archiver(into);
1517 status_t err = BAbstractLayoutItem::Archive(into, deep);
1518 if (err == B_OK)
1519 err = into->AddRect(kFrameField, fFrame);
1521 return archiver.Finish(err);
1525 BArchivable*
1526 BTextControl::TextViewLayoutItem::Instantiate(BMessage* from)
1528 if (validate_instantiation(from, "BTextControl::TextViewLayoutItem"))
1529 return new TextViewLayoutItem(from);
1531 return NULL;
1535 extern "C" void
1536 B_IF_GCC_2(InvalidateLayout__12BTextControlb,
1537 _ZN12BTextControl16InvalidateLayoutEb)(BView* view, bool descendants)
1539 perform_data_layout_invalidated data;
1540 data.descendants = descendants;
1542 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);