btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / interface / TextControl.cpp
bloba6a88049a23931060c911edeaa92f194105081e5
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 }
65 { 0 }
69 class BTextControl::LabelLayoutItem : public BAbstractLayoutItem {
70 public:
71 LabelLayoutItem(BTextControl* parent);
72 LabelLayoutItem(BMessage* from);
74 virtual bool IsVisible();
75 virtual void SetVisible(bool visible);
77 virtual BRect Frame();
78 virtual void SetFrame(BRect frame);
80 void SetParent(BTextControl* parent);
81 virtual BView* View();
83 virtual BSize BaseMinSize();
84 virtual BSize BaseMaxSize();
85 virtual BSize BasePreferredSize();
86 virtual BAlignment BaseAlignment();
88 BRect FrameInParent() const;
90 virtual status_t Archive(BMessage* into, bool deep = true) const;
91 static BArchivable* Instantiate(BMessage* from);
93 private:
94 BTextControl* fParent;
95 BRect fFrame;
99 class BTextControl::TextViewLayoutItem : public BAbstractLayoutItem {
100 public:
101 TextViewLayoutItem(BTextControl* parent);
102 TextViewLayoutItem(BMessage* from);
104 virtual bool IsVisible();
105 virtual void SetVisible(bool visible);
107 virtual BRect Frame();
108 virtual void SetFrame(BRect frame);
110 void SetParent(BTextControl* parent);
111 virtual BView* View();
113 virtual BSize BaseMinSize();
114 virtual BSize BaseMaxSize();
115 virtual BSize BasePreferredSize();
116 virtual BAlignment BaseAlignment();
118 BRect FrameInParent() const;
120 virtual status_t Archive(BMessage* into, bool deep = true) const;
121 static BArchivable* Instantiate(BMessage* from);
122 private:
123 BTextControl* fParent;
124 BRect fFrame;
128 struct BTextControl::LayoutData {
129 LayoutData(float width, float height)
131 label_layout_item(NULL),
132 text_view_layout_item(NULL),
133 previous_width(width),
134 previous_height(height),
135 valid(false)
139 LabelLayoutItem* label_layout_item;
140 TextViewLayoutItem* text_view_layout_item;
141 float previous_width; // used in FrameResized() for
142 float previous_height; // invalidation
143 font_height font_info;
144 float label_width;
145 float label_height;
146 BSize min;
147 BSize text_view_min;
148 bool valid;
152 static const int32 kFrameMargin = 2;
153 static const int32 kLabelInputSpacing = 3;
156 // #pragma mark - BTextControl
159 BTextControl::BTextControl(BRect frame, const char* name, const char* label,
160 const char* text, BMessage* message, uint32 resizeMask, uint32 flags)
162 BControl(frame, name, label, message, resizeMask, flags | B_FRAME_EVENTS)
164 _InitData(label);
165 _InitText(text);
166 _ValidateLayout();
170 BTextControl::BTextControl(const char* name, const char* label,
171 const char* text, BMessage* message, uint32 flags)
173 BControl(name, label, message, flags | B_FRAME_EVENTS)
175 _InitData(label);
176 _InitText(text);
177 _ValidateLayout();
181 BTextControl::BTextControl(const char* label, const char* text,
182 BMessage* message)
184 BControl(NULL, label, message,
185 B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS)
187 _InitData(label);
188 _InitText(text);
189 _ValidateLayout();
193 BTextControl::~BTextControl()
195 SetModificationMessage(NULL);
196 delete fLayoutData;
200 // #pragma mark - Archiving
203 BTextControl::BTextControl(BMessage* archive)
205 BControl(BUnarchiver::PrepareArchive(archive))
207 BUnarchiver unarchiver(archive);
209 _InitData(Label(), archive);
211 if (!BUnarchiver::IsArchiveManaged(archive))
212 _InitText(NULL, archive);
214 status_t err = B_OK;
215 if (archive->HasFloat("_divide"))
216 err = archive->FindFloat("_divide", &fDivider);
218 if (err == B_OK && archive->HasMessage("_mod_msg")) {
219 BMessage* message = new BMessage;
220 err = archive->FindMessage("_mod_msg", message);
221 SetModificationMessage(message);
224 unarchiver.Finish(err);
228 BArchivable*
229 BTextControl::Instantiate(BMessage* archive)
231 if (validate_instantiation(archive, "BTextControl"))
232 return new BTextControl(archive);
234 return NULL;
238 status_t
239 BTextControl::Archive(BMessage* data, bool deep) const
241 BArchiver archiver(data);
242 status_t result = BControl::Archive(data, deep);
244 alignment labelAlignment;
245 alignment textAlignment;
246 if (result == B_OK)
247 GetAlignment(&labelAlignment, &textAlignment);
249 if (result == B_OK)
250 result = data->AddInt32("_a_label", labelAlignment);
252 if (result == B_OK)
253 result = data->AddInt32("_a_text", textAlignment);
255 if (result == B_OK)
256 result = data->AddFloat("_divide", Divider());
258 if (result == B_OK && ModificationMessage() != NULL)
259 result = data->AddMessage("_mod_msg", ModificationMessage());
261 return archiver.Finish(result);
265 status_t
266 BTextControl::AllArchived(BMessage* into) const
268 BArchiver archiver(into);
269 status_t err = B_OK;
271 if (archiver.IsArchived(fLayoutData->text_view_layout_item)) {
272 err = archiver.AddArchivable(kTextViewItemField,
273 fLayoutData->text_view_layout_item);
276 if (err == B_OK && archiver.IsArchived(fLayoutData->label_layout_item)) {
277 err = archiver.AddArchivable(kLabelItemField,
278 fLayoutData->label_layout_item);
281 return err;
285 status_t
286 BTextControl::AllUnarchived(const BMessage* from)
288 status_t err;
289 if ((err = BControl::AllUnarchived(from)) != B_OK)
290 return err;
292 _InitText(NULL, from);
294 BUnarchiver unarchiver(from);
295 if (unarchiver.IsInstantiated(kTextViewItemField)) {
296 err = unarchiver.FindObject(kTextViewItemField,
297 BUnarchiver::B_DONT_ASSUME_OWNERSHIP,
298 fLayoutData->text_view_layout_item);
300 if (err == B_OK)
301 fLayoutData->text_view_layout_item->SetParent(this);
302 else
303 return err;
306 if (unarchiver.IsInstantiated(kLabelItemField)) {
307 err = unarchiver.FindObject(kLabelItemField,
308 BUnarchiver::B_DONT_ASSUME_OWNERSHIP,
309 fLayoutData->label_layout_item);
311 if (err == B_OK)
312 fLayoutData->label_layout_item->SetParent(this);
314 return err;
318 // #pragma mark - Hook methods
321 void
322 BTextControl::AllAttached()
324 BControl::AllAttached();
328 void
329 BTextControl::AllDetached()
331 BControl::AllDetached();
335 void
336 BTextControl::AttachedToWindow()
338 BControl::AttachedToWindow();
340 _UpdateTextViewColors(IsEnabled());
341 fText->MakeEditable(IsEnabled());
345 void
346 BTextControl::DetachedFromWindow()
348 BControl::DetachedFromWindow();
352 void
353 BTextControl::Draw(BRect updateRect)
355 bool enabled = IsEnabled();
356 bool active = fText->IsFocus() && Window()->IsActive();
358 BRect rect = fText->Frame();
359 rect.InsetBy(-2, -2);
361 rgb_color base = ViewColor();
362 uint32 flags = fLook;
363 if (!enabled)
364 flags |= BControlLook::B_DISABLED;
366 if (active)
367 flags |= BControlLook::B_FOCUSED;
369 be_control_look->DrawTextControlBorder(this, rect, updateRect, base,
370 flags);
372 if (Label() != NULL) {
373 if (fLayoutData->label_layout_item != NULL) {
374 rect = fLayoutData->label_layout_item->FrameInParent();
375 } else {
376 rect = Bounds();
377 rect.right = fDivider - kLabelInputSpacing;
380 // erase the is control flag before drawing the label so that the label
381 // will get drawn using B_PANEL_TEXT_COLOR
382 flags &= ~BControlLook::B_IS_CONTROL;
384 be_control_look->DrawLabel(this, Label(), rect, updateRect,
385 base, flags, BAlignment(fLabelAlign, B_ALIGN_MIDDLE));
390 void
391 BTextControl::FrameMoved(BPoint newPosition)
393 BControl::FrameMoved(newPosition);
397 void
398 BTextControl::FrameResized(float width, float height)
400 CALLED();
402 BControl::FrameResized(width, height);
404 // TODO: this causes flickering still...
406 // changes in width
408 BRect bounds = Bounds();
410 if (bounds.Width() > fLayoutData->previous_width) {
411 // invalidate the region between the old and the new right border
412 BRect rect = bounds;
413 rect.left += fLayoutData->previous_width - kFrameMargin;
414 rect.right--;
415 Invalidate(rect);
416 } else if (bounds.Width() < fLayoutData->previous_width) {
417 // invalidate the region of the new right border
418 BRect rect = bounds;
419 rect.left = rect.right - kFrameMargin;
420 Invalidate(rect);
423 // changes in height
425 if (bounds.Height() > fLayoutData->previous_height) {
426 // invalidate the region between the old and the new bottom border
427 BRect rect = bounds;
428 rect.top += fLayoutData->previous_height - kFrameMargin;
429 rect.bottom--;
430 Invalidate(rect);
431 // invalidate label area
432 rect = bounds;
433 rect.right = fDivider;
434 Invalidate(rect);
435 } else if (bounds.Height() < fLayoutData->previous_height) {
436 // invalidate the region of the new bottom border
437 BRect rect = bounds;
438 rect.top = rect.bottom - kFrameMargin;
439 Invalidate(rect);
440 // invalidate label area
441 rect = bounds;
442 rect.right = fDivider;
443 Invalidate(rect);
446 fLayoutData->previous_width = bounds.Width();
447 fLayoutData->previous_height = bounds.Height();
449 TRACE("width: %.2f, height: %.2f\n", bounds.Width(), bounds.Height());
453 status_t
454 BTextControl::Invoke(BMessage* message)
456 return BControl::Invoke(message);
460 void
461 BTextControl::LayoutInvalidated(bool descendants)
463 CALLED();
465 fLayoutData->valid = false;
469 void
470 BTextControl::MessageReceived(BMessage* message)
472 if (message->what == B_COLORS_UPDATED) {
474 if (message->HasColor(ui_color_name(B_PANEL_BACKGROUND_COLOR))
475 || message->HasColor(ui_color_name(B_PANEL_TEXT_COLOR))
476 || message->HasColor(ui_color_name(B_DOCUMENT_BACKGROUND_COLOR))
477 || message->HasColor(ui_color_name(B_DOCUMENT_TEXT_COLOR))) {
478 _UpdateTextViewColors(IsEnabled());
482 if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) {
483 BMessage reply(B_REPLY);
484 bool handled = false;
486 BMessage specifier;
487 int32 index;
488 int32 form;
489 const char* property;
490 if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) {
491 if (strcmp(property, "Value") == 0) {
492 if (message->what == B_GET_PROPERTY) {
493 reply.AddString("result", fText->Text());
494 handled = true;
495 } else {
496 const char* value = NULL;
497 // B_SET_PROPERTY
498 if (message->FindString("data", &value) == B_OK) {
499 fText->SetText(value);
500 reply.AddInt32("error", B_OK);
501 handled = true;
507 if (handled) {
508 message->SendReply(&reply);
509 return;
513 BControl::MessageReceived(message);
517 void
518 BTextControl::MouseDown(BPoint where)
520 if (!fText->IsFocus())
521 fText->MakeFocus(true);
525 void
526 BTextControl::MouseMoved(BPoint where, uint32 transit,
527 const BMessage* dragMessage)
529 BControl::MouseMoved(where, transit, dragMessage);
533 void
534 BTextControl::MouseUp(BPoint where)
536 BControl::MouseUp(where);
540 void
541 BTextControl::WindowActivated(bool active)
543 if (fText->IsFocus()) {
544 // invalidate to remove/show focus indication
545 BRect rect = fText->Frame();
546 rect.InsetBy(-1, -1);
547 Invalidate(rect);
549 // help out embedded text view which doesn't
550 // get notified of this
551 fText->Invalidate();
556 // #pragma mark - Getters and Setters
559 void
560 BTextControl::SetText(const char* text)
562 if (InvokeKind() != B_CONTROL_INVOKED)
563 return;
565 CALLED();
567 fText->SetText(text);
569 if (fText->IsFocus()) {
570 fText->SetInitialText();
571 fText->SelectAll();
574 fText->Invalidate();
578 const char*
579 BTextControl::Text() const
581 return fText->Text();
585 int32
586 BTextControl::TextLength() const
588 return fText->TextLength();
592 void
593 BTextControl::MarkAsInvalid(bool invalid)
595 uint32 look = fLook;
597 if (invalid)
598 fLook |= BControlLook::B_INVALID;
599 else
600 fLook &= ~BControlLook::B_INVALID;
602 if (look != fLook)
603 Invalidate();
607 void
608 BTextControl::SetValue(int32 value)
610 BControl::SetValue(value);
614 BTextView*
615 BTextControl::TextView() const
617 return fText;
621 void
622 BTextControl::SetModificationMessage(BMessage* message)
624 delete fModificationMessage;
625 fModificationMessage = message;
629 BMessage*
630 BTextControl::ModificationMessage() const
632 return fModificationMessage;
636 void
637 BTextControl::SetAlignment(alignment labelAlignment, alignment textAlignment)
639 fText->SetAlignment(textAlignment);
640 fText->AlignTextRect();
642 if (fLabelAlign != labelAlignment) {
643 fLabelAlign = labelAlignment;
644 Invalidate();
649 void
650 BTextControl::GetAlignment(alignment* _label, alignment* _text) const
652 if (_label != NULL)
653 *_label = fLabelAlign;
655 if (_text != NULL)
656 *_text = fText->Alignment();
660 void
661 BTextControl::SetDivider(float position)
663 fDivider = floorf(position + 0.5);
665 _LayoutTextView();
667 if (Window()) {
668 fText->Invalidate();
669 Invalidate();
674 float
675 BTextControl::Divider() const
677 return fDivider;
681 void
682 BTextControl::MakeFocus(bool state)
684 if (state != fText->IsFocus()) {
685 fText->MakeFocus(state);
687 if (state)
688 fText->SelectAll();
693 void
694 BTextControl::SetEnabled(bool enable)
696 if (IsEnabled() == enable)
697 return;
699 if (Window() != NULL) {
700 fText->MakeEditable(enable);
701 if (enable)
702 fText->SetFlags(fText->Flags() | B_NAVIGABLE);
703 else
704 fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
706 _UpdateTextViewColors(enable);
708 fText->Invalidate();
709 Window()->UpdateIfNeeded();
712 BControl::SetEnabled(enable);
716 void
717 BTextControl::GetPreferredSize(float* _width, float* _height)
719 CALLED();
721 _ValidateLayoutData();
723 if (_width) {
724 float minWidth = fLayoutData->min.width;
725 if (Label() == NULL && !(Flags() & B_SUPPORTS_LAYOUT)) {
726 // Indeed, only if there is no label! BeOS backwards compatible
727 // behavior:
728 minWidth = max_c(minWidth, Bounds().Width());
730 *_width = minWidth;
733 if (_height)
734 *_height = fLayoutData->min.height;
738 void
739 BTextControl::ResizeToPreferred()
741 BView::ResizeToPreferred();
743 fDivider = 0.0;
744 const char* label = Label();
745 if (label)
746 fDivider = ceil(StringWidth(label)) + 2.0;
748 _LayoutTextView();
752 void
753 BTextControl::SetFlags(uint32 flags)
755 // If the textview is navigable, set it to not navigable if needed
756 // Else if it is not navigable, set it to navigable if needed
757 if (fText->Flags() & B_NAVIGABLE) {
758 if (!(flags & B_NAVIGABLE))
759 fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
761 } else {
762 if (flags & B_NAVIGABLE)
763 fText->SetFlags(fText->Flags() | B_NAVIGABLE);
766 // Don't make this one navigable
767 flags &= ~B_NAVIGABLE;
769 BView::SetFlags(flags);
773 // #pragma mark - Scripting
776 BHandler*
777 BTextControl::ResolveSpecifier(BMessage* message, int32 index,
778 BMessage* specifier, int32 what, const char* property)
780 BPropertyInfo propInfo(sPropertyList);
782 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
783 return this;
785 return BControl::ResolveSpecifier(message, index, specifier, what,
786 property);
790 status_t
791 BTextControl::GetSupportedSuites(BMessage* data)
793 return BControl::GetSupportedSuites(data);
797 // #pragma mark - Layout
800 BSize
801 BTextControl::MinSize()
803 CALLED();
805 _ValidateLayoutData();
806 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min);
810 BSize
811 BTextControl::MaxSize()
813 CALLED();
815 _ValidateLayoutData();
817 BSize max = fLayoutData->min;
818 max.width = B_SIZE_UNLIMITED;
820 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
824 BSize
825 BTextControl::PreferredSize()
827 CALLED();
829 _ValidateLayoutData();
830 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData->min);
834 BAlignment
835 BTextControl::LayoutAlignment()
837 CALLED();
839 _ValidateLayoutData();
840 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
841 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER));
845 BLayoutItem*
846 BTextControl::CreateLabelLayoutItem()
848 if (!fLayoutData->label_layout_item)
849 fLayoutData->label_layout_item = new LabelLayoutItem(this);
851 return fLayoutData->label_layout_item;
855 BLayoutItem*
856 BTextControl::CreateTextViewLayoutItem()
858 if (!fLayoutData->text_view_layout_item)
859 fLayoutData->text_view_layout_item = new TextViewLayoutItem(this);
861 return fLayoutData->text_view_layout_item;
865 void
866 BTextControl::DoLayout()
868 // Bail out, if we shan't do layout.
869 if (!(Flags() & B_SUPPORTS_LAYOUT))
870 return;
872 CALLED();
874 // If the user set a layout, we let the base class version call its
875 // hook.
876 if (GetLayout()) {
877 BView::DoLayout();
878 return;
881 _ValidateLayoutData();
883 // validate current size
884 BSize size(Bounds().Size());
885 if (size.width < fLayoutData->min.width)
886 size.width = fLayoutData->min.width;
888 if (size.height < fLayoutData->min.height)
889 size.height = fLayoutData->min.height;
891 BRect dirty(fText->Frame());
892 BRect textFrame;
894 // divider
895 float divider = 0;
896 if (fLayoutData->text_view_layout_item != NULL) {
897 if (fLayoutData->label_layout_item != NULL) {
898 // We have layout items. They define the divider location.
899 divider = fabs(fLayoutData->text_view_layout_item->Frame().left
900 - fLayoutData->label_layout_item->Frame().left);
902 textFrame = fLayoutData->text_view_layout_item->FrameInParent();
903 } else {
904 if (fLayoutData->label_width > 0) {
905 divider = fLayoutData->label_width
906 + be_control_look->DefaultLabelSpacing();
908 textFrame.Set(divider, 0, size.width, size.height);
911 // place the text view and set the divider
912 textFrame.InsetBy(kFrameMargin, kFrameMargin);
913 BLayoutUtils::AlignInFrame(fText, textFrame);
915 fDivider = divider;
917 // invalidate dirty region
918 dirty = dirty | fText->Frame();
919 dirty.InsetBy(-kFrameMargin, -kFrameMargin);
921 Invalidate(dirty);
925 // #pragma mark - protected methods
928 status_t
929 BTextControl::SetIcon(const BBitmap* icon, uint32 flags)
931 return BControl::SetIcon(icon, flags);
935 // #pragma mark - private methods
938 status_t
939 BTextControl::Perform(perform_code code, void* _data)
941 switch (code) {
942 case PERFORM_CODE_MIN_SIZE:
943 ((perform_data_min_size*)_data)->return_value
944 = BTextControl::MinSize();
945 return B_OK;
947 case PERFORM_CODE_MAX_SIZE:
948 ((perform_data_max_size*)_data)->return_value
949 = BTextControl::MaxSize();
950 return B_OK;
952 case PERFORM_CODE_PREFERRED_SIZE:
953 ((perform_data_preferred_size*)_data)->return_value
954 = BTextControl::PreferredSize();
955 return B_OK;
957 case PERFORM_CODE_LAYOUT_ALIGNMENT:
958 ((perform_data_layout_alignment*)_data)->return_value
959 = BTextControl::LayoutAlignment();
960 return B_OK;
962 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
963 ((perform_data_has_height_for_width*)_data)->return_value
964 = BTextControl::HasHeightForWidth();
965 return B_OK;
967 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
969 perform_data_get_height_for_width* data
970 = (perform_data_get_height_for_width*)_data;
971 BTextControl::GetHeightForWidth(data->width, &data->min, &data->max,
972 &data->preferred);
973 return B_OK;
976 case PERFORM_CODE_SET_LAYOUT:
978 perform_data_set_layout* data = (perform_data_set_layout*)_data;
979 BTextControl::SetLayout(data->layout);
980 return B_OK;
983 case PERFORM_CODE_LAYOUT_INVALIDATED:
985 perform_data_layout_invalidated* data
986 = (perform_data_layout_invalidated*)_data;
987 BTextControl::LayoutInvalidated(data->descendants);
988 return B_OK;
991 case PERFORM_CODE_DO_LAYOUT:
993 BTextControl::DoLayout();
994 return B_OK;
997 case PERFORM_CODE_SET_ICON:
999 perform_data_set_icon* data = (perform_data_set_icon*)_data;
1000 return BTextControl::SetIcon(data->icon, data->flags);
1003 case PERFORM_CODE_ALL_UNARCHIVED:
1005 perform_data_all_unarchived* data
1006 = (perform_data_all_unarchived*)_data;
1007 data->return_value = BTextControl::AllUnarchived(data->archive);
1008 return B_OK;
1011 case PERFORM_CODE_ALL_ARCHIVED:
1013 perform_data_all_archived* data
1014 = (perform_data_all_archived*)_data;
1015 data->return_value = BTextControl::AllArchived(data->archive);
1016 return B_OK;
1020 return BControl::Perform(code, _data);
1024 // #pragma mark - FBC padding
1027 void BTextControl::_ReservedTextControl1() {}
1028 void BTextControl::_ReservedTextControl2() {}
1029 void BTextControl::_ReservedTextControl3() {}
1030 void BTextControl::_ReservedTextControl4() {}
1033 BTextControl&
1034 BTextControl::operator=(const BTextControl&)
1036 return *this;
1040 void
1041 BTextControl::_UpdateTextViewColors(bool enable)
1043 rgb_color textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
1044 rgb_color viewColor = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
1045 BFont font;
1047 fText->GetFontAndColor(0, &font);
1049 if (!enable) {
1050 textColor = disable_color(textColor, ViewColor());
1051 viewColor = disable_color(ViewColor(), viewColor);
1054 fText->SetFontAndColor(&font, B_FONT_ALL, &textColor);
1055 fText->SetViewColor(viewColor);
1056 fText->SetLowColor(viewColor);
1060 void
1061 BTextControl::_CommitValue()
1066 void
1067 BTextControl::_InitData(const char* label, const BMessage* archive)
1069 BRect bounds(Bounds());
1071 fText = NULL;
1072 fModificationMessage = NULL;
1073 fLabelAlign = B_ALIGN_LEFT;
1074 fDivider = 0.0f;
1075 fLayoutData = new LayoutData(bounds.Width(), bounds.Height());
1077 int32 flags = 0;
1079 BFont font(be_plain_font);
1081 if (!archive || !archive->HasString("_fname"))
1082 flags |= B_FONT_FAMILY_AND_STYLE;
1084 if (!archive || !archive->HasFloat("_fflt"))
1085 flags |= B_FONT_SIZE;
1087 if (flags != 0)
1088 SetFont(&font, flags);
1090 if (label != NULL)
1091 fDivider = floorf(bounds.Width() / 2.0f);
1093 fLook = 0;
1097 void
1098 BTextControl::_InitText(const char* initialText, const BMessage* archive)
1100 if (archive)
1101 fText = static_cast<BPrivate::_BTextInput_*>(FindView("_input_"));
1103 if (fText == NULL) {
1104 BRect bounds(Bounds());
1105 BRect frame(fDivider, bounds.top, bounds.right, bounds.bottom);
1106 // we are stroking the frame around the text view, which
1107 // is 2 pixels wide
1108 frame.InsetBy(kFrameMargin, kFrameMargin);
1109 BRect textRect(frame.OffsetToCopy(B_ORIGIN));
1111 fText = new BPrivate::_BTextInput_(frame, textRect,
1112 B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS
1113 | (Flags() & B_NAVIGABLE));
1114 AddChild(fText);
1116 SetText(initialText);
1117 fText->SetAlignment(B_ALIGN_LEFT);
1118 fText->AlignTextRect();
1121 // Although this is not strictly initializing the text view,
1122 // it cannot be done while fText is NULL, so it resides here.
1123 if (archive) {
1124 int32 labelAlignment = B_ALIGN_LEFT;
1125 int32 textAlignment = B_ALIGN_LEFT;
1127 status_t err = B_OK;
1128 if (archive->HasInt32("_a_label"))
1129 err = archive->FindInt32("_a_label", &labelAlignment);
1131 if (err == B_OK && archive->HasInt32("_a_text"))
1132 err = archive->FindInt32("_a_text", &textAlignment);
1134 SetAlignment((alignment)labelAlignment, (alignment)textAlignment);
1137 uint32 navigableFlags = Flags() & B_NAVIGABLE;
1138 if (navigableFlags != 0)
1139 BView::SetFlags(Flags() & ~B_NAVIGABLE);
1143 void
1144 BTextControl::_ValidateLayout()
1146 CALLED();
1148 _ValidateLayoutData();
1150 ResizeTo(Bounds().Width(), fLayoutData->min.height);
1152 _LayoutTextView();
1156 void
1157 BTextControl::_LayoutTextView()
1159 CALLED();
1161 BRect frame;
1162 if (fLayoutData->text_view_layout_item != NULL) {
1163 frame = fLayoutData->text_view_layout_item->FrameInParent();
1164 } else {
1165 frame = Bounds();
1166 frame.left = fDivider;
1169 // we are stroking the frame around the text view, which
1170 // is 2 pixels wide
1171 frame.InsetBy(kFrameMargin, kFrameMargin);
1172 fText->MoveTo(frame.left, frame.top);
1173 fText->ResizeTo(frame.Width(), frame.Height());
1174 fText->AlignTextRect();
1176 TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
1177 TRACE("fDivider: %.2f\n", fDivider);
1178 TRACE("fText frame: (%.2f, %.2f, %.2f, %.2f)\n",
1179 frame.left, frame.top, frame.right, frame.bottom);
1183 void
1184 BTextControl::_UpdateFrame()
1186 CALLED();
1188 if (fLayoutData->text_view_layout_item != NULL) {
1189 BRect textFrame = fLayoutData->text_view_layout_item->Frame();
1190 BRect labelFrame;
1191 if (fLayoutData->label_layout_item != NULL)
1192 labelFrame = fLayoutData->label_layout_item->Frame();
1194 BRect frame;
1195 if (labelFrame.IsValid()) {
1196 frame = textFrame | labelFrame;
1198 // update divider
1199 fDivider = fabs(textFrame.left - labelFrame.left);
1200 } else {
1201 frame = textFrame;
1202 fDivider = 0;
1205 // update our frame
1206 MoveTo(frame.left, frame.top);
1207 BSize oldSize(Bounds().Size());
1208 ResizeTo(frame.Width(), frame.Height());
1209 BSize newSize(Bounds().Size());
1211 // If the size changes, ResizeTo() will trigger a relayout, otherwise
1212 // we need to do that explicitly.
1213 if (newSize != oldSize)
1214 Relayout();
1219 void
1220 BTextControl::_ValidateLayoutData()
1222 CALLED();
1224 if (fLayoutData->valid)
1225 return;
1227 // cache font height
1228 font_height& fh = fLayoutData->font_info;
1229 GetFontHeight(&fh);
1231 const char* label = Label();
1232 if (label != NULL) {
1233 fLayoutData->label_width = ceilf(StringWidth(label));
1234 fLayoutData->label_height = ceilf(fh.ascent) + ceilf(fh.descent);
1235 } else {
1236 fLayoutData->label_width = 0;
1237 fLayoutData->label_height = 0;
1240 // compute the minimal divider
1241 float divider = 0;
1242 if (fLayoutData->label_width > 0) {
1243 divider = fLayoutData->label_width
1244 + be_control_look->DefaultLabelSpacing();
1247 // If we shan't do real layout, we let the current divider take influence.
1248 if (!(Flags() & B_SUPPORTS_LAYOUT))
1249 divider = max_c(divider, fDivider);
1251 // get the minimal (== preferred) text view size
1252 fLayoutData->text_view_min = fText->MinSize();
1254 TRACE("text view min width: %.2f\n", fLayoutData->text_view_min.width);
1256 // compute our minimal (== preferred) size
1257 BSize min(fLayoutData->text_view_min);
1258 min.width += 2 * kFrameMargin;
1259 min.height += 2 * kFrameMargin;
1261 if (divider > 0)
1262 min.width += divider;
1264 if (fLayoutData->label_height > min.height)
1265 min.height = fLayoutData->label_height;
1267 fLayoutData->min = min;
1269 fLayoutData->valid = true;
1270 ResetLayoutInvalidation();
1272 TRACE("width: %.2f, height: %.2f\n", min.width, min.height);
1276 // #pragma mark - BTextControl::LabelLayoutItem
1279 BTextControl::LabelLayoutItem::LabelLayoutItem(BTextControl* parent)
1281 fParent(parent),
1282 fFrame()
1287 BTextControl::LabelLayoutItem::LabelLayoutItem(BMessage* from)
1289 BAbstractLayoutItem(from),
1290 fParent(NULL),
1291 fFrame()
1293 from->FindRect(kFrameField, &fFrame);
1297 bool
1298 BTextControl::LabelLayoutItem::IsVisible()
1300 return !fParent->IsHidden(fParent);
1304 void
1305 BTextControl::LabelLayoutItem::SetVisible(bool visible)
1307 // not allowed
1311 BRect
1312 BTextControl::LabelLayoutItem::Frame()
1314 return fFrame;
1318 void
1319 BTextControl::LabelLayoutItem::SetFrame(BRect frame)
1321 fFrame = frame;
1322 fParent->_UpdateFrame();
1326 void
1327 BTextControl::LabelLayoutItem::SetParent(BTextControl* parent)
1329 fParent = parent;
1333 BView*
1334 BTextControl::LabelLayoutItem::View()
1336 return fParent;
1340 BSize
1341 BTextControl::LabelLayoutItem::BaseMinSize()
1343 fParent->_ValidateLayoutData();
1345 if (!fParent->Label())
1346 return BSize(-1, -1);
1348 return BSize(fParent->fLayoutData->label_width
1349 + be_control_look->DefaultLabelSpacing(),
1350 fParent->fLayoutData->label_height);
1354 BSize
1355 BTextControl::LabelLayoutItem::BaseMaxSize()
1357 return BaseMinSize();
1361 BSize
1362 BTextControl::LabelLayoutItem::BasePreferredSize()
1364 return BaseMinSize();
1368 BAlignment
1369 BTextControl::LabelLayoutItem::BaseAlignment()
1371 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
1375 BRect
1376 BTextControl::LabelLayoutItem::FrameInParent() const
1378 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
1382 status_t
1383 BTextControl::LabelLayoutItem::Archive(BMessage* into, bool deep) const
1385 BArchiver archiver(into);
1386 status_t err = BAbstractLayoutItem::Archive(into, deep);
1387 if (err == B_OK)
1388 err = into->AddRect(kFrameField, fFrame);
1390 return archiver.Finish(err);
1394 BArchivable*
1395 BTextControl::LabelLayoutItem::Instantiate(BMessage* from)
1397 if (validate_instantiation(from, "BTextControl::LabelLayoutItem"))
1398 return new LabelLayoutItem(from);
1399 return NULL;
1403 // #pragma mark - BTextControl::TextViewLayoutItem
1406 BTextControl::TextViewLayoutItem::TextViewLayoutItem(BTextControl* parent)
1408 fParent(parent),
1409 fFrame()
1411 // by default the part right of the divider shall have an unlimited maximum
1412 // width
1413 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
1417 BTextControl::TextViewLayoutItem::TextViewLayoutItem(BMessage* from)
1419 BAbstractLayoutItem(from),
1420 fParent(NULL),
1421 fFrame()
1423 from->FindRect(kFrameField, &fFrame);
1427 bool
1428 BTextControl::TextViewLayoutItem::IsVisible()
1430 return !fParent->IsHidden(fParent);
1434 void
1435 BTextControl::TextViewLayoutItem::SetVisible(bool visible)
1437 // not allowed
1441 BRect
1442 BTextControl::TextViewLayoutItem::Frame()
1444 return fFrame;
1448 void
1449 BTextControl::TextViewLayoutItem::SetFrame(BRect frame)
1451 fFrame = frame;
1452 fParent->_UpdateFrame();
1456 void
1457 BTextControl::TextViewLayoutItem::SetParent(BTextControl* parent)
1459 fParent = parent;
1463 BView*
1464 BTextControl::TextViewLayoutItem::View()
1466 return fParent;
1470 BSize
1471 BTextControl::TextViewLayoutItem::BaseMinSize()
1473 fParent->_ValidateLayoutData();
1475 BSize size = fParent->fLayoutData->text_view_min;
1476 size.width += 2 * kFrameMargin;
1477 size.height += 2 * kFrameMargin;
1479 return size;
1483 BSize
1484 BTextControl::TextViewLayoutItem::BaseMaxSize()
1486 BSize size(BaseMinSize());
1487 size.width = B_SIZE_UNLIMITED;
1489 return size;
1493 BSize
1494 BTextControl::TextViewLayoutItem::BasePreferredSize()
1496 BSize size(BaseMinSize());
1497 // puh, no idea...
1498 size.width = 100;
1500 return size;
1504 BAlignment
1505 BTextControl::TextViewLayoutItem::BaseAlignment()
1507 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
1511 BRect
1512 BTextControl::TextViewLayoutItem::FrameInParent() const
1514 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
1518 status_t
1519 BTextControl::TextViewLayoutItem::Archive(BMessage* into, bool deep) const
1521 BArchiver archiver(into);
1522 status_t err = BAbstractLayoutItem::Archive(into, deep);
1523 if (err == B_OK)
1524 err = into->AddRect(kFrameField, fFrame);
1526 return archiver.Finish(err);
1530 BArchivable*
1531 BTextControl::TextViewLayoutItem::Instantiate(BMessage* from)
1533 if (validate_instantiation(from, "BTextControl::TextViewLayoutItem"))
1534 return new TextViewLayoutItem(from);
1536 return NULL;
1540 extern "C" void
1541 B_IF_GCC_2(InvalidateLayout__12BTextControlb,
1542 _ZN12BTextControl16InvalidateLayoutEb)(BView* view, bool descendants)
1544 perform_data_layout_invalidated data;
1545 data.descendants = descendants;
1547 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);