xref: /haiku/src/apps/webpositive/tabview/TabManager.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 <Application.h>
13 #include <AbstractLayoutItem.h>
14 #include <Bitmap.h>
15 #include <Button.h>
16 #include <CardLayout.h>
17 #include <ControlLook.h>
18 #include <Catalog.h>
19 #include <GroupView.h>
20 #include <MenuBar.h>
21 #include <MenuItem.h>
22 #include <PopUpMenu.h>
23 #include <Rect.h>
24 #include <SpaceLayoutItem.h>
25 #include <Window.h>
26 
27 #include "TabContainerView.h"
28 #include "TabView.h"
29 
30 
31 #undef B_TRANSLATION_CONTEXT
32 #define B_TRANSLATION_CONTEXT "Tab Manager"
33 
34 
35 const static BString kEmptyString;
36 
37 
38 // #pragma mark - Helper classes
39 
40 
41 class TabButton : public BButton {
42 public:
43 	TabButton(BMessage* message)
44 		: BButton("", message)
45 	{
46 	}
47 
48 	virtual BSize MinSize()
49 	{
50 		return BSize(12, 12);
51 	}
52 
53 	virtual BSize MaxSize()
54 	{
55 		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
56 	}
57 
58 	virtual BSize PreferredSize()
59 	{
60 		return MinSize();
61 	}
62 
63 	virtual void Draw(BRect updateRect)
64 	{
65 		BRect bounds(Bounds());
66 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
67 		SetHighColor(tint_color(base, B_DARKEN_2_TINT));
68 		StrokeLine(bounds.LeftBottom(), bounds.RightBottom());
69 		bounds.bottom--;
70 		uint32 flags = be_control_look->Flags(this);
71 		uint32 borders = BControlLook::B_TOP_BORDER
72 			| BControlLook::B_BOTTOM_BORDER;
73 		be_control_look->DrawInactiveTab(this, bounds, updateRect, base,
74 			0, borders);
75 		if (IsEnabled()) {
76 			rgb_color button = tint_color(base, 1.07);
77 			be_control_look->DrawButtonBackground(this, bounds, updateRect,
78 				button, flags, 0);
79 		}
80 
81 		bounds.left = (bounds.left + bounds.right) / 2 - 6;
82 		bounds.top = (bounds.top + bounds.bottom) / 2 - 6;
83 		bounds.right = bounds.left + 12;
84 		bounds.bottom = bounds.top + 12;
85 		DrawSymbol(bounds, 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 		switch (message->what) {
244 			case MSG_SCROLL_TABS_LEFT:
245 				fTabContainerView->SetFirstVisibleTabIndex(
246 					fTabContainerView->FirstVisibleTabIndex() - 1);
247 				break;
248 			case MSG_SCROLL_TABS_RIGHT:
249 				fTabContainerView->SetFirstVisibleTabIndex(
250 					fTabContainerView->FirstVisibleTabIndex() + 1);
251 				break;
252 			case MSG_OPEN_TAB_MENU:
253 			{
254 				BPopUpMenu* tabMenu = new BPopUpMenu("tab menu", true, false);
255 				int tabCount = fTabContainerView->GetLayout()->CountItems();
256 				for (int i = 0; i < tabCount; i++) {
257 					TabView* tab = fTabContainerView->TabAt(i);
258 					if (tab) {
259 						BMenuItem* item = new BMenuItem(tab->Label(), NULL);
260 						tabMenu->AddItem(item);
261 						if (tab->IsFront())
262 							item->SetMarked(true);
263 					}
264 				}
265 
266 				// Force layout to get the final menu size. InvalidateLayout()
267 				// did not seem to work here.
268 				tabMenu->AttachedToWindow();
269 				BRect buttonFrame = fTabMenuButton->Frame();
270 				BRect menuFrame = tabMenu->Frame();
271 				BPoint openPoint = ConvertToScreen(buttonFrame.LeftBottom());
272 				// Open with the right side of the menu aligned with the right
273 				// side of the button and a little below.
274 				openPoint.x -= menuFrame.Width() - buttonFrame.Width();
275 				openPoint.y += 2;
276 
277 				BMenuItem *selected = tabMenu->Go(openPoint, false, false,
278 					ConvertToScreen(buttonFrame));
279 				if (selected) {
280 					selected->SetMarked(true);
281 					int32 index = tabMenu->IndexOf(selected);
282 					if (index != B_ERROR)
283 						fTabContainerView->SelectTab(index);
284 				}
285 				fTabMenuButton->MenuClosed();
286 				delete tabMenu;
287 
288 				break;
289 			}
290 			default:
291 				BGroupView::MessageReceived(message);
292 				break;
293 		}
294 	}
295 
296 	void AddScrollLeftButton(TabButton* button)
297 	{
298 		fScrollLeftTabButton = button;
299 		GroupLayout()->AddView(button, 0.0f);
300 	}
301 
302 	void AddScrollRightButton(TabButton* button)
303 	{
304 		fScrollRightTabButton = button;
305 		GroupLayout()->AddView(button, 0.0f);
306 	}
307 
308 	void AddTabMenuButton(TabMenuTabButton* button)
309 	{
310 		fTabMenuButton = button;
311 		GroupLayout()->AddView(button, 0.0f);
312 	}
313 
314 	void EnableScrollButtons(bool canScrollLeft, bool canScrollRight)
315 	{
316 		fScrollLeftTabButton->SetEnabled(canScrollLeft);
317 		fScrollRightTabButton->SetEnabled(canScrollRight);
318 		if (!canScrollLeft && !canScrollRight) {
319 			// hide scroll buttons
320 		} else {
321 			// show scroll buttons
322 		}
323 	}
324 
325 private:
326 	TabContainerView*	fTabContainerView;
327 	TabButton*			fScrollLeftTabButton;
328 	TabButton*			fScrollRightTabButton;
329 	TabMenuTabButton*	fTabMenuButton;
330 };
331 
332 
333 class TabButtonContainer : public BGroupView {
334 public:
335 	TabButtonContainer()
336 		:
337 		BGroupView(B_HORIZONTAL, 0.0)
338 	{
339 		SetFlags(Flags() | B_WILL_DRAW);
340 		SetViewColor(B_TRANSPARENT_COLOR);
341 		SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
342 		GroupLayout()->SetInsets(0, 6, 0, 0);
343 	}
344 
345 	virtual void Draw(BRect updateRect)
346 	{
347 		BRect bounds(Bounds());
348 		rgb_color base = LowColor();
349 		be_control_look->DrawInactiveTab(this, bounds, updateRect,
350 			base, 0, BControlLook::B_TOP_BORDER);
351 	}
352 };
353 
354 
355 class TabManagerController : public TabContainerView::Controller {
356 public:
357 	TabManagerController(TabManager* manager);
358 
359 	virtual ~TabManagerController();
360 
361 	virtual void TabSelected(int32 index)
362 	{
363 		fManager->SelectTab(index);
364 	}
365 
366 	virtual bool HasFrames()
367 	{
368 		return false;
369 	}
370 
371 	virtual TabView* CreateTabView();
372 
373 	virtual void DoubleClickOutsideTabs();
374 
375 	virtual void UpdateTabScrollability(bool canScrollLeft,
376 		bool canScrollRight)
377 	{
378 		fTabContainerGroup->EnableScrollButtons(canScrollLeft, canScrollRight);
379 	}
380 
381 	virtual	void SetToolTip(const BString& text)
382 	{
383 		if (fCurrentToolTip == text)
384 			return;
385 		fCurrentToolTip = text;
386 		fManager->GetTabContainerView()->HideToolTip();
387 		fManager->GetTabContainerView()->SetToolTip(
388 			reinterpret_cast<BToolTip*>(NULL));
389 		fManager->GetTabContainerView()->SetToolTip(fCurrentToolTip.String());
390 	}
391 
392 	void CloseTab(int32 index);
393 
394 	void SetCloseButtonsAvailable(bool available)
395 	{
396 		fCloseButtonsAvailable = available;
397 	}
398 
399 	bool CloseButtonsAvailable() const
400 	{
401 		return fCloseButtonsAvailable;
402 	}
403 
404 	void SetDoubleClickOutsideTabsMessage(const BMessage& message,
405 		const BMessenger& target);
406 
407 	void SetTabContainerGroup(TabContainerGroup* tabContainerGroup)
408 	{
409 		fTabContainerGroup = tabContainerGroup;
410 	}
411 
412 private:
413 	TabManager*			fManager;
414 	TabContainerGroup*	fTabContainerGroup;
415 	bool				fCloseButtonsAvailable;
416 	BMessage*			fDoubleClickOutsideTabsMessage;
417 	BMessenger			fTarget;
418 	BString				fCurrentToolTip;
419 };
420 
421 
422 // #pragma mark - WebTabView
423 
424 
425 class WebTabView : public TabView {
426 public:
427 	WebTabView(TabManagerController* controller);
428 	~WebTabView();
429 
430 	virtual BSize MaxSize();
431 
432 	virtual void DrawContents(BView* owner, BRect frame, const BRect& updateRect,
433 		bool isFirst, bool isLast, bool isFront);
434 
435 	virtual void MouseDown(BPoint where, uint32 buttons);
436 	virtual void MouseUp(BPoint where);
437 	virtual void MouseMoved(BPoint where, uint32 transit,
438 		const BMessage* dragMessage);
439 
440 	void SetIcon(const BBitmap* icon);
441 
442 private:
443 	void _DrawCloseButton(BView* owner, BRect& frame, const BRect& updateRect,
444 		bool isFirst, bool isLast, bool isFront);
445 	BRect _CloseRectFrame(BRect frame) const;
446 
447 private:
448 	BBitmap* fIcon;
449 	TabManagerController* fController;
450 	bool fOverCloseRect;
451 	bool fClicked;
452 };
453 
454 
455 WebTabView::WebTabView(TabManagerController* controller)
456 	:
457 	TabView(),
458 	fIcon(NULL),
459 	fController(controller),
460 	fOverCloseRect(false),
461 	fClicked(false)
462 {
463 }
464 
465 
466 WebTabView::~WebTabView()
467 {
468 	delete fIcon;
469 }
470 
471 
472 static const int kIconSize = 18;
473 static const int kIconInset = 3;
474 
475 
476 BSize
477 WebTabView::MaxSize()
478 {
479 	// Account for icon.
480 	BSize size(TabView::MaxSize());
481 	size.height = max_c(size.height, kIconSize + kIconInset * 2);
482 	if (fIcon)
483 		size.width += kIconSize + kIconInset * 2;
484 	// Account for close button.
485 	size.width += size.height;
486 	return size;
487 }
488 
489 
490 void
491 WebTabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect,
492 	bool isFirst, bool isLast, bool isFront)
493 {
494 	if (fController->CloseButtonsAvailable())
495 		_DrawCloseButton(owner, frame, updateRect, isFirst, isLast, isFront);
496 
497 	if (fIcon) {
498 		BRect iconBounds(0, 0, kIconSize - 1, kIconSize - 1);
499 		// clip to icon bounds, if they are smaller
500 		if (iconBounds.Contains(fIcon->Bounds()))
501 			iconBounds = fIcon->Bounds();
502 		else {
503 			// Try to scale down the icon by an even factor so the
504 			// final size is between 14 and 18 pixel size. If this fails,
505 			// the icon will simply be displayed at 18x18.
506 			float scale = 2;
507 			while ((fIcon->Bounds().Width() + 1) / scale > kIconSize)
508 				scale *= 2;
509 			if ((fIcon->Bounds().Width() + 1) / scale >= kIconSize - 4
510 				&& (fIcon->Bounds().Height() + 1) / scale >= kIconSize - 4
511 				&& (fIcon->Bounds().Height() + 1) / scale <= kIconSize) {
512 				iconBounds.right = (fIcon->Bounds().Width() + 1) / scale - 1;
513 				iconBounds.bottom = (fIcon->Bounds().Height() + 1) / scale - 1;
514 			}
515 		}
516 		BPoint iconPos(frame.left + kIconInset - 1,
517 			frame.top + floorf((frame.Height() - iconBounds.Height()) / 2));
518 		iconBounds.OffsetTo(iconPos);
519 		owner->SetDrawingMode(B_OP_ALPHA);
520 		owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
521 		owner->DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
522 			B_FILTER_BITMAP_BILINEAR);
523 		owner->SetDrawingMode(B_OP_COPY);
524 		frame.left = frame.left + kIconSize + kIconInset * 2;
525 	}
526 
527 	TabView::DrawContents(owner, frame, updateRect, isFirst, isLast, isFront);
528 }
529 
530 
531 void
532 WebTabView::MouseDown(BPoint where, uint32 buttons)
533 {
534 	if (buttons & B_TERTIARY_MOUSE_BUTTON) {
535 		// Immediately close tab
536 		fController->CloseTab(ContainerView()->IndexOf(this));
537 		return;
538 	}
539 
540 	BRect closeRect = _CloseRectFrame(Frame());
541 	if (!fController->CloseButtonsAvailable() || !closeRect.Contains(where)) {
542 		TabView::MouseDown(where, buttons);
543 		return;
544 	}
545 
546 	fClicked = true;
547 	ContainerView()->Invalidate(closeRect);
548 }
549 
550 
551 void
552 WebTabView::MouseUp(BPoint where)
553 {
554 	if (!fClicked) {
555 		TabView::MouseUp(where);
556 		return;
557 	}
558 
559 	fClicked = false;
560 
561 	if (_CloseRectFrame(Frame()).Contains(where))
562 		fController->CloseTab(ContainerView()->IndexOf(this));
563 }
564 
565 
566 void
567 WebTabView::MouseMoved(BPoint where, uint32 transit,
568 	const BMessage* dragMessage)
569 {
570 	if (fController->CloseButtonsAvailable()) {
571 		BRect closeRect = _CloseRectFrame(Frame());
572 		bool overCloseRect = closeRect.Contains(where);
573 		if (overCloseRect != fOverCloseRect) {
574 			fOverCloseRect = overCloseRect;
575 			ContainerView()->Invalidate(closeRect);
576 		}
577 	}
578 
579 	// Set the tool tip
580 	fController->SetToolTip(Label());
581 
582 	TabView::MouseMoved(where, transit, dragMessage);
583 }
584 
585 
586 void
587 WebTabView::SetIcon(const BBitmap* icon)
588 {
589 	delete fIcon;
590 	if (icon)
591 		fIcon = new BBitmap(icon);
592 	else
593 		fIcon = NULL;
594 	LayoutItem()->InvalidateLayout();
595 }
596 
597 
598 BRect
599 WebTabView::_CloseRectFrame(BRect frame) const
600 {
601 	frame.left = frame.right - frame.Height();
602 	return frame;
603 }
604 
605 
606 void WebTabView::_DrawCloseButton(BView* owner, BRect& frame,
607 	const BRect& updateRect, bool isFirst, bool isLast, bool isFront)
608 {
609 	BRect closeRect = _CloseRectFrame(frame);
610 	frame.right = closeRect.left - be_control_look->DefaultLabelSpacing();
611 
612 	closeRect.left = (closeRect.left + closeRect.right) / 2 - 3;
613 	closeRect.right = closeRect.left + 6;
614 	closeRect.top = (closeRect.top + closeRect.bottom) / 2 - 3;
615 	closeRect.bottom = closeRect.top + 6;
616 
617 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
618 	float tint = B_DARKEN_1_TINT;
619 	if (!IsFront()) {
620 		base = tint_color(base, tint);
621 		tint *= 1.02;
622 	}
623 
624 	if (fOverCloseRect)
625 		tint *= 1.4;
626 	else
627 		tint *= 1.2;
628 
629 	if (fClicked && fOverCloseRect) {
630 		// Draw the button frame
631 		BRect buttonRect(closeRect.InsetByCopy(-4, -4));
632 		be_control_look->DrawButtonFrame(owner, buttonRect, updateRect,
633 			base, base,
634 			BControlLook::B_ACTIVATED | BControlLook::B_BLEND_FRAME);
635 		be_control_look->DrawButtonBackground(owner, buttonRect, updateRect,
636 			base, BControlLook::B_ACTIVATED);
637 		closeRect.OffsetBy(1, 1);
638 		tint *= 1.2;
639 	}
640 
641 	// Draw the ×
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