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