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