tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / Box.cpp
blobd243a911e66e417e6f18846bf65e0b91ccb9a4a6
1 /*
2 * Copyright 2001-2013 Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Stephan Aßmus, superstippi@gmx.de
7 * DarkWyrm, bpmagic@columbus.rr.com
8 * Axel Dörfler, axeld@pinc-software.de
9 * Marc Flerackers, mflerackers@androme.be
10 * John Scipione, jscipione@gmail.com
14 #include <Box.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include <ControlLook.h>
21 #include <Layout.h>
22 #include <LayoutUtils.h>
23 #include <Message.h>
24 #include <Region.h>
26 #include <binary_compatibility/Interface.h>
29 struct BBox::LayoutData {
30 LayoutData()
31 : valid(false)
35 BRect label_box; // label box (label string or label view); in case
36 // of a label string not including descent
37 BRect insets; // insets induced by border and label
38 BSize min;
39 BSize max;
40 BSize preferred;
41 BAlignment alignment;
42 bool valid; // validity the other fields
46 BBox::BBox(BRect frame, const char* name, uint32 resizingMode, uint32 flags,
47 border_style border)
49 BView(frame, name, resizingMode, flags | B_WILL_DRAW | B_FRAME_EVENTS),
50 fStyle(border)
52 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
53 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
55 _InitObject();
59 BBox::BBox(const char* name, uint32 flags, border_style border, BView* child)
61 BView(name, flags | B_WILL_DRAW | B_FRAME_EVENTS),
62 fStyle(border)
64 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
65 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
67 _InitObject();
69 if (child)
70 AddChild(child);
74 BBox::BBox(border_style border, BView* child)
76 BView(NULL, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP),
77 fStyle(border)
79 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
80 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
82 _InitObject();
84 if (child)
85 AddChild(child);
89 BBox::BBox(BMessage* archive)
91 BView(archive),
92 fStyle(B_FANCY_BORDER)
94 _InitObject(archive);
98 BBox::~BBox()
100 _ClearLabel();
102 delete fLayoutData;
106 BArchivable*
107 BBox::Instantiate(BMessage* archive)
109 if (validate_instantiation(archive, "BBox"))
110 return new BBox(archive);
112 return NULL;
116 status_t
117 BBox::Archive(BMessage* archive, bool deep) const
119 status_t ret = BView::Archive(archive, deep);
121 if (fLabel && ret == B_OK)
122 ret = archive->AddString("_label", fLabel);
124 if (fLabelView && ret == B_OK)
125 ret = archive->AddBool("_lblview", true);
127 if (fStyle != B_FANCY_BORDER && ret == B_OK)
128 ret = archive->AddInt32("_style", fStyle);
130 return ret;
134 void
135 BBox::SetBorder(border_style border)
137 if (border == fStyle)
138 return;
140 fStyle = border;
142 InvalidateLayout();
144 if (Window() != NULL && LockLooper()) {
145 Invalidate();
146 UnlockLooper();
151 border_style
152 BBox::Border() const
154 return fStyle;
158 //! This function is not part of the R5 API and is not yet finalized yet
159 float
160 BBox::TopBorderOffset()
162 _ValidateLayoutData();
164 if (fLabel != NULL || fLabelView != NULL)
165 return fLayoutData->label_box.Height() / 2;
167 return 0;
171 //! This function is not part of the R5 API and is not yet finalized yet
172 BRect
173 BBox::InnerFrame()
175 _ValidateLayoutData();
177 BRect frame(Bounds());
178 frame.left += fLayoutData->insets.left;
179 frame.top += fLayoutData->insets.top;
180 frame.right -= fLayoutData->insets.right;
181 frame.bottom -= fLayoutData->insets.bottom;
183 return frame;
187 void
188 BBox::SetLabel(const char* string)
190 _ClearLabel();
192 if (string)
193 fLabel = strdup(string);
195 InvalidateLayout();
197 if (Window())
198 Invalidate();
202 status_t
203 BBox::SetLabel(BView* viewLabel)
205 _ClearLabel();
207 if (viewLabel) {
208 fLabelView = viewLabel;
209 fLabelView->MoveTo(10.0f, 0.0f);
210 AddChild(fLabelView, ChildAt(0));
213 InvalidateLayout();
215 if (Window())
216 Invalidate();
218 return B_OK;
222 const char*
223 BBox::Label() const
225 return fLabel;
229 BView*
230 BBox::LabelView() const
232 return fLabelView;
236 void
237 BBox::Draw(BRect updateRect)
239 _ValidateLayoutData();
241 PushState();
243 BRect labelBox = BRect(0, 0, 0, 0);
244 if (fLabel != NULL) {
245 labelBox = fLayoutData->label_box;
246 BRegion update(updateRect);
247 update.Exclude(labelBox);
249 ConstrainClippingRegion(&update);
250 } else if (fLabelView != NULL)
251 labelBox = fLabelView->Bounds();
253 switch (fStyle) {
254 case B_FANCY_BORDER:
255 _DrawFancy(labelBox);
256 break;
258 case B_PLAIN_BORDER:
259 _DrawPlain(labelBox);
260 break;
262 default:
263 break;
266 if (fLabel) {
267 ConstrainClippingRegion(NULL);
269 font_height fontHeight;
270 GetFontHeight(&fontHeight);
272 SetHighColor(0, 0, 0);
273 DrawString(fLabel, BPoint(10.0f, ceilf(fontHeight.ascent)));
276 PopState();
280 void
281 BBox::AttachedToWindow()
283 BView* parent = Parent();
284 if (parent != NULL) {
285 // inherit the color from parent
286 rgb_color color = parent->ViewColor();
287 if (color == B_TRANSPARENT_COLOR)
288 color = ui_color(B_PANEL_BACKGROUND_COLOR);
290 SetViewColor(color);
291 SetLowColor(color);
294 // The box could have been resized in the mean time
295 fBounds = Bounds();
299 void
300 BBox::DetachedFromWindow()
302 BView::DetachedFromWindow();
306 void
307 BBox::AllAttached()
309 BView::AllAttached();
313 void
314 BBox::AllDetached()
316 BView::AllDetached();
320 void
321 BBox::FrameResized(float width, float height)
323 BRect bounds(Bounds());
325 // invalidate the regions that the app_server did not
326 // (for removing the previous or drawing the new border)
327 if (fStyle != B_NO_BORDER) {
328 // TODO: this must be made part of the be_control_look stuff!
329 int32 borderSize = fStyle == B_PLAIN_BORDER ? 0 : 2;
331 BRect invalid(bounds);
332 if (fBounds.right < bounds.right) {
333 // enlarging
334 invalid.left = fBounds.right - borderSize;
335 invalid.right = fBounds.right;
337 Invalidate(invalid);
338 } else if (fBounds.right > bounds.right) {
339 // shrinking
340 invalid.left = bounds.right - borderSize;
342 Invalidate(invalid);
345 invalid = bounds;
346 if (fBounds.bottom < bounds.bottom) {
347 // enlarging
348 invalid.top = fBounds.bottom - borderSize;
349 invalid.bottom = fBounds.bottom;
351 Invalidate(invalid);
352 } else if (fBounds.bottom > bounds.bottom) {
353 // shrinking
354 invalid.top = bounds.bottom - borderSize;
356 Invalidate(invalid);
360 fBounds.right = bounds.right;
361 fBounds.bottom = bounds.bottom;
365 void
366 BBox::MessageReceived(BMessage* message)
368 BView::MessageReceived(message);
372 void
373 BBox::MouseDown(BPoint point)
375 BView::MouseDown(point);
379 void
380 BBox::MouseUp(BPoint point)
382 BView::MouseUp(point);
386 void
387 BBox::WindowActivated(bool active)
389 BView::WindowActivated(active);
393 void
394 BBox::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
396 BView::MouseMoved(point, transit, message);
400 void
401 BBox::FrameMoved(BPoint newLocation)
403 BView::FrameMoved(newLocation);
407 BHandler*
408 BBox::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
409 int32 what, const char* property)
411 return BView::ResolveSpecifier(message, index, specifier, what, property);
415 void
416 BBox::ResizeToPreferred()
418 float width, height;
419 GetPreferredSize(&width, &height);
421 // make sure the box don't get smaller than it already is
422 if (width < Bounds().Width())
423 width = Bounds().Width();
424 if (height < Bounds().Height())
425 height = Bounds().Height();
427 BView::ResizeTo(width, height);
431 void
432 BBox::GetPreferredSize(float* _width, float* _height)
434 _ValidateLayoutData();
436 if (_width)
437 *_width = fLayoutData->preferred.width;
438 if (_height)
439 *_height = fLayoutData->preferred.height;
443 void
444 BBox::MakeFocus(bool focused)
446 BView::MakeFocus(focused);
450 status_t
451 BBox::GetSupportedSuites(BMessage* message)
453 return BView::GetSupportedSuites(message);
457 status_t
458 BBox::Perform(perform_code code, void* _data)
460 switch (code) {
461 case PERFORM_CODE_MIN_SIZE:
462 ((perform_data_min_size*)_data)->return_value
463 = BBox::MinSize();
464 return B_OK;
465 case PERFORM_CODE_MAX_SIZE:
466 ((perform_data_max_size*)_data)->return_value
467 = BBox::MaxSize();
468 return B_OK;
469 case PERFORM_CODE_PREFERRED_SIZE:
470 ((perform_data_preferred_size*)_data)->return_value
471 = BBox::PreferredSize();
472 return B_OK;
473 case PERFORM_CODE_LAYOUT_ALIGNMENT:
474 ((perform_data_layout_alignment*)_data)->return_value
475 = BBox::LayoutAlignment();
476 return B_OK;
477 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
478 ((perform_data_has_height_for_width*)_data)->return_value
479 = BBox::HasHeightForWidth();
480 return B_OK;
481 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
483 perform_data_get_height_for_width* data
484 = (perform_data_get_height_for_width*)_data;
485 BBox::GetHeightForWidth(data->width, &data->min, &data->max,
486 &data->preferred);
487 return B_OK;
489 case PERFORM_CODE_SET_LAYOUT:
491 perform_data_set_layout* data = (perform_data_set_layout*)_data;
492 BBox::SetLayout(data->layout);
493 return B_OK;
495 case PERFORM_CODE_LAYOUT_INVALIDATED:
497 perform_data_layout_invalidated* data
498 = (perform_data_layout_invalidated*)_data;
499 BBox::LayoutInvalidated(data->descendants);
500 return B_OK;
502 case PERFORM_CODE_DO_LAYOUT:
504 BBox::DoLayout();
505 return B_OK;
509 return BView::Perform(code, _data);
513 BSize
514 BBox::MinSize()
516 _ValidateLayoutData();
518 BSize size = (GetLayout() ? GetLayout()->MinSize() : fLayoutData->min);
519 if (size.width < fLayoutData->min.width)
520 size.width = fLayoutData->min.width;
521 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
525 BSize
526 BBox::MaxSize()
528 _ValidateLayoutData();
530 BSize size = (GetLayout() ? GetLayout()->MaxSize() : fLayoutData->max);
531 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
535 BSize
536 BBox::PreferredSize()
538 _ValidateLayoutData();
540 BSize size = (GetLayout() ? GetLayout()->PreferredSize()
541 : fLayoutData->preferred);
542 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
546 BAlignment
547 BBox::LayoutAlignment()
549 _ValidateLayoutData();
551 BAlignment alignment = (GetLayout() ? GetLayout()->Alignment()
552 : fLayoutData->alignment);
553 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), alignment);
557 void
558 BBox::LayoutInvalidated(bool descendants)
560 fLayoutData->valid = false;
564 void
565 BBox::DoLayout()
567 // Bail out, if we shan't do layout.
568 if (!(Flags() & B_SUPPORTS_LAYOUT))
569 return;
571 BLayout* layout = GetLayout();
573 // If the user set a layout, let the base class version call its
574 // hook. In case when we have BView as a label, remove it from child list
575 // so it won't be layouted with the rest of views and add it again
576 // after that.
577 if (layout != NULL) {
578 if (fLabelView)
579 RemoveChild(fLabelView);
581 BView::DoLayout();
583 if (fLabelView != NULL) {
584 DisableLayoutInvalidation();
585 // don't trigger a relayout
586 AddChild(fLabelView, ChildAt(0));
587 EnableLayoutInvalidation();
591 _ValidateLayoutData();
593 // Even if the user set a layout, restore label view to it's
594 // desired position.
596 // layout the label view
597 if (fLabelView != NULL) {
598 fLabelView->MoveTo(fLayoutData->label_box.LeftTop());
599 fLabelView->ResizeTo(fLayoutData->label_box.Size());
602 // If we have layout return here and do not layout the child
603 if (layout != NULL)
604 return;
606 // layout the child
607 if (BView* child = _Child()) {
608 BRect frame(Bounds());
609 frame.left += fLayoutData->insets.left;
610 frame.top += fLayoutData->insets.top;
611 frame.right -= fLayoutData->insets.right;
612 frame.bottom -= fLayoutData->insets.bottom;
614 BLayoutUtils::AlignInFrame(child, frame);
619 void BBox::_ReservedBox1() {}
620 void BBox::_ReservedBox2() {}
623 BBox &
624 BBox::operator=(const BBox &)
626 return *this;
630 void
631 BBox::_InitObject(BMessage* archive)
633 fBounds = Bounds();
635 fLabel = NULL;
636 fLabelView = NULL;
637 fLayoutData = new LayoutData;
639 int32 flags = 0;
641 BFont font(be_bold_font);
643 if (!archive || !archive->HasString("_fname"))
644 flags = B_FONT_FAMILY_AND_STYLE;
646 if (!archive || !archive->HasFloat("_fflt"))
647 flags |= B_FONT_SIZE;
649 if (flags != 0)
650 SetFont(&font, flags);
652 if (archive != NULL) {
653 const char* string;
654 if (archive->FindString("_label", &string) == B_OK)
655 SetLabel(string);
657 bool fancy;
658 int32 style;
660 if (archive->FindBool("_style", &fancy) == B_OK)
661 fStyle = fancy ? B_FANCY_BORDER : B_PLAIN_BORDER;
662 else if (archive->FindInt32("_style", &style) == B_OK)
663 fStyle = (border_style)style;
665 bool hasLabelView;
666 if (archive->FindBool("_lblview", &hasLabelView) == B_OK)
667 fLabelView = ChildAt(0);
672 void
673 BBox::_DrawPlain(BRect labelBox)
675 BRect rect = Bounds();
676 rect.top += TopBorderOffset();
678 float lightTint;
679 float shadowTint;
680 if (be_control_look != NULL) {
681 lightTint = B_LIGHTEN_1_TINT;
682 shadowTint = B_DARKEN_1_TINT;
683 } else {
684 lightTint = B_LIGHTEN_MAX_TINT;
685 shadowTint = B_DARKEN_3_TINT;
688 if (rect.Height() == 0.0 || rect.Width() == 0.0) {
689 // used as separator
690 rgb_color shadow = tint_color(ViewColor(), B_DARKEN_2_TINT);
692 SetHighColor(shadow);
693 StrokeLine(rect.LeftTop(),rect.RightBottom());
694 } else {
695 // used as box
696 rgb_color light = tint_color(ViewColor(), lightTint);
697 rgb_color shadow = tint_color(ViewColor(), shadowTint);
699 BeginLineArray(4);
700 AddLine(BPoint(rect.left, rect.bottom),
701 BPoint(rect.left, rect.top), light);
702 AddLine(BPoint(rect.left + 1.0f, rect.top),
703 BPoint(rect.right, rect.top), light);
704 AddLine(BPoint(rect.left + 1.0f, rect.bottom),
705 BPoint(rect.right, rect.bottom), shadow);
706 AddLine(BPoint(rect.right, rect.bottom - 1.0f),
707 BPoint(rect.right, rect.top + 1.0f), shadow);
708 EndLineArray();
713 void
714 BBox::_DrawFancy(BRect labelBox)
716 BRect rect = Bounds();
717 rect.top += TopBorderOffset();
719 if (be_control_look != NULL) {
720 rgb_color base = ViewColor();
721 if (rect.Height() == 1.0) {
722 // used as horizontal separator
723 be_control_look->DrawGroupFrame(this, rect, rect, base,
724 BControlLook::B_TOP_BORDER);
725 } else if (rect.Width() == 1.0) {
726 // used as vertical separator
727 be_control_look->DrawGroupFrame(this, rect, rect, base,
728 BControlLook::B_LEFT_BORDER);
729 } else {
730 // used as box
731 be_control_look->DrawGroupFrame(this, rect, rect, base);
733 return;
736 rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT);
737 rgb_color shadow = tint_color(ViewColor(), B_DARKEN_3_TINT);
739 if (rect.Height() == 1.0) {
740 // used as horizontal separator
741 BeginLineArray(2);
742 AddLine(BPoint(rect.left, rect.top),
743 BPoint(rect.right, rect.top), shadow);
744 AddLine(BPoint(rect.left, rect.bottom),
745 BPoint(rect.right, rect.bottom), light);
746 EndLineArray();
747 } else if (rect.Width() == 1.0) {
748 // used as vertical separator
749 BeginLineArray(2);
750 AddLine(BPoint(rect.left, rect.top),
751 BPoint(rect.left, rect.bottom), shadow);
752 AddLine(BPoint(rect.right, rect.top),
753 BPoint(rect.right, rect.bottom), light);
754 EndLineArray();
755 } else {
756 // used as box
757 BeginLineArray(8);
758 AddLine(BPoint(rect.left, rect.bottom - 1.0),
759 BPoint(rect.left, rect.top), shadow);
760 AddLine(BPoint(rect.left + 1.0, rect.top),
761 BPoint(rect.right - 1.0, rect.top), shadow);
762 AddLine(BPoint(rect.left, rect.bottom),
763 BPoint(rect.right, rect.bottom), light);
764 AddLine(BPoint(rect.right, rect.bottom - 1.0),
765 BPoint(rect.right, rect.top), light);
767 rect.InsetBy(1.0, 1.0);
769 AddLine(BPoint(rect.left, rect.bottom - 1.0),
770 BPoint(rect.left, rect.top), light);
771 AddLine(BPoint(rect.left + 1.0, rect.top),
772 BPoint(rect.right - 1.0, rect.top), light);
773 AddLine(BPoint(rect.left, rect.bottom),
774 BPoint(rect.right, rect.bottom), shadow);
775 AddLine(BPoint(rect.right, rect.bottom - 1.0),
776 BPoint(rect.right, rect.top), shadow);
777 EndLineArray();
782 void
783 BBox::_ClearLabel()
785 fBounds.top = 0;
787 if (fLabel) {
788 free(fLabel);
789 fLabel = NULL;
790 } else if (fLabelView) {
791 fLabelView->RemoveSelf();
792 delete fLabelView;
793 fLabelView = NULL;
798 BView*
799 BBox::_Child() const
801 for (int32 i = 0; BView* view = ChildAt(i); i++) {
802 if (view != fLabelView)
803 return view;
806 return NULL;
810 void
811 BBox::_ValidateLayoutData()
813 if (fLayoutData->valid)
814 return;
816 // compute the label box, width and height
817 bool label = true;
818 float labelHeight = 0; // height of the label (pixel count)
819 if (fLabel) {
820 // leave 6 pixels of the frame, and have a gap of 4 pixels between
821 // the frame and the text on either side
822 font_height fontHeight;
823 GetFontHeight(&fontHeight);
824 fLayoutData->label_box.Set(6.0f, 0, 14.0f + StringWidth(fLabel),
825 ceilf(fontHeight.ascent));
826 labelHeight = ceilf(fontHeight.ascent + fontHeight.descent) + 1;
827 } else if (fLabelView) {
828 // the label view is placed at (0, 10) at its preferred size
829 BSize size = fLabelView->PreferredSize();
830 fLayoutData->label_box.Set(10, 0, 10 + size.width, size.height);
831 labelHeight = size.height + 1;
832 } else
833 label = false;
835 // border
836 switch (fStyle) {
837 case B_PLAIN_BORDER:
838 fLayoutData->insets.Set(1, 1, 1, 1);
839 break;
840 case B_FANCY_BORDER:
841 fLayoutData->insets.Set(3, 3, 3, 3);
842 break;
843 case B_NO_BORDER:
844 default:
845 fLayoutData->insets.Set(0, 0, 0, 0);
846 break;
849 // if there's a label, the top inset will be dictated by the label
850 if (label && labelHeight > fLayoutData->insets.top)
851 fLayoutData->insets.top = labelHeight;
853 // total number of pixel the border adds
854 float addWidth = fLayoutData->insets.left + fLayoutData->insets.right;
855 float addHeight = fLayoutData->insets.top + fLayoutData->insets.bottom;
857 // compute the minimal width induced by the label
858 float minWidth;
859 if (label)
860 minWidth = fLayoutData->label_box.right + fLayoutData->insets.right;
861 else
862 minWidth = addWidth - 1;
864 BAlignment alignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER);
866 // finally consider the child constraints, if we shall support layout
867 BView* child = _Child();
868 if (child && (Flags() & B_SUPPORTS_LAYOUT)) {
869 BSize min = child->MinSize();
870 BSize max = child->MaxSize();
871 BSize preferred = child->PreferredSize();
873 min.width += addWidth;
874 min.height += addHeight;
875 preferred.width += addWidth;
876 preferred.height += addHeight;
877 max.width = BLayoutUtils::AddDistances(max.width, addWidth - 1);
878 max.height = BLayoutUtils::AddDistances(max.height, addHeight - 1);
880 if (min.width < minWidth)
881 min.width = minWidth;
882 BLayoutUtils::FixSizeConstraints(min, max, preferred);
884 fLayoutData->min = min;
885 fLayoutData->max = max;
886 fLayoutData->preferred = preferred;
888 BAlignment childAlignment = child->LayoutAlignment();
889 if (childAlignment.horizontal == B_ALIGN_USE_FULL_WIDTH)
890 alignment.horizontal = B_ALIGN_USE_FULL_WIDTH;
891 if (childAlignment.vertical == B_ALIGN_USE_FULL_HEIGHT)
892 alignment.vertical = B_ALIGN_USE_FULL_HEIGHT;
894 fLayoutData->alignment = alignment;
895 } else {
896 fLayoutData->min.Set(minWidth, addHeight - 1);
897 fLayoutData->max.Set(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
898 fLayoutData->preferred = fLayoutData->min;
899 fLayoutData->alignment = alignment;
902 fLayoutData->valid = true;
903 ResetLayoutInvalidation();
907 extern "C" void
908 B_IF_GCC_2(InvalidateLayout__4BBoxb, _ZN4BBox16InvalidateLayoutEb)(
909 BBox* box, bool descendants)
911 perform_data_layout_invalidated data;
912 data.descendants = descendants;
914 box->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);