xref: /haiku/src/apps/terminal/SmartTabView.cpp (revision 4466b89c65970de4c7236ac87faa2bee4589f413)
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 
170 	Invalidate(TabFrame(CountTabs() - 1).InsetByCopy(-2, -2));
171 }
172 
173 
174 BTab*
175 SmartTabView::RemoveTab(int32 index)
176 {
177 	if (CountTabs() == 2) {
178 		// Hide the tab bar again by resizing the container view
179 
180 		// Make sure the content size stays the same, but take special care
181 		// of full screen mode
182 		BScreen screen(Window());
183 		if (Window()->DecoratorFrame().Height() + 2 * TabHeight()
184 				< screen.Frame().Height()) {
185 			if (Window()->Frame().bottom
186 				> screen.Frame().bottom - 5 - TabHeight()) {
187 				Window()->MoveBy(0, TabHeight());
188 			}
189 			Window()->ResizeBy(0, -TabHeight());
190 		}
191 
192 		// Adapt scroll bar if there is one
193 		if (fScrollView != NULL) {
194 			BScrollBar* bar = fScrollView->ScrollBar(B_VERTICAL);
195 			if (bar != NULL) {
196 				bar->ResizeBy(0, 1);
197 				bar->MoveBy(0, -1);
198 			}
199 		}
200 
201 		ContainerView()->MoveBy(0, -TabHeight());
202 		ContainerView()->ResizeBy(0, TabHeight());
203 	}
204 
205 	return BTabView::RemoveTab(index);
206 }
207 
208 
209 void
210 SmartTabView::MoveTab(int32 index, int32 newIndex)
211 {
212 	// check the indexes
213 	int32 count = CountTabs();
214 	if (index == newIndex || index < 0 || newIndex < 0 || index >= count
215 		|| newIndex >= count) {
216 		return;
217 	}
218 
219 	// Remove the tab from its position and add it to the end. Then cycle the
220 	// tabs starting after the new position (save the tab already moved) to the
221 	// end.
222 	BTab* tab = BTabView::RemoveTab(index);
223 	BTabView::AddTab(tab->View(), tab);
224 
225 	for (int32 i = newIndex; i < count - 1; i++) {
226 		tab = BTabView::RemoveTab(newIndex);
227 		BTabView::AddTab(tab->View(), tab);
228 	}
229 }
230 
231 
232 BRect
233 SmartTabView::DrawTabs()
234 {
235 	if (CountTabs() > 1)
236 		return BTabView::DrawTabs();
237 
238 	return BRect();
239 }
240 
241 
242 /*!	If you have a vertical scroll view that overlaps with the menu bar, it will
243 	be resized automatically when the tabs are hidden/shown.
244 */
245 void
246 SmartTabView::SetScrollView(BScrollView* scrollView)
247 {
248 	fScrollView = scrollView;
249 }
250 
251 
252 int32
253 SmartTabView::_ClickedTabIndex(const BPoint& point)
254 {
255 	if (point.y <= TabHeight()) {
256 		for (int32 i = 0; i < CountTabs(); i++) {
257 			if (TabFrame(i).Contains(point))
258 				return i;
259 		}
260 	}
261 
262 	return -1;
263 }
264 
265 
266 // #pragma mark - Listener
267 
268 
269 SmartTabView::Listener::~Listener()
270 {
271 }
272 
273 
274 void
275 SmartTabView::Listener::TabSelected(SmartTabView* tabView, int32 index)
276 {
277 }
278 
279 
280 void
281 SmartTabView::Listener::TabDoubleClicked(SmartTabView* tabView, BPoint point,
282 	int32 index)
283 {
284 }
285 
286 
287 void
288 SmartTabView::Listener::TabMiddleClicked(SmartTabView* tabView, BPoint point,
289 	int32 index)
290 {
291 }
292 
293 
294 void
295 SmartTabView::Listener::TabRightClicked(SmartTabView* tabView, BPoint point,
296 	int32 index)
297 {
298 }
299