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