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