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