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