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