/* * Copyright 2001-2015 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Marc Flerackers (mflerackers@androme.be) * Jérôme Duval (korli@users.berlios.de) * Stephan Aßmus * Artur Wyszynski * Rene Gollent (rene@gollent.com) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static property_info sPropertyList[] = { { "Selection", { B_GET_PROPERTY, B_SET_PROPERTY }, { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE } }, { 0 } }; BTab::BTab(BView* contentsView) : fEnabled(true), fSelected(false), fFocus(false), fView(contentsView), fTabView(NULL) { } BTab::BTab(BMessage* archive) : BArchivable(archive), fSelected(false), fFocus(false), fView(NULL), fTabView(NULL) { bool disable; if (archive->FindBool("_disable", &disable) != B_OK) SetEnabled(true); else SetEnabled(!disable); } BTab::~BTab() { if (fView == NULL) return; if (fSelected) fView->RemoveSelf(); delete fView; } BArchivable* BTab::Instantiate(BMessage* archive) { if (validate_instantiation(archive, "BTab")) return new BTab(archive); return NULL; } status_t BTab::Archive(BMessage* data, bool deep) const { status_t result = BArchivable::Archive(data, deep); if (result != B_OK) return result; if (!fEnabled) result = data->AddBool("_disable", false); return result; } status_t BTab::Perform(uint32 d, void* arg) { return BArchivable::Perform(d, arg); } const char* BTab::Label() const { if (fView != NULL) return fView->Name(); else return NULL; } void BTab::SetLabel(const char* label) { if (label == NULL || fView == NULL) return; fView->SetName(label); if (fTabView != NULL) fTabView->Invalidate(); } bool BTab::IsSelected() const { return fSelected; } void BTab::Select(BView* owner) { fSelected = true; if (owner == NULL || fView == NULL) return; // NOTE: Views are not added/removed, if there is layout, // they are made visible/invisible in that case. if (owner->GetLayout() == NULL && fView->Parent() == NULL) owner->AddChild(fView); } void BTab::Deselect() { if (fView != NULL) { // NOTE: Views are not added/removed, if there is layout, // they are made visible/invisible in that case. bool removeView = false; BView* container = fView->Parent(); if (container != NULL) removeView = dynamic_cast(container->GetLayout()) == NULL; if (removeView) fView->RemoveSelf(); } fSelected = false; } void BTab::SetEnabled(bool enable) { fEnabled = enable; } bool BTab::IsEnabled() const { return fEnabled; } void BTab::MakeFocus(bool focus) { fFocus = focus; } bool BTab::IsFocus() const { return fFocus; } void BTab::SetView(BView* view) { if (view == NULL || fView == view) return; if (fView != NULL) { fView->RemoveSelf(); delete fView; } fView = view; if (fTabView != NULL && fSelected) { Select(fTabView->ContainerView()); fTabView->Invalidate(); } } BView* BTab::View() const { return fView; } void BTab::DrawFocusMark(BView* owner, BRect frame) { float width = owner->StringWidth(Label()); owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); float offset = IsSelected() ? 3 : 2; switch (fTabView->TabSide()) { case BTabView::kTopSide: owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0, frame.bottom - offset), BPoint((frame.left + frame.right + width) / 2.0, frame.bottom - offset)); break; case BTabView::kBottomSide: owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0, frame.top + offset), BPoint((frame.left + frame.right + width) / 2.0, frame.top + offset)); break; case BTabView::kLeftSide: owner->StrokeLine(BPoint(frame.right - offset, (frame.top + frame.bottom - width) / 2.0), BPoint(frame.right - offset, (frame.top + frame.bottom + width) / 2.0)); break; case BTabView::kRightSide: owner->StrokeLine(BPoint(frame.left + offset, (frame.top + frame.bottom - width) / 2.0), BPoint(frame.left + offset, (frame.top + frame.bottom + width) / 2.0)); break; } } void BTab::DrawLabel(BView* owner, BRect frame) { float rotation = 0.0f; BPoint center(frame.left + frame.Width() / 2, frame.top + frame.Height() / 2); switch (fTabView->TabSide()) { case BTabView::kTopSide: case BTabView::kBottomSide: rotation = 0.0f; break; case BTabView::kLeftSide: rotation = 270.0f; break; case BTabView::kRightSide: rotation = 90.0f; break; } if (rotation != 0.0f) { // DrawLabel doesn't allow rendering rotated text // rotate frame first and BAffineTransform will handle the rotation // we can't give "unrotated" frame because it comes from // BTabView::TabFrame and it is also used to handle mouse clicks BRect originalFrame(frame); frame.top = center.y - originalFrame.Width() / 2; frame.bottom = center.y + originalFrame.Width() / 2; frame.left = center.x - originalFrame.Height() / 2; frame.right = center.x + originalFrame.Height() / 2; } BAffineTransform transform; transform.RotateBy(center, rotation * M_PI / 180.0f); owner->SetTransform(transform); be_control_look->DrawLabel(owner, Label(), frame, frame, ui_color(B_PANEL_BACKGROUND_COLOR), IsEnabled() ? 0 : BControlLook::B_DISABLED, BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); owner->SetTransform(BAffineTransform()); } void BTab::DrawTab(BView* owner, BRect frame, tab_position, bool) { if (fTabView == NULL) return; rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); uint32 flags = 0; uint32 borders = _Borders(owner, frame); int32 index = fTabView->IndexOf(this); int32 selected = fTabView->Selection(); int32 first = 0; int32 last = fTabView->CountTabs() - 1; if (index == selected) { be_control_look->DrawActiveTab(owner, frame, frame, base, flags, borders, fTabView->TabSide(), index, selected, first, last); } else { be_control_look->DrawInactiveTab(owner, frame, frame, base, flags, borders, fTabView->TabSide(), index, selected, first, last); } DrawLabel(owner, frame); } // #pragma mark - BTab private methods uint32 BTab::_Borders(BView* owner, BRect frame) { uint32 borders = 0; if (owner == NULL || fTabView == NULL) return borders; if (fTabView->TabSide() == BTabView::kTopSide || fTabView->TabSide() == BTabView::kBottomSide) { borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER; if (frame.left == owner->Bounds().left) borders |= BControlLook::B_LEFT_BORDER; if (frame.right == owner->Bounds().right) borders |= BControlLook::B_RIGHT_BORDER; } else if (fTabView->TabSide() == BTabView::kLeftSide || fTabView->TabSide() == BTabView::kRightSide) { borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER; if (frame.top == owner->Bounds().top) borders |= BControlLook::B_TOP_BORDER; if (frame.bottom == owner->Bounds().bottom) borders |= BControlLook::B_BOTTOM_BORDER; } return borders; } // #pragma mark - FBC padding and private methods void BTab::_ReservedTab1() {} void BTab::_ReservedTab2() {} void BTab::_ReservedTab3() {} void BTab::_ReservedTab4() {} void BTab::_ReservedTab5() {} void BTab::_ReservedTab6() {} void BTab::_ReservedTab7() {} void BTab::_ReservedTab8() {} void BTab::_ReservedTab9() {} void BTab::_ReservedTab10() {} void BTab::_ReservedTab11() {} void BTab::_ReservedTab12() {} BTab &BTab::operator=(const BTab &) { // this is private and not functional, but exported return *this; } // #pragma mark - BTabView BTabView::BTabView(const char* name, button_width width, uint32 flags) : BView(name, flags) { _InitObject(true, width); } BTabView::BTabView(BRect frame, const char* name, button_width width, uint32 resizeMask, uint32 flags) : BView(frame, name, resizeMask, flags) { _InitObject(false, width); } BTabView::~BTabView() { for (int32 i = 0; i < CountTabs(); i++) delete TabAt(i); delete fTabList; } BTabView::BTabView(BMessage* archive) : BView(BUnarchiver::PrepareArchive(archive)), fTabList(new BList), fContainerView(NULL), fFocus(-1) { BUnarchiver unarchiver(archive); int16 width; if (archive->FindInt16("_but_width", &width) == B_OK) fTabWidthSetting = (button_width)width; else fTabWidthSetting = B_WIDTH_AS_USUAL; if (archive->FindFloat("_high", &fTabHeight) != B_OK) { font_height fh; GetFontHeight(&fh); fTabHeight = ceilf(fh.ascent + fh.descent + fh.leading + 8.0f); } if (archive->FindInt32("_sel", &fSelection) != B_OK) fSelection = -1; if (archive->FindInt32("_border_style", (int32*)&fBorderStyle) != B_OK) fBorderStyle = B_FANCY_BORDER; if (archive->FindInt32("_TabSide", (int32*)&fTabSide) != B_OK) fTabSide = kTopSide; int32 i = 0; BMessage tabMsg; if (BUnarchiver::IsArchiveManaged(archive)) { int32 tabCount; archive->GetInfo("_l_items", NULL, &tabCount); for (int32 i = 0; i < tabCount; i++) { unarchiver.EnsureUnarchived("_l_items", i); unarchiver.EnsureUnarchived("_view_list", i); } return; } fContainerView = ChildAt(0); _InitContainerView(Flags() & B_SUPPORTS_LAYOUT); while (archive->FindMessage("_l_items", i, &tabMsg) == B_OK) { BArchivable* archivedTab = instantiate_object(&tabMsg); if (archivedTab) { BTab* tab = dynamic_cast(archivedTab); BMessage viewMsg; if (archive->FindMessage("_view_list", i, &viewMsg) == B_OK) { BArchivable* archivedView = instantiate_object(&viewMsg); if (archivedView) AddTab(dynamic_cast(archivedView), tab); } } tabMsg.MakeEmpty(); i++; } } BArchivable* BTabView::Instantiate(BMessage* archive) { if ( validate_instantiation(archive, "BTabView")) return new BTabView(archive); return NULL; } status_t BTabView::Archive(BMessage* archive, bool deep) const { BArchiver archiver(archive); status_t result = BView::Archive(archive, deep); if (result == B_OK) result = archive->AddInt16("_but_width", fTabWidthSetting); if (result == B_OK) result = archive->AddFloat("_high", fTabHeight); if (result == B_OK) result = archive->AddInt32("_sel", fSelection); if (result == B_OK && fBorderStyle != B_FANCY_BORDER) result = archive->AddInt32("_border_style", fBorderStyle); if (result == B_OK && fTabSide != kTopSide) result = archive->AddInt32("_TabSide", fTabSide); if (result == B_OK && deep) { for (int32 i = 0; i < CountTabs(); i++) { BTab* tab = TabAt(i); if ((result = archiver.AddArchivable("_l_items", tab, deep)) != B_OK) { break; } result = archiver.AddArchivable("_view_list", tab->View(), deep); } } return archiver.Finish(result); } status_t BTabView::AllUnarchived(const BMessage* archive) { status_t err = BView::AllUnarchived(archive); if (err != B_OK) return err; fContainerView = ChildAt(0); _InitContainerView(Flags() & B_SUPPORTS_LAYOUT); BUnarchiver unarchiver(archive); int32 tabCount; archive->GetInfo("_l_items", NULL, &tabCount); for (int32 i = 0; i < tabCount && err == B_OK; i++) { BTab* tab; err = unarchiver.FindObject("_l_items", i, tab); if (err == B_OK && tab) { BView* view; if ((err = unarchiver.FindObject("_view_list", i, BUnarchiver::B_DONT_ASSUME_OWNERSHIP, view)) != B_OK) break; tab->SetView(view); fTabList->AddItem(tab); } } if (err == B_OK) Select(fSelection); return err; } status_t BTabView::Perform(perform_code code, void* _data) { switch (code) { case PERFORM_CODE_ALL_UNARCHIVED: { perform_data_all_unarchived* data = (perform_data_all_unarchived*)_data; data->return_value = BTabView::AllUnarchived(data->archive); return B_OK; } } return BView::Perform(code, _data); } void BTabView::AttachedToWindow() { BView::AttachedToWindow(); if (fSelection < 0 && CountTabs() > 0) Select(0); } void BTabView::DetachedFromWindow() { BView::DetachedFromWindow(); } void BTabView::AllAttached() { BView::AllAttached(); } void BTabView::AllDetached() { BView::AllDetached(); } // #pragma mark - void BTabView::MessageReceived(BMessage* message) { switch (message->what) { case B_GET_PROPERTY: case B_SET_PROPERTY: { BMessage reply(B_REPLY); bool handled = false; BMessage specifier; int32 index; int32 form; const char* property; if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) { if (strcmp(property, "Selection") == 0) { if (message->what == B_GET_PROPERTY) { reply.AddInt32("result", fSelection); handled = true; } else { // B_GET_PROPERTY int32 selection; if (message->FindInt32("data", &selection) == B_OK) { Select(selection); reply.AddInt32("error", B_OK); handled = true; } } } } if (handled) message->SendReply(&reply); else BView::MessageReceived(message); break; } #if 0 // TODO this would be annoying as-is, but maybe it makes sense with // a modifier or using only deltaX (not the main mouse wheel) case B_MOUSE_WHEEL_CHANGED: { float deltaX = 0.0f; float deltaY = 0.0f; message->FindFloat("be:wheel_delta_x", &deltaX); message->FindFloat("be:wheel_delta_y", &deltaY); if (deltaX == 0.0f && deltaY == 0.0f) return; if (deltaY == 0.0f) deltaY = deltaX; int32 selection = Selection(); int32 numTabs = CountTabs(); if (deltaY > 0 && selection < numTabs - 1) { // move to the right tab. Select(Selection() + 1); } else if (deltaY < 0 && selection > 0 && numTabs > 1) { // move to the left tab. Select(selection - 1); } break; } #endif default: BView::MessageReceived(message); break; } } void BTabView::KeyDown(const char* bytes, int32 numBytes) { if (IsHidden()) return; switch (bytes[0]) { case B_DOWN_ARROW: case B_LEFT_ARROW: { int32 focus = fFocus - 1; if (focus < 0) focus = CountTabs() - 1; SetFocusTab(focus, true); break; } case B_UP_ARROW: case B_RIGHT_ARROW: { int32 focus = fFocus + 1; if (focus >= CountTabs()) focus = 0; SetFocusTab(focus, true); break; } case B_RETURN: case B_SPACE: Select(FocusTab()); break; default: BView::KeyDown(bytes, numBytes); } } void BTabView::MouseDown(BPoint where) { // Which button is pressed? uint32 buttons = 0; BMessage* currentMessage = Window()->CurrentMessage(); if (currentMessage != NULL) { currentMessage->FindInt32("buttons", (int32*)&buttons); } int32 selection = Selection(); int32 numTabs = CountTabs(); if (buttons & B_MOUSE_BUTTON(4)) { // The "back" mouse button moves to previous tab if (selection > 0 && numTabs > 1) Select(Selection() - 1); } else if (buttons & B_MOUSE_BUTTON(5)) { // The "forward" mouse button moves to next tab if (selection < numTabs - 1) Select(Selection() + 1); } else { // Other buttons are used to select a tab by clicking directly on it for (int32 i = 0; i < CountTabs(); i++) { if (TabFrame(i).Contains(where) && i != Selection()) { Select(i); return; } } } BView::MouseDown(where); } void BTabView::MouseUp(BPoint where) { BView::MouseUp(where); } void BTabView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) { BView::MouseMoved(where, transit, dragMessage); } void BTabView::Pulse() { BView::Pulse(); } void BTabView::Select(int32 index) { if (index == Selection()) return; if (index < 0 || index >= CountTabs()) index = Selection(); BTab* tab = TabAt(Selection()); if (tab) tab->Deselect(); tab = TabAt(index); if (tab != NULL && fContainerView != NULL) { if (index == 0) fTabOffset = 0.0f; tab->Select(fContainerView); fSelection = index; // make the view visible through the layout if there is one BCardLayout* layout = dynamic_cast(fContainerView->GetLayout()); if (layout != NULL) layout->SetVisibleItem(index); } Invalidate(); if (index != 0 && !Bounds().Contains(TabFrame(index))){ if (!Bounds().Contains(TabFrame(index).LeftTop())) fTabOffset += TabFrame(index).left - Bounds().left - 20.0f; else fTabOffset += TabFrame(index).right - Bounds().right + 20.0f; Invalidate(); } SetFocusTab(index, true); } int32 BTabView::Selection() const { return fSelection; } void BTabView::WindowActivated(bool active) { BView::WindowActivated(active); if (IsFocus()) Invalidate(); } void BTabView::MakeFocus(bool focus) { BView::MakeFocus(focus); SetFocusTab(Selection(), focus); } void BTabView::SetFocusTab(int32 tab, bool focus) { if (tab >= CountTabs()) tab = 0; if (tab < 0) tab = CountTabs() - 1; if (focus) { if (tab == fFocus) return; if (fFocus != -1){ if (TabAt (fFocus) != NULL) TabAt(fFocus)->MakeFocus(false); Invalidate(TabFrame(fFocus)); } if (TabAt(tab) != NULL){ TabAt(tab)->MakeFocus(true); Invalidate(TabFrame(tab)); fFocus = tab; } } else if (fFocus != -1) { TabAt(fFocus)->MakeFocus(false); Invalidate(TabFrame(fFocus)); fFocus = -1; } } int32 BTabView::FocusTab() const { return fFocus; } void BTabView::Draw(BRect updateRect) { DrawTabs(); DrawBox(TabFrame(fSelection)); if (IsFocus() && fFocus != -1) TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus)); } BRect BTabView::DrawTabs() { BRect bounds(Bounds()); BRect tabFrame(bounds); uint32 borders = 0; rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); // set tabFrame to area around tabs if (fTabSide == kTopSide || fTabSide == kBottomSide) { if (fTabSide == kTopSide) tabFrame.bottom = fTabHeight; else tabFrame.top = tabFrame.bottom - fTabHeight; } else if (fTabSide == kLeftSide || fTabSide == kRightSide) { if (fTabSide == kLeftSide) tabFrame.right = fTabHeight; else tabFrame.left = tabFrame.right - fTabHeight; } // draw frame behind tabs be_control_look->DrawTabFrame(this, tabFrame, bounds, base, 0, borders, fBorderStyle, fTabSide); // draw the tabs on top of the tab frame int32 tabCount = CountTabs(); for (int32 i = 0; i < tabCount; i++) { BRect tabFrame = TabFrame(i); TabAt(i)->DrawTab(this, tabFrame, i == fSelection ? B_TAB_FRONT : (i == 0) ? B_TAB_FIRST : B_TAB_ANY, i != fSelection - 1); } return fSelection < CountTabs() ? TabFrame(fSelection) : BRect(); } void BTabView::DrawBox(BRect selectedTabRect) { BRect rect(Bounds()); uint32 bordersToDraw = BControlLook::B_ALL_BORDERS; switch (fTabSide) { case kTopSide: bordersToDraw &= ~BControlLook::B_TOP_BORDER; rect.top = fTabHeight; break; case kBottomSide: bordersToDraw &= ~BControlLook::B_BOTTOM_BORDER; rect.bottom -= fTabHeight; break; case kLeftSide: bordersToDraw &= ~BControlLook::B_LEFT_BORDER; rect.left = fTabHeight; break; case kRightSide: bordersToDraw &= ~BControlLook::B_RIGHT_BORDER; rect.right -= fTabHeight; break; } rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); if (fBorderStyle == B_FANCY_BORDER) be_control_look->DrawGroupFrame(this, rect, rect, base, bordersToDraw); else if (fBorderStyle == B_PLAIN_BORDER) { be_control_look->DrawBorder(this, rect, rect, base, B_PLAIN_BORDER, 0, bordersToDraw); } else ; // B_NO_BORDER draws no box } BRect BTabView::TabFrame(int32 index) const { if (index >= CountTabs() || index < 0) return BRect(); const float padding = ceilf(be_control_look->DefaultLabelSpacing() * 3.3f); const float height = fTabHeight; const float offset = BControlLook::ComposeSpacing(B_USE_WINDOW_SPACING); const BRect bounds(Bounds()); float width = padding * 5.0f; switch (fTabWidthSetting) { case B_WIDTH_FROM_LABEL: { float x = 0.0f; for (int32 i = 0; i < index; i++){ x += StringWidth(TabAt(i)->Label()) + padding; } switch (fTabSide) { case kTopSide: return BRect(offset + x, 0.0f, offset + x + StringWidth(TabAt(index)->Label()) + padding, height); case kBottomSide: return BRect(offset + x, bounds.bottom - height, offset + x + StringWidth(TabAt(index)->Label()) + padding, bounds.bottom); case kLeftSide: return BRect(0.0f, offset + x, height, offset + x + StringWidth(TabAt(index)->Label()) + padding); case kRightSide: return BRect(bounds.right - height, offset + x, bounds.right, offset + x + StringWidth(TabAt(index)->Label()) + padding); default: return BRect(); } } case B_WIDTH_FROM_WIDEST: width = 0.0; for (int32 i = 0; i < CountTabs(); i++) { float tabWidth = StringWidth(TabAt(i)->Label()) + padding; if (tabWidth > width) width = tabWidth; } // fall through case B_WIDTH_AS_USUAL: default: switch (fTabSide) { case kTopSide: return BRect(offset + index * width, 0.0f, offset + index * width + width, height); case kBottomSide: return BRect(offset + index * width, bounds.bottom - height, offset + index * width + width, bounds.bottom); case kLeftSide: return BRect(0.0f, offset + index * width, height, offset + index * width + width); case kRightSide: return BRect(bounds.right - height, offset + index * width, bounds.right, offset + index * width + width); default: return BRect(); } } } void BTabView::SetFlags(uint32 flags) { BView::SetFlags(flags); } void BTabView::SetResizingMode(uint32 mode) { BView::SetResizingMode(mode); } // #pragma mark - void BTabView::ResizeToPreferred() { BView::ResizeToPreferred(); } void BTabView::GetPreferredSize(float* _width, float* _height) { BView::GetPreferredSize(_width, _height); } BSize BTabView::MinSize() { BSize size; if (GetLayout()) size = GetLayout()->MinSize(); else { size = _TabsMinSize(); BSize containerSize = fContainerView->MinSize(); containerSize.width += 2 * _BorderWidth(); containerSize.height += 2 * _BorderWidth(); if (containerSize.width > size.width) size.width = containerSize.width; size.height += containerSize.height; } return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); } BSize BTabView::MaxSize() { BSize size; if (GetLayout()) size = GetLayout()->MaxSize(); else { size = _TabsMinSize(); BSize containerSize = fContainerView->MaxSize(); containerSize.width += 2 * _BorderWidth(); containerSize.height += 2 * _BorderWidth(); if (containerSize.width > size.width) size.width = containerSize.width; size.height += containerSize.height; } return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); } BSize BTabView::PreferredSize() { BSize size; if (GetLayout() != NULL) size = GetLayout()->PreferredSize(); else { size = _TabsMinSize(); BSize containerSize = fContainerView->PreferredSize(); containerSize.width += 2 * _BorderWidth(); containerSize.height += 2 * _BorderWidth(); if (containerSize.width > size.width) size.width = containerSize.width; size.height += containerSize.height; } return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); } void BTabView::FrameMoved(BPoint newPosition) { BView::FrameMoved(newPosition); } void BTabView::FrameResized(float newWidth, float newHeight) { BView::FrameResized(newWidth, newHeight); } // #pragma mark - BHandler* BTabView::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, int32 what, const char* property) { BPropertyInfo propInfo(sPropertyList); if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK) return this; return BView::ResolveSpecifier(message, index, specifier, what, property); } status_t BTabView::GetSupportedSuites(BMessage* message) { message->AddString("suites", "suite/vnd.Be-tab-view"); BPropertyInfo propInfo(sPropertyList); message->AddFlat("messages", &propInfo); return BView::GetSupportedSuites(message); } // #pragma mark - void BTabView::AddTab(BView* target, BTab* tab) { if (tab == NULL) tab = new BTab(target); else tab->SetView(target); if (fContainerView->GetLayout()) fContainerView->GetLayout()->AddView(CountTabs(), target); fTabList->AddItem(tab); BTab::Private(tab).SetTabView(this); // When we haven't had a any tabs before, but are already attached to the // window, select this one. if (CountTabs() == 1 && Window() != NULL) Select(0); } BTab* BTabView::RemoveTab(int32 index) { if (index < 0 || index >= CountTabs()) return NULL; BTab* tab = (BTab*)fTabList->RemoveItem(index); if (tab == NULL) return NULL; tab->Deselect(); BTab::Private(tab).SetTabView(NULL); if (fContainerView->GetLayout()) fContainerView->GetLayout()->RemoveItem(index); if (CountTabs() == 0) fFocus = -1; else if (index <= fSelection) Select(fSelection - 1); if (fFocus >= 0) { if (fFocus == CountTabs() - 1 || CountTabs() == 0) SetFocusTab(fFocus, false); else SetFocusTab(fFocus, true); } return tab; } BTab* BTabView::TabAt(int32 index) const { return (BTab*)fTabList->ItemAt(index); } void BTabView::SetTabWidth(button_width width) { fTabWidthSetting = width; Invalidate(); } button_width BTabView::TabWidth() const { return fTabWidthSetting; } void BTabView::SetTabHeight(float height) { if (fTabHeight == height) return; fTabHeight = height; _LayoutContainerView(GetLayout() != NULL); Invalidate(); } float BTabView::TabHeight() const { return fTabHeight; } void BTabView::SetBorder(border_style borderStyle) { if (fBorderStyle == borderStyle) return; fBorderStyle = borderStyle; _LayoutContainerView((Flags() & B_SUPPORTS_LAYOUT) != 0); } border_style BTabView::Border() const { return fBorderStyle; } void BTabView::SetTabSide(tab_side tabSide) { if (fTabSide == tabSide) return; fTabSide = tabSide; _LayoutContainerView(Flags() & B_SUPPORTS_LAYOUT); } BTabView::tab_side BTabView::TabSide() const { return fTabSide; } BView* BTabView::ContainerView() const { return fContainerView; } int32 BTabView::CountTabs() const { return fTabList->CountItems(); } BView* BTabView::ViewForTab(int32 tabIndex) const { BTab* tab = TabAt(tabIndex); if (tab != NULL) return tab->View(); return NULL; } int32 BTabView::IndexOf(BTab* tab) const { if (tab != NULL) { int32 tabCount = CountTabs(); for (int32 index = 0; index < tabCount; index++) { if (TabAt(index) == tab) return index; } } return -1; } void BTabView::_InitObject(bool layouted, button_width width) { fTabList = new BList; fTabWidthSetting = width; fSelection = -1; fFocus = -1; fTabOffset = 0.0f; fBorderStyle = B_FANCY_BORDER; fTabSide = kTopSide; SetViewUIColor(B_PANEL_BACKGROUND_COLOR); SetLowUIColor(B_PANEL_BACKGROUND_COLOR); font_height fh; GetFontHeight(&fh); fTabHeight = ceilf(fh.ascent + fh.descent + fh.leading + (be_control_look->DefaultLabelSpacing() * 1.3f)); fContainerView = NULL; _InitContainerView(layouted); } void BTabView::_InitContainerView(bool layouted) { bool needsLayout = false; bool createdContainer = false; if (layouted) { if (GetLayout() == NULL) { SetLayout(new(std::nothrow) BGroupLayout(B_HORIZONTAL)); needsLayout = true; } if (fContainerView == NULL) { fContainerView = new BView("view container", B_WILL_DRAW); fContainerView->SetLayout(new(std::nothrow) BCardLayout()); createdContainer = true; } } else if (fContainerView == NULL) { fContainerView = new BView(Bounds(), "view container", B_FOLLOW_ALL, B_WILL_DRAW); createdContainer = true; } if (needsLayout || createdContainer) _LayoutContainerView(layouted); if (createdContainer) { fContainerView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); fContainerView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR); AddChild(fContainerView); } } BSize BTabView::_TabsMinSize() const { BSize size(0.0f, TabHeight()); int32 count = min_c(2, CountTabs()); for (int32 i = 0; i < count; i++) { BRect frame = TabFrame(i); size.width += frame.Width(); } if (count < CountTabs()) { // TODO: Add size for yet to be implemented buttons that allow // "scrolling" the displayed tabs left/right. } return size; } float BTabView::_BorderWidth() const { switch (fBorderStyle) { default: case B_FANCY_BORDER: return 3.0f; case B_PLAIN_BORDER: return 1.0f; case B_NO_BORDER: return 0.0f; } } void BTabView::_LayoutContainerView(bool layouted) { float borderWidth = _BorderWidth(); if (layouted) { float topBorderOffset; switch (fBorderStyle) { default: case B_FANCY_BORDER: topBorderOffset = 1.0f; break; case B_PLAIN_BORDER: topBorderOffset = 0.0f; break; case B_NO_BORDER: topBorderOffset = -1.0f; break; } BGroupLayout* layout = dynamic_cast(GetLayout()); if (layout != NULL) { float inset = borderWidth + TabHeight() - topBorderOffset; switch (fTabSide) { case kTopSide: layout->SetInsets(borderWidth, inset, borderWidth, borderWidth); break; case kBottomSide: layout->SetInsets(borderWidth, borderWidth, borderWidth, inset); break; case kLeftSide: layout->SetInsets(inset, borderWidth, borderWidth, borderWidth); break; case kRightSide: layout->SetInsets(borderWidth, borderWidth, inset, borderWidth); break; } } } else { BRect bounds = Bounds(); switch (fTabSide) { case kTopSide: bounds.top += TabHeight(); break; case kBottomSide: bounds.bottom -= TabHeight(); break; case kLeftSide: bounds.left += TabHeight(); break; case kRightSide: bounds.right -= TabHeight(); break; } bounds.InsetBy(borderWidth, borderWidth); fContainerView->MoveTo(bounds.left, bounds.top); fContainerView->ResizeTo(bounds.Width(), bounds.Height()); } } // #pragma mark - FBC and forbidden void BTabView::_ReservedTabView3() {} void BTabView::_ReservedTabView4() {} void BTabView::_ReservedTabView5() {} void BTabView::_ReservedTabView6() {} void BTabView::_ReservedTabView7() {} void BTabView::_ReservedTabView8() {} void BTabView::_ReservedTabView9() {} void BTabView::_ReservedTabView10() {} void BTabView::_ReservedTabView11() {} void BTabView::_ReservedTabView12() {} BTabView::BTabView(const BTabView& tabView) : BView(tabView) { // this is private and not functional, but exported } BTabView& BTabView::operator=(const BTabView&) { // this is private and not functional, but exported return *this; } // #pragma mark - binary compatibility extern "C" void B_IF_GCC_2(_ReservedTabView1__8BTabView, _ZN8BTabView17_ReservedTabView1Ev)( BTabView* tabView, border_style borderStyle) { tabView->BTabView::SetBorder(borderStyle); } extern "C" void B_IF_GCC_2(_ReservedTabView2__8BTabView, _ZN8BTabView17_ReservedTabView2Ev)( BTabView* tabView, BTabView::tab_side tabSide) { tabView->BTabView::SetTabSide(tabSide); }