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
SmartTabView(BRect frame,const char * name,button_width width,uint32 resizingMode,uint32 flags)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
~SmartTabView()69 SmartTabView::~SmartTabView()
70 {
71 }
72
73
74 void
SetInsets(float left,float top,float right,float bottom)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
MouseDown(BPoint point)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
AttachedToWindow()116 SmartTabView::AttachedToWindow()
117 {
118 BTabView::AttachedToWindow();
119 }
120
121
122 void
AllAttached()123 SmartTabView::AllAttached()
124 {
125 BTabView::AllAttached();
126 }
127
128
129 void
Select(int32 index)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
AddTab(BView * target,BTab * tab)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*
RemoveTab(int32 index)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
MoveTab(int32 index,int32 newIndex)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
DrawTabs()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
SetScrollView(BScrollView * scrollView)268 SmartTabView::SetScrollView(BScrollView* scrollView)
269 {
270 fScrollView = scrollView;
271 }
272
273
274 int32
_ClickedTabIndex(const BPoint & point)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
~Listener()291 SmartTabView::Listener::~Listener()
292 {
293 }
294
295
296 void
TabSelected(SmartTabView * tabView,int32 index)297 SmartTabView::Listener::TabSelected(SmartTabView* tabView, int32 index)
298 {
299 }
300
301
302 void
TabDoubleClicked(SmartTabView * tabView,BPoint point,int32 index)303 SmartTabView::Listener::TabDoubleClicked(SmartTabView* tabView, BPoint point,
304 int32 index)
305 {
306 }
307
308
309 void
TabMiddleClicked(SmartTabView * tabView,BPoint point,int32 index)310 SmartTabView::Listener::TabMiddleClicked(SmartTabView* tabView, BPoint point,
311 int32 index)
312 {
313 }
314
315
316 void
TabRightClicked(SmartTabView * tabView,BPoint point,int32 index)317 SmartTabView::Listener::TabRightClicked(SmartTabView* tabView, BPoint point,
318 int32 index)
319 {
320 }
321