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