1 /* 2 * Copyright (C) 2010 Rene Gollent <rene@gollent.com> 3 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de> 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "TabManager.h" 30 31 #include <stdio.h> 32 33 #include <Application.h> 34 #include <AbstractLayoutItem.h> 35 #include <Bitmap.h> 36 #include <Button.h> 37 #include <CardLayout.h> 38 #include <ControlLook.h> 39 #include <GroupView.h> 40 #include <MenuBar.h> 41 #include <MenuItem.h> 42 #include <PopUpMenu.h> 43 #include <Rect.h> 44 #include <SpaceLayoutItem.h> 45 #include <Window.h> 46 47 #include "TabContainerView.h" 48 #include "TabView.h" 49 50 51 const static BString kEmptyString; 52 53 54 // #pragma mark - Helper classes 55 56 57 class TabButton : public BButton { 58 public: 59 TabButton(BMessage* message) 60 : BButton("", message) 61 { 62 } 63 64 virtual BSize MinSize() 65 { 66 return BSize(12, 12); 67 } 68 69 virtual BSize MaxSize() 70 { 71 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 72 } 73 74 virtual BSize PreferredSize() 75 { 76 return MinSize(); 77 } 78 79 virtual void Draw(BRect updateRect) 80 { 81 BRect bounds(Bounds()); 82 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 83 SetHighColor(tint_color(base, B_DARKEN_2_TINT)); 84 StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); 85 bounds.bottom--; 86 uint32 flags = be_control_look->Flags(this); 87 uint32 borders = BControlLook::B_TOP_BORDER 88 | BControlLook::B_BOTTOM_BORDER; 89 be_control_look->DrawInactiveTab(this, bounds, updateRect, base, 90 0, borders); 91 if (IsEnabled()) { 92 rgb_color button = tint_color(base, 1.07); 93 be_control_look->DrawButtonBackground(this, bounds, updateRect, 94 button, flags, 0); 95 } 96 97 bounds.left = (bounds.left + bounds.right) / 2 - 6; 98 bounds.top = (bounds.top + bounds.bottom) / 2 - 6; 99 bounds.right = bounds.left + 12; 100 bounds.bottom = bounds.top + 12; 101 DrawSymbol(bounds, updateRect, base); 102 } 103 104 virtual void DrawSymbol(BRect frame, const BRect& updateRect, 105 const rgb_color& base) 106 { 107 } 108 }; 109 110 111 class ScrollLeftTabButton : public TabButton { 112 public: 113 ScrollLeftTabButton(BMessage* message) 114 : TabButton(message) 115 { 116 } 117 118 virtual void DrawSymbol(BRect frame, const BRect& updateRect, 119 const rgb_color& base) 120 { 121 float tint = IsEnabled() ? B_DARKEN_4_TINT : B_DARKEN_1_TINT; 122 be_control_look->DrawArrowShape(this, frame, updateRect, 123 base, BControlLook::B_LEFT_ARROW, 0, tint); 124 } 125 }; 126 127 128 class ScrollRightTabButton : public TabButton { 129 public: 130 ScrollRightTabButton(BMessage* message) 131 : TabButton(message) 132 { 133 } 134 135 virtual void DrawSymbol(BRect frame, const BRect& updateRect, 136 const rgb_color& base) 137 { 138 frame.OffsetBy(1, 0); 139 float tint = IsEnabled() ? B_DARKEN_4_TINT : B_DARKEN_1_TINT; 140 be_control_look->DrawArrowShape(this, frame, updateRect, 141 base, BControlLook::B_RIGHT_ARROW, 0, tint); 142 } 143 }; 144 145 146 class NewTabButton : public TabButton { 147 public: 148 NewTabButton(BMessage* message) 149 : TabButton(message) 150 { 151 SetToolTip("New tab (Cmd-T)"); 152 } 153 154 virtual BSize MinSize() 155 { 156 return BSize(18, 12); 157 } 158 159 virtual void DrawSymbol(BRect frame, const BRect& updateRect, 160 const rgb_color& base) 161 { 162 SetHighColor(tint_color(base, B_DARKEN_4_TINT)); 163 float inset = 3; 164 frame.InsetBy(2, 2); 165 frame.top++; 166 frame.left++; 167 FillRoundRect(BRect(frame.left, frame.top + inset, 168 frame.right, frame.bottom - inset), 1, 1); 169 FillRoundRect(BRect(frame.left + inset, frame.top, 170 frame.right - inset, frame.bottom), 1, 1); 171 } 172 }; 173 174 175 class TabMenuTabButton : public TabButton { 176 public: 177 TabMenuTabButton(BMessage* message) 178 : TabButton(message) 179 { 180 } 181 182 virtual BSize MinSize() 183 { 184 return BSize(18, 12); 185 } 186 187 virtual void DrawSymbol(BRect frame, const BRect& updateRect, 188 const rgb_color& base) 189 { 190 be_control_look->DrawArrowShape(this, frame, updateRect, 191 base, BControlLook::B_DOWN_ARROW, 0, B_DARKEN_4_TINT); 192 } 193 194 virtual void MouseDown(BPoint point) 195 { 196 if (!IsEnabled()) 197 return; 198 199 // Invoke must be called before setting B_CONTROL_ON 200 // for the button to stay "down" 201 Invoke(); 202 SetValue(B_CONTROL_ON); 203 } 204 205 virtual void MouseUp(BPoint point) 206 { 207 // Do nothing 208 } 209 210 void MenuClosed() 211 { 212 SetValue(B_CONTROL_OFF); 213 } 214 }; 215 216 217 enum { 218 MSG_SCROLL_TABS_LEFT = 'stlt', 219 MSG_SCROLL_TABS_RIGHT = 'strt', 220 MSG_OPEN_TAB_MENU = 'otmn' 221 }; 222 223 224 class TabContainerGroup : public BGroupView { 225 public: 226 TabContainerGroup(TabContainerView* tabContainerView) 227 : 228 BGroupView(B_HORIZONTAL, 0.0), 229 fTabContainerView(tabContainerView), 230 fScrollLeftTabButton(NULL), 231 fScrollRightTabButton(NULL), 232 fTabMenuButton(NULL) 233 { 234 } 235 236 virtual void AttachedToWindow() 237 { 238 if (fScrollLeftTabButton != NULL) 239 fScrollLeftTabButton->SetTarget(this); 240 if (fScrollRightTabButton != NULL) 241 fScrollRightTabButton->SetTarget(this); 242 if (fTabMenuButton != NULL) 243 fTabMenuButton->SetTarget(this); 244 } 245 246 virtual void MessageReceived(BMessage* message) 247 { 248 switch (message->what) { 249 case MSG_SCROLL_TABS_LEFT: 250 fTabContainerView->SetFirstVisibleTabIndex( 251 fTabContainerView->FirstVisibleTabIndex() - 1); 252 break; 253 case MSG_SCROLL_TABS_RIGHT: 254 fTabContainerView->SetFirstVisibleTabIndex( 255 fTabContainerView->FirstVisibleTabIndex() + 1); 256 break; 257 case MSG_OPEN_TAB_MENU: 258 { 259 BPopUpMenu* tabMenu = new BPopUpMenu("tab menu", true, false); 260 int tabCount = fTabContainerView->GetLayout()->CountItems(); 261 for (int i = 0; i < tabCount; i++) { 262 TabView* tab = fTabContainerView->TabAt(i); 263 if (tab) { 264 BMenuItem* item = new BMenuItem(tab->Label(), NULL); 265 tabMenu->AddItem(item); 266 if (tab->IsFront()) 267 item->SetMarked(true); 268 } 269 } 270 271 // Force layout to get the final menu size. InvalidateLayout() 272 // did not seem to work here. 273 tabMenu->AttachedToWindow(); 274 BRect buttonFrame = fTabMenuButton->Frame(); 275 BRect menuFrame = tabMenu->Frame(); 276 BPoint openPoint = ConvertToScreen(buttonFrame.LeftBottom()); 277 // Open with the right side of the menu aligned with the right 278 // side of the button and a little below. 279 openPoint.x -= menuFrame.Width() - buttonFrame.Width(); 280 openPoint.y += 2; 281 282 BMenuItem *selected = tabMenu->Go(openPoint, false, false, 283 ConvertToScreen(buttonFrame)); 284 if (selected) { 285 selected->SetMarked(true); 286 int32 index = tabMenu->IndexOf(selected); 287 if (index != B_ERROR) 288 fTabContainerView->SelectTab(index); 289 } 290 fTabMenuButton->MenuClosed(); 291 delete tabMenu; 292 293 break; 294 } 295 default: 296 BGroupView::MessageReceived(message); 297 break; 298 } 299 } 300 301 void AddScrollLeftButton(TabButton* button) 302 { 303 fScrollLeftTabButton = button; 304 GroupLayout()->AddView(button, 0.0f); 305 } 306 307 void AddScrollRightButton(TabButton* button) 308 { 309 fScrollRightTabButton = button; 310 GroupLayout()->AddView(button, 0.0f); 311 } 312 313 void AddTabMenuButton(TabMenuTabButton* button) 314 { 315 fTabMenuButton = button; 316 GroupLayout()->AddView(button, 0.0f); 317 } 318 319 void EnableScrollButtons(bool canScrollLeft, bool canScrollRight) 320 { 321 fScrollLeftTabButton->SetEnabled(canScrollLeft); 322 fScrollRightTabButton->SetEnabled(canScrollRight); 323 if (!canScrollLeft && !canScrollRight) { 324 // hide scroll buttons 325 } else { 326 // show scroll buttons 327 } 328 } 329 330 private: 331 TabContainerView* fTabContainerView; 332 TabButton* fScrollLeftTabButton; 333 TabButton* fScrollRightTabButton; 334 TabMenuTabButton* fTabMenuButton; 335 }; 336 337 338 class TabButtonContainer : public BGroupView { 339 public: 340 TabButtonContainer() 341 : 342 BGroupView(B_HORIZONTAL, 0.0) 343 { 344 SetFlags(Flags() | B_WILL_DRAW); 345 SetViewColor(B_TRANSPARENT_COLOR); 346 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 347 GroupLayout()->SetInsets(0, 6, 0, 0); 348 } 349 350 virtual void Draw(BRect updateRect) 351 { 352 BRect bounds(Bounds()); 353 rgb_color base = LowColor(); 354 be_control_look->DrawInactiveTab(this, bounds, updateRect, 355 base, 0, BControlLook::B_TOP_BORDER); 356 } 357 }; 358 359 360 class TabManagerController : public TabContainerView::Controller { 361 public: 362 TabManagerController(TabManager* manager); 363 364 virtual ~TabManagerController(); 365 366 virtual void TabSelected(int32 index) 367 { 368 fManager->SelectTab(index); 369 } 370 371 virtual bool HasFrames() 372 { 373 return false; 374 } 375 376 virtual TabView* CreateTabView(); 377 378 virtual void DoubleClickOutsideTabs(); 379 380 virtual void UpdateTabScrollability(bool canScrollLeft, 381 bool canScrollRight) 382 { 383 fTabContainerGroup->EnableScrollButtons(canScrollLeft, canScrollRight); 384 } 385 386 virtual void SetToolTip(const BString& text) 387 { 388 if (fCurrentToolTip == text) 389 return; 390 fCurrentToolTip = text; 391 fManager->GetTabContainerView()->HideToolTip(); 392 fManager->GetTabContainerView()->SetToolTip( 393 reinterpret_cast<BToolTip*>(NULL)); 394 fManager->GetTabContainerView()->SetToolTip(fCurrentToolTip.String()); 395 } 396 397 void CloseTab(int32 index); 398 399 void SetCloseButtonsAvailable(bool available) 400 { 401 fCloseButtonsAvailable = available; 402 } 403 404 bool CloseButtonsAvailable() const 405 { 406 return fCloseButtonsAvailable; 407 } 408 409 void SetDoubleClickOutsideTabsMessage(const BMessage& message, 410 const BMessenger& target); 411 412 void SetTabContainerGroup(TabContainerGroup* tabContainerGroup) 413 { 414 fTabContainerGroup = tabContainerGroup; 415 } 416 417 private: 418 TabManager* fManager; 419 TabContainerGroup* fTabContainerGroup; 420 bool fCloseButtonsAvailable; 421 BMessage* fDoubleClickOutsideTabsMessage; 422 BMessenger fTarget; 423 BString fCurrentToolTip; 424 }; 425 426 427 // #pragma mark - WebTabView 428 429 430 class WebTabView : public TabView { 431 public: 432 WebTabView(TabManagerController* controller); 433 ~WebTabView(); 434 435 virtual BSize MaxSize(); 436 437 virtual void DrawContents(BView* owner, BRect frame, const BRect& updateRect, 438 bool isFirst, bool isLast, bool isFront); 439 440 virtual void MouseDown(BPoint where, uint32 buttons); 441 virtual void MouseUp(BPoint where); 442 virtual void MouseMoved(BPoint where, uint32 transit, 443 const BMessage* dragMessage); 444 445 void SetIcon(const BBitmap* icon); 446 447 private: 448 void _DrawCloseButton(BView* owner, BRect& frame, const BRect& updateRect, 449 bool isFirst, bool isLast, bool isFront); 450 BRect _CloseRectFrame(BRect frame) const; 451 452 private: 453 BBitmap* fIcon; 454 TabManagerController* fController; 455 bool fOverCloseRect; 456 bool fClicked; 457 }; 458 459 460 WebTabView::WebTabView(TabManagerController* controller) 461 : 462 TabView(), 463 fIcon(NULL), 464 fController(controller), 465 fOverCloseRect(false), 466 fClicked(false) 467 { 468 } 469 470 471 WebTabView::~WebTabView() 472 { 473 delete fIcon; 474 } 475 476 477 static const int kIconSize = 18; 478 static const int kIconInset = 3; 479 480 481 BSize 482 WebTabView::MaxSize() 483 { 484 // Account for icon. 485 BSize size(TabView::MaxSize()); 486 size.height = max_c(size.height, kIconSize + kIconInset * 2); 487 if (fIcon) 488 size.width += kIconSize + kIconInset * 2; 489 // Account for close button. 490 size.width += size.height; 491 return size; 492 } 493 494 495 void 496 WebTabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect, 497 bool isFirst, bool isLast, bool isFront) 498 { 499 if (fController->CloseButtonsAvailable()) 500 _DrawCloseButton(owner, frame, updateRect, isFirst, isLast, isFront); 501 502 if (fIcon) { 503 BRect iconBounds(0, 0, kIconSize - 1, kIconSize - 1); 504 // clip to icon bounds, if they are smaller 505 if (iconBounds.Contains(fIcon->Bounds())) 506 iconBounds = fIcon->Bounds(); 507 else { 508 // Try to scale down the icon by an even factor so the 509 // final size is between 14 and 18 pixel size. If this fails, 510 // the icon will simply be displayed at 18x18. 511 float scale = 2; 512 while ((fIcon->Bounds().Width() + 1) / scale > kIconSize) 513 scale *= 2; 514 if ((fIcon->Bounds().Width() + 1) / scale >= kIconSize - 4 515 && (fIcon->Bounds().Height() + 1) / scale >= kIconSize - 4 516 && (fIcon->Bounds().Height() + 1) / scale <= kIconSize) { 517 iconBounds.right = (fIcon->Bounds().Width() + 1) / scale - 1; 518 iconBounds.bottom = (fIcon->Bounds().Height() + 1) / scale - 1; 519 } 520 } 521 BPoint iconPos(frame.left + kIconInset - 1, 522 frame.top + floorf((frame.Height() - iconBounds.Height()) / 2)); 523 iconBounds.OffsetTo(iconPos); 524 owner->SetDrawingMode(B_OP_ALPHA); 525 owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 526 owner->DrawBitmap(fIcon, fIcon->Bounds(), iconBounds, 527 B_FILTER_BITMAP_BILINEAR); 528 owner->SetDrawingMode(B_OP_COPY); 529 frame.left = frame.left + kIconSize + kIconInset * 2; 530 } 531 532 TabView::DrawContents(owner, frame, updateRect, isFirst, isLast, isFront); 533 } 534 535 536 void 537 WebTabView::MouseDown(BPoint where, uint32 buttons) 538 { 539 if (buttons & B_TERTIARY_MOUSE_BUTTON) { 540 // Immediately close tab 541 fController->CloseTab(ContainerView()->IndexOf(this)); 542 return; 543 } 544 545 BRect closeRect = _CloseRectFrame(Frame()); 546 if (!fController->CloseButtonsAvailable() || !closeRect.Contains(where)) { 547 TabView::MouseDown(where, buttons); 548 return; 549 } 550 551 fClicked = true; 552 ContainerView()->Invalidate(closeRect); 553 } 554 555 556 void 557 WebTabView::MouseUp(BPoint where) 558 { 559 if (!fClicked) { 560 TabView::MouseUp(where); 561 return; 562 } 563 564 fClicked = false; 565 566 if (_CloseRectFrame(Frame()).Contains(where)) 567 fController->CloseTab(ContainerView()->IndexOf(this)); 568 } 569 570 571 void 572 WebTabView::MouseMoved(BPoint where, uint32 transit, 573 const BMessage* dragMessage) 574 { 575 if (fController->CloseButtonsAvailable()) { 576 BRect closeRect = _CloseRectFrame(Frame()); 577 bool overCloseRect = closeRect.Contains(where); 578 if (overCloseRect != fOverCloseRect) { 579 fOverCloseRect = overCloseRect; 580 ContainerView()->Invalidate(closeRect); 581 } 582 } 583 584 fController->SetToolTip(Label()); 585 586 TabView::MouseMoved(where, transit, dragMessage); 587 } 588 589 590 void 591 WebTabView::SetIcon(const BBitmap* icon) 592 { 593 delete fIcon; 594 if (icon) 595 fIcon = new BBitmap(icon); 596 else 597 fIcon = NULL; 598 LayoutItem()->InvalidateLayout(); 599 } 600 601 602 BRect 603 WebTabView::_CloseRectFrame(BRect frame) const 604 { 605 frame.left = frame.right - frame.Height(); 606 return frame; 607 } 608 609 610 void WebTabView::_DrawCloseButton(BView* owner, BRect& frame, 611 const BRect& updateRect, bool isFirst, bool isLast, bool isFront) 612 { 613 BRect closeRect = _CloseRectFrame(frame); 614 frame.right = closeRect.left - be_control_look->DefaultLabelSpacing(); 615 616 closeRect.left = (closeRect.left + closeRect.right) / 2 - 3; 617 closeRect.right = closeRect.left + 6; 618 closeRect.top = (closeRect.top + closeRect.bottom) / 2 - 3; 619 closeRect.bottom = closeRect.top + 6; 620 621 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 622 float tint = B_DARKEN_1_TINT; 623 if (!IsFront()) { 624 base = tint_color(base, tint); 625 tint *= 1.02; 626 } 627 628 if (fOverCloseRect) 629 tint *= 1.2; 630 631 if (fClicked && fOverCloseRect) { 632 BRect buttonRect(closeRect.InsetByCopy(-4, -4)); 633 be_control_look->DrawButtonFrame(owner, buttonRect, updateRect, 634 base, base, 635 BControlLook::B_ACTIVATED | BControlLook::B_BLEND_FRAME); 636 be_control_look->DrawButtonBackground(owner, buttonRect, updateRect, 637 base, BControlLook::B_ACTIVATED); 638 tint *= 1.2; 639 closeRect.OffsetBy(1, 1); 640 } 641 642 base = tint_color(base, tint); 643 owner->SetHighColor(base); 644 owner->SetPenSize(2); 645 owner->StrokeLine(closeRect.LeftTop(), closeRect.RightBottom()); 646 owner->StrokeLine(closeRect.LeftBottom(), closeRect.RightTop()); 647 owner->SetPenSize(1); 648 } 649 650 651 // #pragma mark - TabManagerController 652 653 654 TabManagerController::TabManagerController(TabManager* manager) 655 : 656 fManager(manager), 657 fTabContainerGroup(NULL), 658 fCloseButtonsAvailable(false), 659 fDoubleClickOutsideTabsMessage(NULL) 660 { 661 } 662 663 664 TabManagerController::~TabManagerController() 665 { 666 delete fDoubleClickOutsideTabsMessage; 667 } 668 669 670 TabView* 671 TabManagerController::CreateTabView() 672 { 673 return new WebTabView(this); 674 } 675 676 677 void 678 TabManagerController::DoubleClickOutsideTabs() 679 { 680 fTarget.SendMessage(fDoubleClickOutsideTabsMessage); 681 } 682 683 684 void 685 TabManagerController::CloseTab(int32 index) 686 { 687 fManager->CloseTab(index); 688 } 689 690 691 void 692 TabManagerController::SetDoubleClickOutsideTabsMessage(const BMessage& message, 693 const BMessenger& target) 694 { 695 delete fDoubleClickOutsideTabsMessage; 696 fDoubleClickOutsideTabsMessage = new BMessage(message); 697 fTarget = target; 698 } 699 700 701 // #pragma mark - TabManager 702 703 704 TabManager::TabManager(const BMessenger& target, BMessage* newTabMessage) 705 : 706 fController(new TabManagerController(this)), 707 fTarget(target) 708 { 709 fController->SetDoubleClickOutsideTabsMessage(*newTabMessage, 710 be_app_messenger); 711 712 fContainerView = new BView("web view container", 0); 713 fCardLayout = new BCardLayout(); 714 fContainerView->SetLayout(fCardLayout); 715 716 fTabContainerView = new TabContainerView(fController); 717 fTabContainerGroup = new TabContainerGroup(fTabContainerView); 718 fTabContainerGroup->GroupLayout()->SetInsets(0, 3, 0, 0); 719 720 fController->SetTabContainerGroup(fTabContainerGroup); 721 722 #if INTEGRATE_MENU_INTO_TAB_BAR 723 fMenu = new BMenu("Menu"); 724 BMenuBar* menuBar = new BMenuBar("Menu bar"); 725 menuBar->AddItem(fMenu); 726 TabButtonContainer* menuBarContainer = new TabButtonContainer(); 727 menuBarContainer->GroupLayout()->AddView(menuBar); 728 fTabContainerGroup->GroupLayout()->AddView(menuBarContainer, 0.0f); 729 #endif 730 731 fTabContainerGroup->GroupLayout()->AddView(fTabContainerView); 732 fTabContainerGroup->AddScrollLeftButton(new ScrollLeftTabButton( 733 new BMessage(MSG_SCROLL_TABS_LEFT))); 734 fTabContainerGroup->AddScrollRightButton(new ScrollRightTabButton( 735 new BMessage(MSG_SCROLL_TABS_RIGHT))); 736 NewTabButton* newTabButton = new NewTabButton(newTabMessage); 737 newTabButton->SetTarget(be_app); 738 fTabContainerGroup->GroupLayout()->AddView(newTabButton, 0.0f); 739 fTabContainerGroup->AddTabMenuButton(new TabMenuTabButton( 740 new BMessage(MSG_OPEN_TAB_MENU))); 741 } 742 743 744 TabManager::~TabManager() 745 { 746 delete fController; 747 } 748 749 750 void 751 TabManager::SetTarget(const BMessenger& target) 752 { 753 fTarget = target; 754 } 755 756 757 const BMessenger& 758 TabManager::Target() const 759 { 760 return fTarget; 761 } 762 763 764 #if INTEGRATE_MENU_INTO_TAB_BAR 765 BMenu* 766 TabManager::Menu() const 767 { 768 return fMenu; 769 } 770 #endif 771 772 773 BView* 774 TabManager::TabGroup() const 775 { 776 return fTabContainerGroup; 777 } 778 779 780 BView* 781 TabManager::GetTabContainerView() const 782 { 783 return fTabContainerView; 784 } 785 786 787 BView* 788 TabManager::ContainerView() const 789 { 790 return fContainerView; 791 } 792 793 794 BView* 795 TabManager::ViewForTab(int32 tabIndex) const 796 { 797 BLayoutItem* item = fCardLayout->ItemAt(tabIndex); 798 if (item != NULL) 799 return item->View(); 800 return NULL; 801 } 802 803 804 int32 805 TabManager::TabForView(const BView* containedView) const 806 { 807 int32 count = fCardLayout->CountItems(); 808 for (int32 i = 0; i < count; i++) { 809 BLayoutItem* item = fCardLayout->ItemAt(i); 810 if (item->View() == containedView) 811 return i; 812 } 813 return -1; 814 } 815 816 817 bool 818 TabManager::HasView(const BView* containedView) const 819 { 820 return TabForView(containedView) >= 0; 821 } 822 823 824 void 825 TabManager::SelectTab(int32 tabIndex) 826 { 827 fCardLayout->SetVisibleItem(tabIndex); 828 fTabContainerView->SelectTab(tabIndex); 829 830 BMessage message(TAB_CHANGED); 831 message.AddInt32("tab index", tabIndex); 832 fTarget.SendMessage(&message); 833 } 834 835 836 void 837 TabManager::SelectTab(const BView* containedView) 838 { 839 int32 tabIndex = TabForView(containedView); 840 if (tabIndex > 0) 841 SelectTab(tabIndex); 842 } 843 844 845 int32 846 TabManager::SelectedTabIndex() const 847 { 848 return fCardLayout->VisibleIndex(); 849 } 850 851 852 void 853 TabManager::CloseTab(int32 tabIndex) 854 { 855 BMessage message(CLOSE_TAB); 856 message.AddInt32("tab index", tabIndex); 857 fTarget.SendMessage(&message); 858 } 859 860 861 void 862 TabManager::AddTab(BView* view, const char* label, int32 index) 863 { 864 fTabContainerView->AddTab(label, index); 865 fCardLayout->AddView(index, view); 866 } 867 868 869 BView* 870 TabManager::RemoveTab(int32 index) 871 { 872 // It's important to remove the view first, since 873 // removing the tab will preliminary mess with the selected tab 874 // and then item count of card layout and tab container will not 875 // match yet. 876 BLayoutItem* item = fCardLayout->RemoveItem(index); 877 if (item == NULL) 878 return NULL; 879 880 TabView* tab = fTabContainerView->RemoveTab(index); 881 delete tab; 882 883 BView* view = item->View(); 884 delete item; 885 return view; 886 } 887 888 889 int32 890 TabManager::CountTabs() const 891 { 892 return fCardLayout->CountItems(); 893 } 894 895 896 void 897 TabManager::SetTabLabel(int32 tabIndex, const char* label) 898 { 899 fTabContainerView->SetTabLabel(tabIndex, label); 900 } 901 902 const BString& 903 TabManager::TabLabel(int32 tabIndex) 904 { 905 TabView* tab = fTabContainerView->TabAt(tabIndex); 906 if (tab) 907 return tab->Label(); 908 else 909 return kEmptyString; 910 } 911 912 void 913 TabManager::SetTabIcon(const BView* containedView, const BBitmap* icon) 914 { 915 WebTabView* tab = dynamic_cast<WebTabView*>(fTabContainerView->TabAt( 916 TabForView(containedView))); 917 if (tab) 918 tab->SetIcon(icon); 919 } 920 921 922 void 923 TabManager::SetCloseButtonsAvailable(bool available) 924 { 925 if (available == fController->CloseButtonsAvailable()) 926 return; 927 fController->SetCloseButtonsAvailable(available); 928 fTabContainerView->Invalidate(); 929 } 930 931 932