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