xref: /haiku/src/apps/webpositive/tabview/TabManager.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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:
45 	TabButton(BMessage* message)
46 		: BButton("", message)
47 	{
48 	}
49 
50 	virtual BSize MinSize()
51 	{
52 		return BSize(12, 12);
53 	}
54 
55 	virtual BSize MaxSize()
56 	{
57 		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
58 	}
59 
60 	virtual BSize PreferredSize()
61 	{
62 		return MinSize();
63 	}
64 
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 
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:
97 	ScrollLeftTabButton(BMessage* message)
98 		: TabButton(message)
99 	{
100 	}
101 
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:
114 	ScrollRightTabButton(BMessage* message)
115 		: TabButton(message)
116 	{
117 	}
118 
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:
132 	NewTabButton(BMessage* message)
133 		: TabButton(message)
134 	{
135 		SetToolTip("New tab (Cmd-T)");
136 	}
137 
138 	virtual BSize MinSize()
139 	{
140 		return BSize(18, 12);
141 	}
142 
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:
161 	TabMenuTabButton(BMessage* message)
162 		: TabButton(message)
163 		, fCloseTime(0)
164 	{
165 	}
166 
167 	virtual BSize MinSize()
168 	{
169 		return BSize(18, 12);
170 	}
171 
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 
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 
196 	virtual void MouseUp(BPoint point)
197 	{
198 		// Do nothing
199 	}
200 
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:
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 
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 
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 
305 	void AddScrollLeftButton(TabButton* button)
306 	{
307 		fScrollLeftTabButton = button;
308 		GroupLayout()->AddView(button, 0.0f);
309 	}
310 
311 	void AddScrollRightButton(TabButton* button)
312 	{
313 		fScrollRightTabButton = button;
314 		GroupLayout()->AddView(button, 0.0f);
315 	}
316 
317 	void AddTabMenuButton(TabMenuTabButton* button)
318 	{
319 		fTabMenuButton = button;
320 		GroupLayout()->AddView(button, 0.0f);
321 	}
322 
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:
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 
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 
367 	virtual void UpdateSelection(int32 index)
368 	{
369 		fManager->SelectTab(index);
370 	}
371 
372 	virtual bool HasFrames()
373 	{
374 		return false;
375 	}
376 
377 	virtual TabView* CreateTabView();
378 
379 	virtual void DoubleClickOutsideTabs();
380 
381 	virtual void UpdateTabScrollability(bool canScrollLeft,
382 		bool canScrollRight)
383 	{
384 		fTabContainerGroup->EnableScrollButtons(canScrollLeft, canScrollRight);
385 	}
386 
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 
401 	void SetCloseButtonsAvailable(bool available)
402 	{
403 		fCloseButtonsAvailable = available;
404 	}
405 
406 	bool CloseButtonsAvailable() const
407 	{
408 		return fCloseButtonsAvailable;
409 	}
410 
411 	void SetDoubleClickOutsideTabsMessage(const BMessage& message,
412 		const BMessenger& target);
413 
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 
461 WebTabView::WebTabView(TabManagerController* controller)
462 	:
463 	TabView(),
464 	fIcon(NULL),
465 	fController(controller),
466 	fOverCloseRect(false),
467 	fClicked(false)
468 {
469 }
470 
471 
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
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
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
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
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
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
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
606 WebTabView::_CloseRectFrame(BRect frame) const
607 {
608 	frame.left = frame.right - frame.Height();
609 	return frame;
610 }
611 
612 
613 void
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 	float tint = B_DARKEN_1_TINT;
627 
628 	float isFront = ContainerView()->SelectedTab()
629 		== static_cast<TabView*>(this);
630 	if (!isFront) {
631 		base = tint_color(base, tint);
632 		tint *= 1.02;
633 	}
634 
635 	if (fOverCloseRect)
636 		tint *= 1.4;
637 	else
638 		tint *= 1.2;
639 
640 	if (fClicked && fOverCloseRect) {
641 		// Draw the button frame
642 		BRect buttonRect(closeRect.InsetByCopy(-4, -4));
643 		be_control_look->DrawButtonFrame(owner, buttonRect, updateRect,
644 			base, base,
645 			BControlLook::B_ACTIVATED | BControlLook::B_BLEND_FRAME);
646 		be_control_look->DrawButtonBackground(owner, buttonRect, updateRect,
647 			base, BControlLook::B_ACTIVATED);
648 		closeRect.OffsetBy(1, 1);
649 		tint *= 1.2;
650 	}
651 
652 	// Draw the ×
653 	base = tint_color(base, tint);
654 	owner->SetHighColor(base);
655 	owner->SetPenSize(2);
656 	owner->StrokeLine(closeRect.LeftTop(), closeRect.RightBottom());
657 	owner->StrokeLine(closeRect.LeftBottom(), closeRect.RightTop());
658 	owner->SetPenSize(1);
659 }
660 
661 
662 // #pragma mark - TabManagerController
663 
664 
665 TabManagerController::TabManagerController(TabManager* manager)
666 	:
667 	fManager(manager),
668 	fTabContainerGroup(NULL),
669 	fCloseButtonsAvailable(false),
670 	fDoubleClickOutsideTabsMessage(NULL)
671 {
672 }
673 
674 
675 TabManagerController::~TabManagerController()
676 {
677 	delete fDoubleClickOutsideTabsMessage;
678 }
679 
680 
681 TabView*
682 TabManagerController::CreateTabView()
683 {
684 	return new WebTabView(this);
685 }
686 
687 
688 void
689 TabManagerController::DoubleClickOutsideTabs()
690 {
691 	fTarget.SendMessage(fDoubleClickOutsideTabsMessage);
692 }
693 
694 
695 void
696 TabManagerController::CloseTab(int32 index)
697 {
698 	fManager->CloseTab(index);
699 }
700 
701 
702 void
703 TabManagerController::SetDoubleClickOutsideTabsMessage(const BMessage& message,
704 	const BMessenger& target)
705 {
706 	delete fDoubleClickOutsideTabsMessage;
707 	fDoubleClickOutsideTabsMessage = new BMessage(message);
708 	fTarget = target;
709 }
710 
711 
712 // #pragma mark - TabManager
713 
714 
715 TabManager::TabManager(const BMessenger& target, BMessage* newTabMessage)
716 	:
717 	fController(new TabManagerController(this)),
718 	fTarget(target)
719 {
720 	fController->SetDoubleClickOutsideTabsMessage(*newTabMessage,
721 		be_app_messenger);
722 
723 	fContainerView = new BView("web view container", 0);
724 	fCardLayout = new BCardLayout();
725 	fContainerView->SetLayout(fCardLayout);
726 
727 	fTabContainerView = new TabContainerView(fController);
728 	fTabContainerGroup = new TabContainerGroup(fTabContainerView);
729 	fTabContainerGroup->GroupLayout()->SetInsets(0, 3, 0, 0);
730 
731 	fController->SetTabContainerGroup(fTabContainerGroup);
732 
733 #if INTEGRATE_MENU_INTO_TAB_BAR
734 	fMenuContainer = new BGroupView(B_HORIZONTAL, 0);
735 	fMenuContainer->GroupLayout()->SetInsets(0, -3, 0, -3);
736 	fTabContainerGroup->GroupLayout()->AddView(fMenuContainer, 0.0f);
737 #endif
738 
739 	fTabContainerGroup->GroupLayout()->AddView(fTabContainerView);
740 	fTabContainerGroup->AddScrollLeftButton(new ScrollLeftTabButton(
741 		new BMessage(MSG_SCROLL_TABS_LEFT)));
742 	fTabContainerGroup->AddScrollRightButton(new ScrollRightTabButton(
743 		new BMessage(MSG_SCROLL_TABS_RIGHT)));
744 	NewTabButton* newTabButton = new NewTabButton(newTabMessage);
745 	newTabButton->SetTarget(be_app);
746 	fTabContainerGroup->GroupLayout()->AddView(newTabButton, 0.0f);
747 	fTabContainerGroup->AddTabMenuButton(new TabMenuTabButton(
748 		new BMessage(MSG_OPEN_TAB_MENU)));
749 }
750 
751 
752 TabManager::~TabManager()
753 {
754 	delete fController;
755 }
756 
757 
758 void
759 TabManager::SetTarget(const BMessenger& target)
760 {
761 	fTarget = target;
762 }
763 
764 
765 const BMessenger&
766 TabManager::Target() const
767 {
768 	return fTarget;
769 }
770 
771 
772 #if INTEGRATE_MENU_INTO_TAB_BAR
773 BGroupLayout*
774 TabManager::MenuContainerLayout() const
775 {
776 	return fMenuContainer->GroupLayout();
777 }
778 #endif
779 
780 
781 BView*
782 TabManager::TabGroup() const
783 {
784 	return fTabContainerGroup;
785 }
786 
787 
788 BView*
789 TabManager::GetTabContainerView() const
790 {
791 	return fTabContainerView;
792 }
793 
794 
795 BView*
796 TabManager::ContainerView() const
797 {
798 	return fContainerView;
799 }
800 
801 
802 BView*
803 TabManager::ViewForTab(int32 tabIndex) const
804 {
805 	BLayoutItem* item = fCardLayout->ItemAt(tabIndex);
806 	if (item != NULL)
807 		return item->View();
808 	return NULL;
809 }
810 
811 
812 int32
813 TabManager::TabForView(const BView* containedView) const
814 {
815 	int32 count = fCardLayout->CountItems();
816 	for (int32 i = 0; i < count; i++) {
817 		BLayoutItem* item = fCardLayout->ItemAt(i);
818 		if (item->View() == containedView)
819 			return i;
820 	}
821 	return -1;
822 }
823 
824 
825 bool
826 TabManager::HasView(const BView* containedView) const
827 {
828 	return TabForView(containedView) >= 0;
829 }
830 
831 
832 void
833 TabManager::SelectTab(int32 tabIndex)
834 {
835 	fCardLayout->SetVisibleItem(tabIndex);
836 	fTabContainerView->SelectTab(tabIndex);
837 
838 	BMessage message(TAB_CHANGED);
839 	message.AddInt32("tab index", tabIndex);
840 	fTarget.SendMessage(&message);
841 }
842 
843 
844 void
845 TabManager::SelectTab(const BView* containedView)
846 {
847 	int32 tabIndex = TabForView(containedView);
848 	if (tabIndex >= 0)
849 		SelectTab(tabIndex);
850 }
851 
852 
853 int32
854 TabManager::SelectedTabIndex() const
855 {
856 	return fCardLayout->VisibleIndex();
857 }
858 
859 
860 void
861 TabManager::CloseTab(int32 tabIndex)
862 {
863 	BMessage message(CLOSE_TAB);
864 	message.AddInt32("tab index", tabIndex);
865 	fTarget.SendMessage(&message);
866 }
867 
868 
869 void
870 TabManager::AddTab(BView* view, const char* label, int32 index)
871 {
872 	fTabContainerView->AddTab(label, index);
873 	fCardLayout->AddView(index, view);
874 }
875 
876 
877 BView*
878 TabManager::RemoveTab(int32 index)
879 {
880 	// It's important to remove the view first, since
881 	// removing the tab will preliminary mess with the selected tab
882 	// and then item count of card layout and tab container will not
883 	// match yet.
884 	BLayoutItem* item = fCardLayout->RemoveItem(index);
885 	if (item == NULL)
886 		return NULL;
887 
888 	TabView* tab = fTabContainerView->RemoveTab(index);
889 	delete tab;
890 
891 	BView* view = item->View();
892 	delete item;
893 	return view;
894 }
895 
896 
897 int32
898 TabManager::CountTabs() const
899 {
900 	return fCardLayout->CountItems();
901 }
902 
903 
904 void
905 TabManager::SetTabLabel(int32 tabIndex, const char* label)
906 {
907 	fTabContainerView->SetTabLabel(tabIndex, label);
908 }
909 
910 const BString&
911 TabManager::TabLabel(int32 tabIndex)
912 {
913 	TabView* tab = fTabContainerView->TabAt(tabIndex);
914 	if (tab)
915 		return tab->Label();
916 	else
917 		return kEmptyString;
918 }
919 
920 void
921 TabManager::SetTabIcon(const BView* containedView, const BBitmap* icon)
922 {
923 	WebTabView* tab = dynamic_cast<WebTabView*>(fTabContainerView->TabAt(
924 		TabForView(containedView)));
925 	if (tab)
926 		tab->SetIcon(icon);
927 }
928 
929 
930 void
931 TabManager::SetCloseButtonsAvailable(bool available)
932 {
933 	if (available == fController->CloseButtonsAvailable())
934 		return;
935 	fController->SetCloseButtonsAvailable(available);
936 	fTabContainerView->Invalidate();
937 }
938