HaikuDepot: notify work status from main window
[haiku.git] / src / kits / interface / TabView.cpp
blobce5f648a8e4b61dd936ebe69acd7b4cb36457e2e
1 /*
2 * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Jérôme Duval (korli@users.berlios.de)
8 * Stephan Aßmus <superstippi@gmx.de>
9 * Artur Wyszynski
10 * Rene Gollent (rene@gollent.com)
14 #include <TabView.h>
15 #include <TabViewPrivate.h>
17 #include <new>
19 #include <math.h>
20 #include <string.h>
22 #include <CardLayout.h>
23 #include <ControlLook.h>
24 #include <GroupLayout.h>
25 #include <LayoutUtils.h>
26 #include <List.h>
27 #include <Message.h>
28 #include <PropertyInfo.h>
29 #include <Rect.h>
30 #include <Region.h>
31 #include <String.h>
33 #include <binary_compatibility/Support.h>
36 static property_info sPropertyList[] = {
38 "Selection",
39 { B_GET_PROPERTY, B_SET_PROPERTY },
40 { B_DIRECT_SPECIFIER },
41 NULL, 0,
42 { B_INT32_TYPE }
45 { 0 }
49 BTab::BTab(BView* contentsView)
51 fEnabled(true),
52 fSelected(false),
53 fFocus(false),
54 fView(contentsView),
55 fTabView(NULL)
60 BTab::BTab(BMessage* archive)
62 BArchivable(archive),
63 fSelected(false),
64 fFocus(false),
65 fView(NULL),
66 fTabView(NULL)
68 bool disable;
70 if (archive->FindBool("_disable", &disable) != B_OK)
71 SetEnabled(true);
72 else
73 SetEnabled(!disable);
77 BTab::~BTab()
79 if (fView == NULL)
80 return;
82 if (fSelected)
83 fView->RemoveSelf();
85 delete fView;
89 BArchivable*
90 BTab::Instantiate(BMessage* archive)
92 if (validate_instantiation(archive, "BTab"))
93 return new BTab(archive);
95 return NULL;
99 status_t
100 BTab::Archive(BMessage* data, bool deep) const
102 status_t result = BArchivable::Archive(data, deep);
103 if (result != B_OK)
104 return result;
106 if (!fEnabled)
107 result = data->AddBool("_disable", false);
109 return result;
113 status_t
114 BTab::Perform(uint32 d, void* arg)
116 return BArchivable::Perform(d, arg);
120 const char*
121 BTab::Label() const
123 if (fView != NULL)
124 return fView->Name();
125 else
126 return NULL;
130 void
131 BTab::SetLabel(const char* label)
133 if (label == NULL || fView == NULL)
134 return;
136 fView->SetName(label);
138 if (fTabView != NULL)
139 fTabView->Invalidate();
143 bool
144 BTab::IsSelected() const
146 return fSelected;
150 void
151 BTab::Select(BView* owner)
153 fSelected = true;
155 if (owner == NULL || fView == NULL)
156 return;
158 // NOTE: Views are not added/removed, if there is layout,
159 // they are made visible/invisible in that case.
160 if (owner->GetLayout() == NULL && fView->Parent() == NULL)
161 owner->AddChild(fView);
165 void
166 BTab::Deselect()
168 if (fView != NULL) {
169 // NOTE: Views are not added/removed, if there is layout,
170 // they are made visible/invisible in that case.
171 bool removeView = false;
172 BView* container = fView->Parent();
173 if (container != NULL)
174 removeView =
175 dynamic_cast<BCardLayout*>(container->GetLayout()) == NULL;
176 if (removeView)
177 fView->RemoveSelf();
180 fSelected = false;
184 void
185 BTab::SetEnabled(bool enable)
187 fEnabled = enable;
191 bool
192 BTab::IsEnabled() const
194 return fEnabled;
198 void
199 BTab::MakeFocus(bool focus)
201 fFocus = focus;
205 bool
206 BTab::IsFocus() const
208 return fFocus;
212 void
213 BTab::SetView(BView* view)
215 if (view == NULL || fView == view)
216 return;
218 if (fView != NULL) {
219 fView->RemoveSelf();
220 delete fView;
222 fView = view;
224 if (fTabView != NULL && fSelected) {
225 Select(fTabView->ContainerView());
226 fTabView->Invalidate();
231 BView*
232 BTab::View() const
234 return fView;
238 void
239 BTab::DrawFocusMark(BView* owner, BRect frame)
241 float width = owner->StringWidth(Label());
243 owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
245 float offset = IsSelected() ? 3 : 2;
246 switch (fTabView->TabSide()) {
247 case BTabView::kTopSide:
248 owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0,
249 frame.bottom - offset),
250 BPoint((frame.left + frame.right + width) / 2.0,
251 frame.bottom - offset));
252 break;
253 case BTabView::kBottomSide:
254 owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0,
255 frame.top + offset),
256 BPoint((frame.left + frame.right + width) / 2.0,
257 frame.top + offset));
258 break;
259 case BTabView::kLeftSide:
260 owner->StrokeLine(BPoint(frame.right - offset,
261 (frame.top + frame.bottom - width) / 2.0),
262 BPoint(frame.right - offset,
263 (frame.top + frame.bottom + width) / 2.0));
264 break;
265 case BTabView::kRightSide:
266 owner->StrokeLine(BPoint(frame.left + offset,
267 (frame.top + frame.bottom - width) / 2.0),
268 BPoint(frame.left + offset,
269 (frame.top + frame.bottom + width) / 2.0));
270 break;
275 void
276 BTab::DrawLabel(BView* owner, BRect frame)
278 float rotation = 0.0f;
279 BPoint center(frame.left + frame.Width() / 2,
280 frame.top + frame.Height() / 2);
281 switch (fTabView->TabSide()) {
282 case BTabView::kTopSide:
283 case BTabView::kBottomSide:
284 rotation = 0.0f;
285 break;
286 case BTabView::kLeftSide:
287 rotation = 270.0f;
288 break;
289 case BTabView::kRightSide:
290 rotation = 90.0f;
291 break;
294 if (rotation != 0.0f) {
295 // DrawLabel doesn't allow rendering rotated text
296 // rotate frame first and BAffineTransform will handle the rotation
297 // we can't give "unrotated" frame because it comes from
298 // BTabView::TabFrame and it is also used to handle mouse clicks
299 BRect originalFrame(frame);
300 frame.top = center.y - originalFrame.Width() / 2;
301 frame.bottom = center.y + originalFrame.Width() / 2;
302 frame.left = center.x - originalFrame.Height() / 2;
303 frame.right = center.x + originalFrame.Height() / 2;
306 BAffineTransform transform;
307 transform.RotateBy(center, rotation * M_PI / 180.0f);
308 owner->SetTransform(transform);
309 be_control_look->DrawLabel(owner, Label(), frame, frame,
310 ui_color(B_PANEL_BACKGROUND_COLOR),
311 IsEnabled() ? 0 : BPrivate::BControlLook::B_DISABLED,
312 BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER));
313 owner->SetTransform(BAffineTransform());
317 void
318 BTab::DrawTab(BView* owner, BRect frame, tab_position position, bool full)
320 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
321 uint32 borders = 0;
322 if (fTabView->TabSide() == BTabView::kTopSide
323 || fTabView->TabSide() == BTabView::kBottomSide) {
324 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
326 if (frame.left == owner->Bounds().left)
327 borders |= BControlLook::B_LEFT_BORDER;
329 if (frame.right == owner->Bounds().right)
330 borders |= BControlLook::B_RIGHT_BORDER;
331 } else if (fTabView->TabSide() == BTabView::kLeftSide
332 || fTabView->TabSide() == BTabView::kRightSide) {
333 borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER;
335 if (frame.top == owner->Bounds().top)
336 borders |= BControlLook::B_TOP_BORDER;
338 if (frame.bottom == owner->Bounds().bottom)
339 borders |= BControlLook::B_BOTTOM_BORDER;
342 if (position == B_TAB_FRONT) {
343 be_control_look->DrawActiveTab(owner, frame, frame, no_tint, 0,
344 borders, fTabView->TabSide());
345 } else {
346 be_control_look->DrawInactiveTab(owner, frame, frame, no_tint, 0,
347 borders, fTabView->TabSide());
350 DrawLabel(owner, frame);
354 // #pragma mark - FBC padding and private methods
357 void BTab::_ReservedTab1() {}
358 void BTab::_ReservedTab2() {}
359 void BTab::_ReservedTab3() {}
360 void BTab::_ReservedTab4() {}
361 void BTab::_ReservedTab5() {}
362 void BTab::_ReservedTab6() {}
363 void BTab::_ReservedTab7() {}
364 void BTab::_ReservedTab8() {}
365 void BTab::_ReservedTab9() {}
366 void BTab::_ReservedTab10() {}
367 void BTab::_ReservedTab11() {}
368 void BTab::_ReservedTab12() {}
370 BTab &BTab::operator=(const BTab &)
372 // this is private and not functional, but exported
373 return *this;
377 // #pragma mark - BTabView
380 BTabView::BTabView(const char* name, button_width width, uint32 flags)
382 BView(name, flags)
384 _InitObject(true, width);
388 BTabView::BTabView(BRect frame, const char* name, button_width width,
389 uint32 resizeMask, uint32 flags)
391 BView(frame, name, resizeMask, flags)
393 _InitObject(false, width);
397 BTabView::~BTabView()
399 for (int32 i = 0; i < CountTabs(); i++)
400 delete TabAt(i);
402 delete fTabList;
406 BTabView::BTabView(BMessage* archive)
408 BView(BUnarchiver::PrepareArchive(archive)),
409 fTabList(new BList),
410 fContainerView(NULL),
411 fFocus(-1)
413 BUnarchiver unarchiver(archive);
415 int16 width;
416 if (archive->FindInt16("_but_width", &width) == B_OK)
417 fTabWidthSetting = (button_width)width;
418 else
419 fTabWidthSetting = B_WIDTH_AS_USUAL;
421 if (archive->FindFloat("_high", &fTabHeight) != B_OK) {
422 font_height fh;
423 GetFontHeight(&fh);
424 fTabHeight = ceilf(fh.ascent + fh.descent + fh.leading + 8.0f);
427 if (archive->FindInt32("_sel", &fSelection) != B_OK)
428 fSelection = -1;
430 if (archive->FindInt32("_border_style", (int32*)&fBorderStyle) != B_OK)
431 fBorderStyle = B_FANCY_BORDER;
433 if (archive->FindInt32("_TabSide", (int32*)&fTabSide) != B_OK)
434 fTabSide = kTopSide;
436 int32 i = 0;
437 BMessage tabMsg;
439 if (BUnarchiver::IsArchiveManaged(archive)) {
440 int32 tabCount;
441 archive->GetInfo("_l_items", NULL, &tabCount);
442 for (int32 i = 0; i < tabCount; i++) {
443 unarchiver.EnsureUnarchived("_l_items", i);
444 unarchiver.EnsureUnarchived("_view_list", i);
446 return;
449 fContainerView = ChildAt(0);
450 _InitContainerView(Flags() & B_SUPPORTS_LAYOUT);
452 while (archive->FindMessage("_l_items", i, &tabMsg) == B_OK) {
453 BArchivable* archivedTab = instantiate_object(&tabMsg);
455 if (archivedTab) {
456 BTab* tab = dynamic_cast<BTab*>(archivedTab);
458 BMessage viewMsg;
459 if (archive->FindMessage("_view_list", i, &viewMsg) == B_OK) {
460 BArchivable* archivedView = instantiate_object(&viewMsg);
461 if (archivedView)
462 AddTab(dynamic_cast<BView*>(archivedView), tab);
466 tabMsg.MakeEmpty();
467 i++;
472 BArchivable*
473 BTabView::Instantiate(BMessage* archive)
475 if ( validate_instantiation(archive, "BTabView"))
476 return new BTabView(archive);
478 return NULL;
482 status_t
483 BTabView::Archive(BMessage* archive, bool deep) const
485 BArchiver archiver(archive);
487 status_t result = BView::Archive(archive, deep);
489 if (result == B_OK)
490 result = archive->AddInt16("_but_width", fTabWidthSetting);
491 if (result == B_OK)
492 result = archive->AddFloat("_high", fTabHeight);
493 if (result == B_OK)
494 result = archive->AddInt32("_sel", fSelection);
495 if (result == B_OK && fBorderStyle != B_FANCY_BORDER)
496 result = archive->AddInt32("_border_style", fBorderStyle);
497 if (result == B_OK && fTabSide != kTopSide)
498 result = archive->AddInt32("_TabSide", fTabSide);
500 if (result == B_OK && deep) {
501 for (int32 i = 0; i < CountTabs(); i++) {
502 BTab* tab = TabAt(i);
504 if ((result = archiver.AddArchivable("_l_items", tab, deep))
505 != B_OK) {
506 break;
508 result = archiver.AddArchivable("_view_list", tab->View(), deep);
512 return archiver.Finish(result);
516 status_t
517 BTabView::AllUnarchived(const BMessage* archive)
519 status_t err = BView::AllUnarchived(archive);
520 if (err != B_OK)
521 return err;
523 fContainerView = ChildAt(0);
524 _InitContainerView(Flags() & B_SUPPORTS_LAYOUT);
526 BUnarchiver unarchiver(archive);
528 int32 tabCount;
529 archive->GetInfo("_l_items", NULL, &tabCount);
530 for (int32 i = 0; i < tabCount && err == B_OK; i++) {
531 BTab* tab;
532 err = unarchiver.FindObject("_l_items", i, tab);
533 if (err == B_OK && tab) {
534 BView* view;
535 if ((err = unarchiver.FindObject("_view_list", i,
536 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, view)) != B_OK)
537 break;
539 tab->SetView(view);
540 fTabList->AddItem(tab);
544 if (err == B_OK)
545 Select(fSelection);
547 return err;
551 status_t
552 BTabView::Perform(perform_code code, void* _data)
554 switch (code) {
555 case PERFORM_CODE_ALL_UNARCHIVED:
557 perform_data_all_unarchived* data
558 = (perform_data_all_unarchived*)_data;
560 data->return_value = BTabView::AllUnarchived(data->archive);
561 return B_OK;
565 return BView::Perform(code, _data);
569 void
570 BTabView::AttachedToWindow()
572 BView::AttachedToWindow();
574 if (fSelection < 0 && CountTabs() > 0)
575 Select(0);
579 void
580 BTabView::DetachedFromWindow()
582 BView::DetachedFromWindow();
586 void
587 BTabView::AllAttached()
589 BView::AllAttached();
593 void
594 BTabView::AllDetached()
596 BView::AllDetached();
600 // #pragma mark -
603 void
604 BTabView::MessageReceived(BMessage* message)
606 switch (message->what) {
607 case B_GET_PROPERTY:
608 case B_SET_PROPERTY:
610 BMessage reply(B_REPLY);
611 bool handled = false;
613 BMessage specifier;
614 int32 index;
615 int32 form;
616 const char* property;
617 if (message->GetCurrentSpecifier(&index, &specifier, &form,
618 &property) == B_OK) {
619 if (strcmp(property, "Selection") == 0) {
620 if (message->what == B_GET_PROPERTY) {
621 reply.AddInt32("result", fSelection);
622 handled = true;
623 } else {
624 // B_GET_PROPERTY
625 int32 selection;
626 if (message->FindInt32("data", &selection) == B_OK) {
627 Select(selection);
628 reply.AddInt32("error", B_OK);
629 handled = true;
635 if (handled)
636 message->SendReply(&reply);
637 else
638 BView::MessageReceived(message);
639 break;
642 #if 0
643 case B_MOUSE_WHEEL_CHANGED:
645 float deltaX = 0.0f;
646 float deltaY = 0.0f;
647 message->FindFloat("be:wheel_delta_x", &deltaX);
648 message->FindFloat("be:wheel_delta_y", &deltaY);
650 if (deltaX == 0.0f && deltaY == 0.0f)
651 return;
653 if (deltaY == 0.0f)
654 deltaY = deltaX;
656 int32 selection = Selection();
657 int32 numTabs = CountTabs();
658 if (deltaY > 0 && selection < numTabs - 1) {
659 // move to the right tab.
660 Select(Selection() + 1);
661 } else if (deltaY < 0 && selection > 0 && numTabs > 1) {
662 // move to the left tab.
663 Select(selection - 1);
665 break;
667 #endif
669 default:
670 BView::MessageReceived(message);
671 break;
676 void
677 BTabView::KeyDown(const char* bytes, int32 numBytes)
679 if (IsHidden())
680 return;
682 switch (bytes[0]) {
683 case B_DOWN_ARROW:
684 case B_LEFT_ARROW: {
685 int32 focus = fFocus - 1;
686 if (focus < 0)
687 focus = CountTabs() - 1;
688 SetFocusTab(focus, true);
689 break;
692 case B_UP_ARROW:
693 case B_RIGHT_ARROW: {
694 int32 focus = fFocus + 1;
695 if (focus >= CountTabs())
696 focus = 0;
697 SetFocusTab(focus, true);
698 break;
701 case B_RETURN:
702 case B_SPACE:
703 Select(FocusTab());
704 break;
706 default:
707 BView::KeyDown(bytes, numBytes);
712 void
713 BTabView::MouseDown(BPoint where)
715 for (int32 i = 0; i < CountTabs(); i++) {
716 if (TabFrame(i).Contains(where)
717 && i != Selection()) {
718 Select(i);
719 return;
723 BView::MouseDown(where);
727 void
728 BTabView::MouseUp(BPoint where)
730 BView::MouseUp(where);
734 void
735 BTabView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
737 BView::MouseMoved(where, transit, dragMessage);
741 void
742 BTabView::Pulse()
744 BView::Pulse();
748 void
749 BTabView::Select(int32 index)
751 if (index == Selection())
752 return;
754 if (index < 0 || index >= CountTabs())
755 index = Selection();
757 BTab* tab = TabAt(Selection());
759 if (tab)
760 tab->Deselect();
762 tab = TabAt(index);
763 if (tab != NULL && fContainerView != NULL) {
764 if (index == 0)
765 fTabOffset = 0.0f;
767 tab->Select(fContainerView);
768 fSelection = index;
770 // make the view visible through the layout if there is one
771 BCardLayout* layout
772 = dynamic_cast<BCardLayout*>(fContainerView->GetLayout());
773 if (layout != NULL)
774 layout->SetVisibleItem(index);
777 Invalidate();
779 if (index != 0 && !Bounds().Contains(TabFrame(index))){
780 if (!Bounds().Contains(TabFrame(index).LeftTop()))
781 fTabOffset += TabFrame(index).left - Bounds().left - 20.0f;
782 else
783 fTabOffset += TabFrame(index).right - Bounds().right + 20.0f;
785 Invalidate();
788 SetFocusTab(index, true);
792 int32
793 BTabView::Selection() const
795 return fSelection;
799 void
800 BTabView::WindowActivated(bool active)
802 BView::WindowActivated(active);
804 if (IsFocus())
805 Invalidate();
809 void
810 BTabView::MakeFocus(bool focus)
812 BView::MakeFocus(focus);
814 SetFocusTab(Selection(), focus);
818 void
819 BTabView::SetFocusTab(int32 tab, bool focus)
821 if (tab >= CountTabs())
822 tab = 0;
824 if (tab < 0)
825 tab = CountTabs() - 1;
827 if (focus) {
828 if (tab == fFocus)
829 return;
831 if (fFocus != -1){
832 if (TabAt (fFocus) != NULL)
833 TabAt(fFocus)->MakeFocus(false);
834 Invalidate(TabFrame(fFocus));
836 if (TabAt(tab) != NULL){
837 TabAt(tab)->MakeFocus(true);
838 Invalidate(TabFrame(tab));
839 fFocus = tab;
841 } else if (fFocus != -1) {
842 TabAt(fFocus)->MakeFocus(false);
843 Invalidate(TabFrame(fFocus));
844 fFocus = -1;
849 int32
850 BTabView::FocusTab() const
852 return fFocus;
856 void
857 BTabView::Draw(BRect updateRect)
859 DrawTabs();
860 DrawBox(TabFrame(fSelection));
862 if (IsFocus() && fFocus != -1)
863 TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus));
867 BRect
868 BTabView::DrawTabs()
870 BRect bounds(Bounds());
871 BRect tabsBounds;
872 uint32 borders = 0;
873 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
874 if (fTabSide == kTopSide || fTabSide == kBottomSide) {
875 if (fTabSide == kTopSide)
876 bounds.bottom = fTabHeight;
877 else
878 bounds.top = bounds.bottom - fTabHeight;
879 tabsBounds = bounds;
880 // make a copy for later
882 // draw an inactive tab frame behind all tabs
883 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
884 if (fBorderStyle == B_NO_BORDER) {
885 // removes left border that is an artifact of DrawInactiveTab()
886 bounds.left -= 1;
887 } else {
888 borders |= BControlLook::B_LEFT_BORDER
889 | BControlLook::B_RIGHT_BORDER;
892 // DrawInactiveTab draws 2px border
893 // draw a little wider tab frame to align B_PLAIN_BORDER with it
894 if (fBorderStyle == B_PLAIN_BORDER) {
895 bounds.left -= 1;
896 bounds.right += 1;
898 } else if (fTabSide == kLeftSide || fTabSide == kRightSide) {
899 if (fTabSide == kLeftSide)
900 bounds.right = fTabHeight;
901 else
902 bounds.left = bounds.right - fTabHeight;
903 tabsBounds = bounds;
904 // make a copy for later
906 // draw an inactive tab frame behind all tabs
907 borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER;
908 if (fBorderStyle == B_NO_BORDER) {
909 // removes top border that is an artifact of DrawInactiveTab()
910 bounds.top -= 1;
911 } else {
912 borders |= BControlLook::B_TOP_BORDER
913 | BControlLook::B_BOTTOM_BORDER;
916 // DrawInactiveTab draws 2px border
917 // draw a little wider tab frame to align B_PLAIN_BORDER with it
918 if (fBorderStyle == B_PLAIN_BORDER) {
919 bounds.top -= 1;
920 bounds.bottom += 1;
924 be_control_look->DrawInactiveTab(this, bounds, bounds, base, 0,
925 borders, fTabSide);
927 // draw the tabs on top of the inactive tab bounds
928 BRect activeTabFrame;
929 int32 tabCount = CountTabs();
930 for (int32 i = 0; i < tabCount; i++) {
931 BRect tabFrame = TabFrame(i);
932 if (i == fSelection)
933 activeTabFrame = tabFrame;
935 TabAt(i)->DrawTab(this, tabFrame,
936 i == fSelection ? B_TAB_FRONT :
937 (i == 0) ? B_TAB_FIRST : B_TAB_ANY,
938 i + 1 != fSelection);
941 float last = 0.0f;
942 float lastTab = 0.0f;
943 if (fTabSide == kTopSide || fTabSide == kBottomSide) {
944 lastTab = TabFrame(tabCount - 1).right;
945 last = bounds.right;
946 tabsBounds.left = tabsBounds.right = lastTab;
947 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
948 } else if (fTabSide == kLeftSide || fTabSide == kRightSide) {
949 lastTab = TabFrame(tabCount - 1).bottom;
950 last = bounds.bottom;
951 tabsBounds.top = tabsBounds.bottom = lastTab;
952 borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER;
955 if (lastTab < last) {
956 // draw a 1px right border on the last tab
957 be_control_look->DrawInactiveTab(this, tabsBounds, tabsBounds, base, 0,
958 borders, fTabSide);
961 return fSelection < CountTabs() ? TabFrame(fSelection) : BRect();
965 void
966 BTabView::DrawBox(BRect selectedTabRect)
968 BRect rect(Bounds());
969 uint32 bordersToDraw = BControlLook::B_ALL_BORDERS;
970 switch (fTabSide) {
971 case kTopSide:
972 bordersToDraw &= ~BControlLook::B_TOP_BORDER;
973 rect.top = fTabHeight;
974 break;
975 case kBottomSide:
976 bordersToDraw &= ~BControlLook::B_BOTTOM_BORDER;
977 rect.bottom -= fTabHeight;
978 break;
979 case kLeftSide:
980 bordersToDraw &= ~BControlLook::B_LEFT_BORDER;
981 rect.left = fTabHeight;
982 break;
983 case kRightSide:
984 bordersToDraw &= ~BControlLook::B_RIGHT_BORDER;
985 rect.right -= fTabHeight;
986 break;
989 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
990 if (fBorderStyle == B_FANCY_BORDER)
991 be_control_look->DrawGroupFrame(this, rect, rect, base, bordersToDraw);
992 else if (fBorderStyle == B_PLAIN_BORDER) {
993 be_control_look->DrawBorder(this, rect, rect, base, B_PLAIN_BORDER,
994 0, bordersToDraw);
995 } else
996 ; // B_NO_BORDER draws no box
1000 BRect
1001 BTabView::TabFrame(int32 index) const
1003 if (index >= CountTabs() || index < 0)
1004 return BRect();
1006 float width = 100.0f;
1007 float height = fTabHeight;
1008 float offset = BControlLook::ComposeSpacing(B_USE_WINDOW_SPACING);
1009 BRect bounds(Bounds());
1011 switch (fTabWidthSetting) {
1012 case B_WIDTH_FROM_LABEL:
1014 float x = 0.0f;
1015 for (int32 i = 0; i < index; i++){
1016 x += StringWidth(TabAt(i)->Label()) + 20.0f;
1019 switch (fTabSide) {
1020 case kTopSide:
1021 return BRect(offset + x, 0.0f,
1022 offset + x + StringWidth(TabAt(index)->Label()) + 20.0f,
1023 height);
1024 case kBottomSide:
1025 return BRect(offset + x, bounds.bottom - height,
1026 offset + x + StringWidth(TabAt(index)->Label()) + 20.0f,
1027 bounds.bottom);
1028 case kLeftSide:
1029 return BRect(0.0f, offset + x, height, offset + x
1030 + StringWidth(TabAt(index)->Label()) + 20.0f);
1031 case kRightSide:
1032 return BRect(bounds.right - height, offset + x,
1033 bounds.right, offset + x
1034 + StringWidth(TabAt(index)->Label()) + 20.0f);
1035 default:
1036 return BRect();
1040 case B_WIDTH_FROM_WIDEST:
1041 width = 0.0;
1042 for (int32 i = 0; i < CountTabs(); i++) {
1043 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f;
1044 if (tabWidth > width)
1045 width = tabWidth;
1047 // fall through
1049 case B_WIDTH_AS_USUAL:
1050 default:
1051 switch (fTabSide) {
1052 case kTopSide:
1053 return BRect(offset + index * width, 0.0f,
1054 offset + index * width + width, height);
1055 case kBottomSide:
1056 return BRect(offset + index * width, bounds.bottom - height,
1057 offset + index * width + width, bounds.bottom);
1058 case kLeftSide:
1059 return BRect(0.0f, offset + index * width, height,
1060 offset + index * width + width);
1061 case kRightSide:
1062 return BRect(bounds.right - height, offset + index * width,
1063 bounds.right, offset + index * width + width);
1064 default:
1065 return BRect();
1071 void
1072 BTabView::SetFlags(uint32 flags)
1074 BView::SetFlags(flags);
1078 void
1079 BTabView::SetResizingMode(uint32 mode)
1081 BView::SetResizingMode(mode);
1085 // #pragma mark -
1088 void
1089 BTabView::ResizeToPreferred()
1091 BView::ResizeToPreferred();
1095 void
1096 BTabView::GetPreferredSize(float* _width, float* _height)
1098 BView::GetPreferredSize(_width, _height);
1102 BSize
1103 BTabView::MinSize()
1105 BSize size;
1106 if (GetLayout())
1107 size = GetLayout()->MinSize();
1108 else {
1109 size = _TabsMinSize();
1110 BSize containerSize = fContainerView->MinSize();
1111 containerSize.width += 2 * _BorderWidth();
1112 containerSize.height += 2 * _BorderWidth();
1113 if (containerSize.width > size.width)
1114 size.width = containerSize.width;
1115 size.height += containerSize.height;
1117 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
1121 BSize
1122 BTabView::MaxSize()
1124 BSize size;
1125 if (GetLayout())
1126 size = GetLayout()->MaxSize();
1127 else {
1128 size = _TabsMinSize();
1129 BSize containerSize = fContainerView->MaxSize();
1130 containerSize.width += 2 * _BorderWidth();
1131 containerSize.height += 2 * _BorderWidth();
1132 if (containerSize.width > size.width)
1133 size.width = containerSize.width;
1134 size.height += containerSize.height;
1136 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
1140 BSize
1141 BTabView::PreferredSize()
1143 BSize size;
1144 if (GetLayout() != NULL)
1145 size = GetLayout()->PreferredSize();
1146 else {
1147 size = _TabsMinSize();
1148 BSize containerSize = fContainerView->PreferredSize();
1149 containerSize.width += 2 * _BorderWidth();
1150 containerSize.height += 2 * _BorderWidth();
1151 if (containerSize.width > size.width)
1152 size.width = containerSize.width;
1153 size.height += containerSize.height;
1155 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
1159 void
1160 BTabView::FrameMoved(BPoint newPosition)
1162 BView::FrameMoved(newPosition);
1166 void
1167 BTabView::FrameResized(float newWidth, float newHeight)
1169 BView::FrameResized(newWidth, newHeight);
1173 // #pragma mark -
1176 BHandler*
1177 BTabView::ResolveSpecifier(BMessage* message, int32 index,
1178 BMessage* specifier, int32 what, const char* property)
1180 BPropertyInfo propInfo(sPropertyList);
1182 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
1183 return this;
1185 return BView::ResolveSpecifier(message, index, specifier, what, property);
1189 status_t
1190 BTabView::GetSupportedSuites(BMessage* message)
1192 message->AddString("suites", "suite/vnd.Be-tab-view");
1194 BPropertyInfo propInfo(sPropertyList);
1195 message->AddFlat("messages", &propInfo);
1197 return BView::GetSupportedSuites(message);
1201 // #pragma mark -
1204 void
1205 BTabView::AddTab(BView* target, BTab* tab)
1207 if (tab == NULL)
1208 tab = new BTab(target);
1209 else
1210 tab->SetView(target);
1212 if (fContainerView->GetLayout())
1213 fContainerView->GetLayout()->AddView(CountTabs(), target);
1215 fTabList->AddItem(tab);
1216 BTab::Private(tab).SetTabView(this);
1218 // When we haven't had a any tabs before, but are already attached to the
1219 // window, select this one.
1220 if (CountTabs() == 1 && Window() != NULL)
1221 Select(0);
1225 BTab*
1226 BTabView::RemoveTab(int32 index)
1228 if (index < 0 || index >= CountTabs())
1229 return NULL;
1231 BTab* tab = (BTab*)fTabList->RemoveItem(index);
1232 if (tab == NULL)
1233 return NULL;
1235 tab->Deselect();
1236 BTab::Private(tab).SetTabView(NULL);
1238 if (fContainerView->GetLayout())
1239 fContainerView->GetLayout()->RemoveItem(index);
1241 if (CountTabs() == 0)
1242 fFocus = -1;
1243 else if (index <= fSelection)
1244 Select(fSelection - 1);
1246 if (fFocus >= 0) {
1247 if (fFocus == CountTabs() - 1 || CountTabs() == 0)
1248 SetFocusTab(fFocus, false);
1249 else
1250 SetFocusTab(fFocus, true);
1253 return tab;
1257 BTab*
1258 BTabView::TabAt(int32 index) const
1260 return (BTab*)fTabList->ItemAt(index);
1264 void
1265 BTabView::SetTabWidth(button_width width)
1267 fTabWidthSetting = width;
1269 Invalidate();
1273 button_width
1274 BTabView::TabWidth() const
1276 return fTabWidthSetting;
1280 void
1281 BTabView::SetTabHeight(float height)
1283 if (fTabHeight == height)
1284 return;
1286 fTabHeight = height;
1287 _LayoutContainerView(GetLayout() != NULL);
1289 Invalidate();
1293 float
1294 BTabView::TabHeight() const
1296 return fTabHeight;
1300 void
1301 BTabView::SetBorder(border_style borderStyle)
1303 if (fBorderStyle == borderStyle)
1304 return;
1306 fBorderStyle = borderStyle;
1308 _LayoutContainerView((Flags() & B_SUPPORTS_LAYOUT) != 0);
1312 border_style
1313 BTabView::Border() const
1315 return fBorderStyle;
1319 void
1320 BTabView::SetTabSide(tab_side tabSide)
1322 if (fTabSide == tabSide)
1323 return;
1325 fTabSide = tabSide;
1326 _LayoutContainerView(Flags() & B_SUPPORTS_LAYOUT);
1330 BTabView::tab_side
1331 BTabView::TabSide() const
1333 return fTabSide;
1337 BView*
1338 BTabView::ContainerView() const
1340 return fContainerView;
1344 int32
1345 BTabView::CountTabs() const
1347 return fTabList->CountItems();
1351 BView*
1352 BTabView::ViewForTab(int32 tabIndex) const
1354 BTab* tab = TabAt(tabIndex);
1355 if (tab != NULL)
1356 return tab->View();
1358 return NULL;
1362 void
1363 BTabView::_InitObject(bool layouted, button_width width)
1365 fTabList = new BList;
1367 fTabWidthSetting = width;
1368 fSelection = -1;
1369 fFocus = -1;
1370 fTabOffset = 0.0f;
1371 fBorderStyle = B_FANCY_BORDER;
1372 fTabSide = kTopSide;
1374 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1375 SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1377 font_height fh;
1378 GetFontHeight(&fh);
1379 fTabHeight = ceilf(fh.ascent + fh.descent + fh.leading + 8.0f);
1381 fContainerView = NULL;
1382 _InitContainerView(layouted);
1386 void
1387 BTabView::_InitContainerView(bool layouted)
1389 bool needsLayout = false;
1390 bool createdContainer = false;
1391 if (layouted) {
1392 if (GetLayout() == NULL) {
1393 SetLayout(new(std::nothrow) BGroupLayout(B_HORIZONTAL));
1394 needsLayout = true;
1397 if (fContainerView == NULL) {
1398 fContainerView = new BView("view container", B_WILL_DRAW);
1399 fContainerView->SetLayout(new(std::nothrow) BCardLayout());
1400 createdContainer = true;
1402 } else if (fContainerView == NULL) {
1403 fContainerView = new BView(Bounds(), "view container", B_FOLLOW_ALL,
1404 B_WILL_DRAW);
1405 createdContainer = true;
1408 if (needsLayout || createdContainer)
1409 _LayoutContainerView(layouted);
1411 if (createdContainer) {
1412 fContainerView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1413 fContainerView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1414 AddChild(fContainerView);
1419 BSize
1420 BTabView::_TabsMinSize() const
1422 BSize size(0.0f, TabHeight());
1423 int32 count = min_c(2, CountTabs());
1424 for (int32 i = 0; i < count; i++) {
1425 BRect frame = TabFrame(i);
1426 size.width += frame.Width();
1429 if (count < CountTabs()) {
1430 // TODO: Add size for yet to be implemented buttons that allow
1431 // "scrolling" the displayed tabs left/right.
1434 return size;
1438 float
1439 BTabView::_BorderWidth() const
1441 switch (fBorderStyle) {
1442 default:
1443 case B_FANCY_BORDER:
1444 return 3.0f;
1446 case B_PLAIN_BORDER:
1447 return 1.0f;
1449 case B_NO_BORDER:
1450 return 0.0f;
1455 void
1456 BTabView::_LayoutContainerView(bool layouted)
1458 float borderWidth = _BorderWidth();
1459 if (layouted) {
1460 float topBorderOffset;
1461 switch (fBorderStyle) {
1462 default:
1463 case B_FANCY_BORDER:
1464 topBorderOffset = 1.0f;
1465 break;
1467 case B_PLAIN_BORDER:
1468 topBorderOffset = 0.0f;
1469 break;
1471 case B_NO_BORDER:
1472 topBorderOffset = -1.0f;
1473 break;
1475 BGroupLayout* layout = dynamic_cast<BGroupLayout*>(GetLayout());
1476 if (layout != NULL) {
1477 float inset = borderWidth + TabHeight() - topBorderOffset;
1478 switch (fTabSide) {
1479 case kTopSide:
1480 layout->SetInsets(borderWidth, inset, borderWidth,
1481 borderWidth);
1482 break;
1483 case kBottomSide:
1484 layout->SetInsets(borderWidth, borderWidth, borderWidth,
1485 inset);
1486 break;
1487 case kLeftSide:
1488 layout->SetInsets(inset, borderWidth, borderWidth,
1489 borderWidth);
1490 break;
1491 case kRightSide:
1492 layout->SetInsets(borderWidth, borderWidth, inset,
1493 borderWidth);
1494 break;
1497 } else {
1498 BRect bounds = Bounds();
1499 switch (fTabSide) {
1500 case kTopSide:
1501 bounds.top += TabHeight();
1502 break;
1503 case kBottomSide:
1504 bounds.bottom -= TabHeight();
1505 break;
1506 case kLeftSide:
1507 bounds.left += TabHeight();
1508 break;
1509 case kRightSide:
1510 bounds.right -= TabHeight();
1511 break;
1513 bounds.InsetBy(borderWidth, borderWidth);
1515 fContainerView->MoveTo(bounds.left, bounds.top);
1516 fContainerView->ResizeTo(bounds.Width(), bounds.Height());
1521 // #pragma mark - FBC and forbidden
1524 void BTabView::_ReservedTabView3() {}
1525 void BTabView::_ReservedTabView4() {}
1526 void BTabView::_ReservedTabView5() {}
1527 void BTabView::_ReservedTabView6() {}
1528 void BTabView::_ReservedTabView7() {}
1529 void BTabView::_ReservedTabView8() {}
1530 void BTabView::_ReservedTabView9() {}
1531 void BTabView::_ReservedTabView10() {}
1532 void BTabView::_ReservedTabView11() {}
1533 void BTabView::_ReservedTabView12() {}
1536 BTabView::BTabView(const BTabView& tabView)
1537 : BView(tabView)
1539 // this is private and not functional, but exported
1543 BTabView&
1544 BTabView::operator=(const BTabView&)
1546 // this is private and not functional, but exported
1547 return *this;
1550 // #pragma mark - binary compatibility
1553 extern "C" void
1554 B_IF_GCC_2(_ReservedTabView1__8BTabView, _ZN8BTabView17_ReservedTabView1Ev)(
1555 BTabView* tabView, border_style borderStyle)
1557 tabView->BTabView::SetBorder(borderStyle);
1560 extern "C" void
1561 B_IF_GCC_2(_ReservedTabView2__8BTabView, _ZN8BTabView17_ReservedTabView2Ev)(
1562 BTabView* tabView, BTabView::tab_side tabSide)
1564 tabView->BTabView::SetTabSide(tabSide);