xref: /haiku/src/apps/terminal/SmartTabView.cpp (revision 4720c31bb08f5c6d1c8ddb616463c6fba9b350a1)
1 /*
2  * Copyright 2007-2009, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  *	Authors:
6  *		Stefano Ceccherini (burton666@libero.it)
7  */
8 
9 #include "SmartTabView.h"
10 
11 #include <MenuItem.h>
12 #include <Message.h>
13 #include <Messenger.h>
14 #include <PopUpMenu.h>
15 #include <Screen.h>
16 #include <Window.h>
17 
18 #include <stdio.h>
19 
20 const static uint32 kCloseTab = 'ClTb';
21 
22 SmartTabView::SmartTabView(BRect frame, const char *name, button_width width,
23 				uint32 resizingMode, uint32 flags)
24 	:
25 	BTabView(frame, name, width, resizingMode, flags),
26 	fInsets(0, 0, 0, 0)
27 {
28 	// Resize the container view to fill the complete tab view for single-tab
29 	// mode. Later, when more than one tab is added, we shrink the container
30 	// view again.
31 	frame.OffsetTo(B_ORIGIN);
32 	ContainerView()->MoveTo(frame.LeftTop());
33 	ContainerView()->ResizeTo(frame.Width(), frame.Height());
34 }
35 
36 
37 SmartTabView::~SmartTabView()
38 {
39 }
40 
41 
42 void
43 SmartTabView::SetInsets(float left, float top, float right, float bottom)
44 {
45 	fInsets.left = left;
46 	fInsets.top = top;
47 	fInsets.right = right;
48 	fInsets.bottom = bottom;
49 }
50 
51 
52 void
53 SmartTabView::MouseDown(BPoint point)
54 {
55 	bool handled = false;
56 
57 	if (CountTabs() > 1) {
58 		int32 tabIndex = _ClickedTabIndex(point);
59 		if (tabIndex >= 0) {
60 			int32 buttons;
61 			Window()->CurrentMessage()->FindInt32("buttons", &buttons);
62 			if (buttons & B_SECONDARY_MOUSE_BUTTON) {
63 				BMessage *message = new BMessage(kCloseTab);
64 				message->AddInt32("index", tabIndex);
65 
66 				BPopUpMenu *popUpMenu = new BPopUpMenu("tab menu");
67 				popUpMenu->AddItem(new BMenuItem("Close Tab", message));
68 				popUpMenu->SetAsyncAutoDestruct(true);
69 				popUpMenu->SetTargetForItems(BMessenger(this));
70 				popUpMenu->Go(ConvertToScreen(point), true, true, true);
71 
72 				handled = true;
73 			} else if (buttons & B_TERTIARY_MOUSE_BUTTON) {
74 				RemoveAndDeleteTab(tabIndex);
75 				handled = true;
76 			}
77 		}
78 	}
79 
80 	if (!handled)
81 		BTabView::MouseDown(point);
82 
83 }
84 
85 
86 void
87 SmartTabView::AttachedToWindow()
88 {
89 	BTabView::AttachedToWindow();
90 }
91 
92 
93 void
94 SmartTabView::AllAttached()
95 {
96 	BTabView::AllAttached();
97 }
98 
99 
100 void
101 SmartTabView::MessageReceived(BMessage *message)
102 {
103 	switch (message->what) {
104 		case kCloseTab:
105 		{
106 			int32 tabIndex = 0;
107 			if (message->FindInt32("index", &tabIndex) == B_OK)
108 				RemoveAndDeleteTab(tabIndex);
109 			break;
110 		}
111 		default:
112 			BTabView::MessageReceived(message);
113 			break;
114 	}
115 }
116 
117 
118 void
119 SmartTabView::Select(int32 index)
120 {
121 	BTabView::Select(index);
122 	BView *view = ViewForTab(index);
123 	if (view != NULL) {
124 		view->MoveTo(fInsets.LeftTop());
125 		view->ResizeTo(ContainerView()->Bounds().Width()
126 				- fInsets.left - fInsets.right,
127 			ContainerView()->Bounds().Height() - fInsets.top - fInsets.bottom);
128 	}
129 }
130 
131 
132 void
133 SmartTabView::RemoveAndDeleteTab(int32 index)
134 {
135 	// Select another tab
136 	if (index == Selection()) {
137 		if (index > 0)
138 			Select(index - 1);
139 		else if (index < CountTabs())
140 			Select(index + 1);
141 	}
142 	delete RemoveTab(index);
143 }
144 
145 
146 void
147 SmartTabView::AddTab(BView *target, BTab *tab)
148 {
149 	if (target == NULL)
150 		return;
151 
152 	BTabView::AddTab(target, tab);
153 
154 	if (CountTabs() == 1) {
155 		// Call select on the tab, since
156 		// we're resizing the contained view
157 		// inside that function
158 		Select(0);
159 	} else if (CountTabs() == 2) {
160 		// Need to resize the view, since we're
161 		// switching from "normal" to tabbed mode
162 		ContainerView()->ResizeBy(0, -TabHeight());
163 		ContainerView()->MoveBy(0, TabHeight());
164 
165 		BScreen screen(Window());
166 		if (Window()->Frame().bottom + TabHeight() > screen.Frame().bottom - 5)
167 			Window()->MoveBy(0, -TabHeight());
168 
169 		Window()->ResizeBy(0, TabHeight());
170 	}
171 
172 	Invalidate(TabFrame(CountTabs() - 1).InsetByCopy(-2, -2));
173 }
174 
175 
176 BTab *
177 SmartTabView::RemoveTab(int32 index)
178 {
179 	if (CountTabs() == 2) {
180 		// see above
181 		BScreen screen(Window());
182 		if (Window()->Frame().bottom > screen.Frame().bottom - 5 - TabHeight())
183 			Window()->MoveBy(0, TabHeight());
184 		Window()->ResizeBy(0, -TabHeight());
185 		ContainerView()->MoveBy(0, -TabHeight());
186 		ContainerView()->ResizeBy(0, TabHeight());
187 	}
188 	return BTabView::RemoveTab(index);
189 }
190 
191 
192 BRect
193 SmartTabView::DrawTabs()
194 {
195 	if (CountTabs() > 1)
196 		return BTabView::DrawTabs();
197 	return BRect();
198 }
199 
200 
201 int32
202 SmartTabView::_ClickedTabIndex(const BPoint &point)
203 {
204 	if (point.y <= TabHeight()) {
205 		for (int32 i = 0; i < CountTabs(); i++) {
206 			if (TabFrame(i).Contains(point))
207 				return i;
208 		}
209 	}
210 
211 	return -1;
212 }
213