xref: /haiku/src/apps/terminal/SmartTabView.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2007-2010, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  *	Authors:
6  *		Stefano Ceccherini (burton666@libero.it)
7  *		Ingo Weinhold (ingo_weinhold@gmx.de)
8  */
9 
10 
11 /*!	The SmartTabView class is a BTabView descendant that hides the tab bar
12 	as long as there is only a single tab.
13 	Furthermore, it provides a tab context menu, as well as allowing you to
14 	close buttons with the middle mouse button.
15 */
16 
17 
18 #include "SmartTabView.h"
19 
20 #include <stdio.h>
21 
22 #include <BitmapButton.h>
23 #include <Button.h>
24 #include <Catalog.h>
25 #include <Locale.h>
26 #include <Message.h>
27 #include <Messenger.h>
28 #include <Screen.h>
29 #include <ScrollView.h>
30 #include <Window.h>
31 
32 #include "TermConst.h"
33 #include "WindowIcon.h"
34 
35 
36 // #pragma mark - SmartTabView
37 
38 
39 SmartTabView::SmartTabView(BRect frame, const char* name, button_width width,
40 		uint32 resizingMode, uint32 flags)
41 	:
42 	BTabView(frame, name, width, resizingMode, flags),
43 	fInsets(0, 0, 0, 0),
44 	fScrollView(NULL),
45 	fListener(NULL)
46 {
47 	// Resize the container view to fill the complete tab view for single-tab
48 	// mode. Later, when more than one tab is added, we shrink the container
49 	// view again.
50 	frame.OffsetTo(B_ORIGIN);
51 	ContainerView()->MoveTo(frame.LeftTop());
52 	ContainerView()->ResizeTo(frame.Width(), frame.Height());
53 
54 	BRect buttonRect(frame);
55 	buttonRect.left = frame.right - B_V_SCROLL_BAR_WIDTH + 1;
56 	buttonRect.bottom = frame.top + TabHeight() - 1;
57 	fFullScreenButton = new BBitmapButton(kWindowIconBits, kWindowIconWidth,
58 		kWindowIconHeight, kWindowIconFormat, new BMessage(FULLSCREEN));
59 	fFullScreenButton->SetResizingMode(B_FOLLOW_TOP | B_FOLLOW_RIGHT);
60 	fFullScreenButton->MoveTo(buttonRect.LeftTop());
61 	fFullScreenButton->ResizeTo(buttonRect.Width(), buttonRect.Height());
62 	fFullScreenButton->Hide();
63 
64 	AddChild(fFullScreenButton);
65 }
66 
67 
68 SmartTabView::~SmartTabView()
69 {
70 }
71 
72 
73 void
74 SmartTabView::SetInsets(float left, float top, float right, float bottom)
75 {
76 	fInsets.left = left;
77 	fInsets.top = top;
78 	fInsets.right = right;
79 	fInsets.bottom = bottom;
80 }
81 
82 
83 void
84 SmartTabView::MouseDown(BPoint point)
85 {
86 	bool handled = false;
87 
88 	if (CountTabs() > 1) {
89 		int32 tabIndex = _ClickedTabIndex(point);
90 		int32 buttons = 0;
91 		int32 clickCount = 0;
92 		Window()->CurrentMessage()->FindInt32("buttons", &buttons);
93 		Window()->CurrentMessage()->FindInt32("clicks", &clickCount);
94 
95 		if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0 && clickCount == 2) {
96 			if (fListener != NULL)
97 				fListener->TabDoubleClicked(this, point, tabIndex);
98 		} else if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
99 			if (fListener != NULL)
100 				fListener->TabRightClicked(this, point, tabIndex);
101 			handled = true;
102 		} else if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) {
103 			if (fListener != NULL)
104 				fListener->TabMiddleClicked(this, point, tabIndex);
105 			handled = true;
106 		}
107 	}
108 
109 	if (!handled)
110 		BTabView::MouseDown(point);
111 }
112 
113 
114 void
115 SmartTabView::AttachedToWindow()
116 {
117 	BTabView::AttachedToWindow();
118 }
119 
120 
121 void
122 SmartTabView::AllAttached()
123 {
124 	BTabView::AllAttached();
125 }
126 
127 
128 void
129 SmartTabView::Select(int32 index)
130 {
131 	BTabView::Select(index);
132 	BView *view = ViewForTab(index);
133 	if (view != NULL) {
134 		view->MoveTo(fInsets.LeftTop());
135 		view->ResizeTo(ContainerView()->Bounds().Width()
136 				- fInsets.left - fInsets.right,
137 			ContainerView()->Bounds().Height() - fInsets.top - fInsets.bottom);
138 	}
139 
140 	if (fListener != NULL)
141 		fListener->TabSelected(this, index);
142 }
143 
144 
145 void
146 SmartTabView::AddTab(BView* target, BTab* tab)
147 {
148 	if (target == NULL)
149 		return;
150 
151 	BTabView::AddTab(target, tab);
152 
153 	if (CountTabs() == 1) {
154 		// Call select on the tab, since
155 		// we're resizing the contained view
156 		// inside that function
157 		Select(0);
158 	} else if (CountTabs() == 2) {
159 		// Need to resize the view, since we're switching from "normal"
160 		// to tabbed mode
161 		ContainerView()->ResizeBy(0, -TabHeight());
162 		ContainerView()->MoveBy(0, TabHeight());
163 		fFullScreenButton->Show();
164 
165 		// Make sure the content size stays the same, but take special care
166 		// of full screen mode
167 		BScreen screen(Window());
168 		if (Window()->DecoratorFrame().Height() + 2 * TabHeight()
169 				< screen.Frame().Height()) {
170 			if (Window()->Frame().bottom + TabHeight()
171 				> screen.Frame().bottom - 5) {
172 				Window()->MoveBy(0, -TabHeight());
173 			}
174 
175 			Window()->ResizeBy(0, TabHeight());
176 		}
177 
178 		// Adapt scroll bar if there is one
179 		if (fScrollView != NULL) {
180 			BScrollBar* bar = fScrollView->ScrollBar(B_VERTICAL);
181 			if (bar != NULL) {
182 				bar->ResizeBy(0, -1);
183 				bar->MoveBy(0, 1);
184 			}
185 		}
186 
187 		SetBorder(B_NO_BORDER);
188 	}
189 
190 	Invalidate(TabFrame(CountTabs() - 1).InsetByCopy(-2, -2));
191 }
192 
193 
194 BTab*
195 SmartTabView::RemoveTab(int32 index)
196 {
197 	if (CountTabs() == 2) {
198 		// Hide the tab bar again by resizing the container view
199 
200 		// Make sure the content size stays the same, but take special care
201 		// of full screen mode
202 		BScreen screen(Window());
203 		if (Window()->DecoratorFrame().Height() + 2 * TabHeight()
204 				< screen.Frame().Height()) {
205 			if (Window()->Frame().bottom
206 				> screen.Frame().bottom - 5 - TabHeight()) {
207 				Window()->MoveBy(0, TabHeight());
208 			}
209 			Window()->ResizeBy(0, -TabHeight());
210 		}
211 
212 		// Adapt scroll bar if there is one
213 		if (fScrollView != NULL) {
214 			BScrollBar* bar = fScrollView->ScrollBar(B_VERTICAL);
215 			if (bar != NULL) {
216 				bar->ResizeBy(0, 1);
217 				bar->MoveBy(0, -1);
218 			}
219 		}
220 
221 		ContainerView()->MoveBy(0, -TabHeight());
222 		ContainerView()->ResizeBy(0, TabHeight());
223 		fFullScreenButton->Hide();
224 	}
225 
226 	return BTabView::RemoveTab(index);
227 }
228 
229 
230 void
231 SmartTabView::MoveTab(int32 index, int32 newIndex)
232 {
233 	// check the indexes
234 	int32 count = CountTabs();
235 	if (index == newIndex || index < 0 || newIndex < 0 || index >= count
236 		|| newIndex >= count) {
237 		return;
238 	}
239 
240 	// Remove the tab from its position and add it to the end. Then cycle the
241 	// tabs starting after the new position (save the tab already moved) to the
242 	// end.
243 	BTab* tab = BTabView::RemoveTab(index);
244 	BTabView::AddTab(tab->View(), tab);
245 
246 	for (int32 i = newIndex; i < count - 1; i++) {
247 		tab = BTabView::RemoveTab(newIndex);
248 		BTabView::AddTab(tab->View(), tab);
249 	}
250 }
251 
252 
253 BRect
254 SmartTabView::DrawTabs()
255 {
256 	if (CountTabs() > 1)
257 		return BTabView::DrawTabs();
258 
259 	return BRect();
260 }
261 
262 
263 /*!	If you have a vertical scroll view that overlaps with the menu bar, it will
264 	be resized automatically when the tabs are hidden/shown.
265 */
266 void
267 SmartTabView::SetScrollView(BScrollView* scrollView)
268 {
269 	fScrollView = scrollView;
270 }
271 
272 
273 int32
274 SmartTabView::_ClickedTabIndex(const BPoint& point)
275 {
276 	if (point.y <= TabHeight()) {
277 		for (int32 i = 0; i < CountTabs(); i++) {
278 			if (TabFrame(i).Contains(point))
279 				return i;
280 		}
281 	}
282 
283 	return -1;
284 }
285 
286 
287 // #pragma mark - Listener
288 
289 
290 SmartTabView::Listener::~Listener()
291 {
292 }
293 
294 
295 void
296 SmartTabView::Listener::TabSelected(SmartTabView* tabView, int32 index)
297 {
298 }
299 
300 
301 void
302 SmartTabView::Listener::TabDoubleClicked(SmartTabView* tabView, BPoint point,
303 	int32 index)
304 {
305 }
306 
307 
308 void
309 SmartTabView::Listener::TabMiddleClicked(SmartTabView* tabView, BPoint point,
310 	int32 index)
311 {
312 }
313 
314 
315 void
316 SmartTabView::Listener::TabRightClicked(SmartTabView* tabView, BPoint point,
317 	int32 index)
318 {
319 }
320