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
37const static BString kEmptyString;
38
39
40// #pragma mark - Helper classes
41
42
43class TabButton : public BButton {
44public:
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
95class ScrollLeftTabButton : public TabButton {
96public:
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
112class ScrollRightTabButton : public TabButton {
113public:
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
130class NewTabButton : public TabButton {
131public:
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
159class TabMenuTabButton : public TabButton {
160public:
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
207private:
208	bigtime_t fCloseTime;
209};
210
211
212enum {
213	MSG_SCROLL_TABS_LEFT	= 'stlt',
214	MSG_SCROLL_TABS_RIGHT	= 'strt',
215	MSG_OPEN_TAB_MENU		= 'otmn'
216};
217
218
219class TabContainerGroup : public BGroupView {
220public:
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
334private:
335	TabContainerView*	fTabContainerView;
336	TabButton*			fScrollLeftTabButton;
337	TabButton*			fScrollRightTabButton;
338	TabMenuTabButton*	fTabMenuButton;
339};
340
341
342class TabButtonContainer : public BGroupView {
343public:
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
361class TabManagerController : public TabContainerView::Controller {
362public:
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
419private:
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
432class WebTabView : public TabView {
433public:
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
449private:
450	void _DrawCloseButton(BView* owner, BRect& frame, const BRect& updateRect);
451	BRect _CloseRectFrame(BRect frame) const;
452
453private:
454	BBitmap* fIcon;
455	TabManagerController* fController;
456	bool fOverCloseRect;
457	bool fClicked;
458};
459
460
461WebTabView::WebTabView(TabManagerController* controller)
462	:
463	TabView(),
464	fIcon(NULL),
465	fController(controller),
466	fOverCloseRect(false),
467	fClicked(false)
468{
469}
470
471
472WebTabView::~WebTabView()
473{
474	delete fIcon;
475}
476
477
478static const int kIconSize = 18;
479static const int kIconInset = 3;
480
481
482BSize
483WebTabView::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
496void
497WebTabView::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
538void
539WebTabView::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
558void
559WebTabView::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
573void
574WebTabView::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
593void
594WebTabView::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
605BRect
606WebTabView::_CloseRectFrame(BRect frame) const
607{
608	frame.left = frame.right - frame.Height();
609	return frame;
610}
611
612
613void
614WebTabView::_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
665TabManagerController::TabManagerController(TabManager* manager)
666	:
667	fManager(manager),
668	fTabContainerGroup(NULL),
669	fCloseButtonsAvailable(false),
670	fDoubleClickOutsideTabsMessage(NULL)
671{
672}
673
674
675TabManagerController::~TabManagerController()
676{
677	delete fDoubleClickOutsideTabsMessage;
678}
679
680
681TabView*
682TabManagerController::CreateTabView()
683{
684	return new WebTabView(this);
685}
686
687
688void
689TabManagerController::DoubleClickOutsideTabs()
690{
691	fTarget.SendMessage(fDoubleClickOutsideTabsMessage);
692}
693
694
695void
696TabManagerController::CloseTab(int32 index)
697{
698	fManager->CloseTab(index);
699}
700
701
702void
703TabManagerController::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
715TabManager::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
752TabManager::~TabManager()
753{
754	delete fController;
755}
756
757
758void
759TabManager::SetTarget(const BMessenger& target)
760{
761	fTarget = target;
762}
763
764
765const BMessenger&
766TabManager::Target() const
767{
768	return fTarget;
769}
770
771
772#if INTEGRATE_MENU_INTO_TAB_BAR
773BGroupLayout*
774TabManager::MenuContainerLayout() const
775{
776	return fMenuContainer->GroupLayout();
777}
778#endif
779
780
781BView*
782TabManager::TabGroup() const
783{
784	return fTabContainerGroup;
785}
786
787
788BView*
789TabManager::GetTabContainerView() const
790{
791	return fTabContainerView;
792}
793
794
795BView*
796TabManager::ContainerView() const
797{
798	return fContainerView;
799}
800
801
802BView*
803TabManager::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
812int32
813TabManager::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
825bool
826TabManager::HasView(const BView* containedView) const
827{
828	return TabForView(containedView) >= 0;
829}
830
831
832void
833TabManager::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
844void
845TabManager::SelectTab(const BView* containedView)
846{
847	int32 tabIndex = TabForView(containedView);
848	if (tabIndex >= 0)
849		SelectTab(tabIndex);
850}
851
852
853int32
854TabManager::SelectedTabIndex() const
855{
856	return fCardLayout->VisibleIndex();
857}
858
859
860void
861TabManager::CloseTab(int32 tabIndex)
862{
863	BMessage message(CLOSE_TAB);
864	message.AddInt32("tab index", tabIndex);
865	fTarget.SendMessage(&message);
866}
867
868
869void
870TabManager::AddTab(BView* view, const char* label, int32 index)
871{
872	fTabContainerView->AddTab(label, index);
873	fCardLayout->AddView(index, view);
874}
875
876
877BView*
878TabManager::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
897int32
898TabManager::CountTabs() const
899{
900	return fCardLayout->CountItems();
901}
902
903
904void
905TabManager::SetTabLabel(int32 tabIndex, const char* label)
906{
907	fTabContainerView->SetTabLabel(tabIndex, label);
908}
909
910const BString&
911TabManager::TabLabel(int32 tabIndex)
912{
913	TabView* tab = fTabContainerView->TabAt(tabIndex);
914	if (tab)
915		return tab->Label();
916	else
917		return kEmptyString;
918}
919
920void
921TabManager::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
930void
931TabManager::SetCloseButtonsAvailable(bool available)
932{
933	if (available == fController->CloseButtonsAvailable())
934		return;
935	fController->SetCloseButtonsAvailable(available);
936	fTabContainerView->Invalidate();
937}
938