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