xref: /haiku/src/apps/webpositive/tabview/TabManager.cpp (revision eea5774f46bba925156498abf9cb1a1165647bf7)
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 
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 
682 TabManagerController::TabManagerController(TabManager* manager)
683 	:
684 	fManager(manager),
685 	fTabContainerGroup(NULL),
686 	fCloseButtonsAvailable(false),
687 	fDoubleClickOutsideTabsMessage(NULL)
688 {
689 }
690 
691 
692 TabManagerController::~TabManagerController()
693 {
694 	delete fDoubleClickOutsideTabsMessage;
695 }
696 
697 
698 TabView*
699 TabManagerController::CreateTabView()
700 {
701 	return new WebTabView(this);
702 }
703 
704 
705 void
706 TabManagerController::DoubleClickOutsideTabs()
707 {
708 	fTarget.SendMessage(fDoubleClickOutsideTabsMessage);
709 }
710 
711 
712 void
713 TabManagerController::CloseTab(int32 index)
714 {
715 	fManager->CloseTab(index);
716 }
717 
718 
719 void
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 
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 
769 TabManager::~TabManager()
770 {
771 	delete fController;
772 }
773 
774 
775 void
776 TabManager::SetTarget(const BMessenger& target)
777 {
778 	fTarget = target;
779 }
780 
781 
782 const BMessenger&
783 TabManager::Target() const
784 {
785 	return fTarget;
786 }
787 
788 
789 #if INTEGRATE_MENU_INTO_TAB_BAR
790 BGroupLayout*
791 TabManager::MenuContainerLayout() const
792 {
793 	return fMenuContainer->GroupLayout();
794 }
795 #endif
796 
797 
798 BView*
799 TabManager::TabGroup() const
800 {
801 	return fTabContainerGroup;
802 }
803 
804 
805 BView*
806 TabManager::GetTabContainerView() const
807 {
808 	return fTabContainerView;
809 }
810 
811 
812 BView*
813 TabManager::ContainerView() const
814 {
815 	return fContainerView;
816 }
817 
818 
819 BView*
820 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
830 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
843 TabManager::HasView(const BView* containedView) const
844 {
845 	return TabForView(containedView) >= 0;
846 }
847 
848 
849 void
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
862 TabManager::SelectTab(const BView* containedView)
863 {
864 	int32 tabIndex = TabForView(containedView);
865 	if (tabIndex >= 0)
866 		SelectTab(tabIndex);
867 }
868 
869 
870 int32
871 TabManager::SelectedTabIndex() const
872 {
873 	return fCardLayout->VisibleIndex();
874 }
875 
876 
877 void
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
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*
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
915 TabManager::CountTabs() const
916 {
917 	return fCardLayout->CountItems();
918 }
919 
920 
921 void
922 TabManager::SetTabLabel(int32 tabIndex, const char* label)
923 {
924 	fTabContainerView->SetTabLabel(tabIndex, label);
925 }
926 
927 const BString&
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
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
948 TabManager::SetCloseButtonsAvailable(bool available)
949 {
950 	if (available == fController->CloseButtonsAvailable())
951 		return;
952 	fController->SetCloseButtonsAvailable(available);
953 	fTabContainerView->Invalidate();
954 }
955