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