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 40SmartTabView::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 69SmartTabView::~SmartTabView() 70{ 71} 72 73 74void 75SmartTabView::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 84void 85SmartTabView::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 115void 116SmartTabView::AttachedToWindow() 117{ 118 BTabView::AttachedToWindow(); 119} 120 121 122void 123SmartTabView::AllAttached() 124{ 125 BTabView::AllAttached(); 126} 127 128 129void 130SmartTabView::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 146void 147SmartTabView::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 195BTab* 196SmartTabView::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 231void 232SmartTabView::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 254BRect 255SmartTabView::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*/ 267void 268SmartTabView::SetScrollView(BScrollView* scrollView) 269{ 270 fScrollView = scrollView; 271} 272 273 274int32 275SmartTabView::_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 291SmartTabView::Listener::~Listener() 292{ 293} 294 295 296void 297SmartTabView::Listener::TabSelected(SmartTabView* tabView, int32 index) 298{ 299} 300 301 302void 303SmartTabView::Listener::TabDoubleClicked(SmartTabView* tabView, BPoint point, 304 int32 index) 305{ 306} 307 308 309void 310SmartTabView::Listener::TabMiddleClicked(SmartTabView* tabView, BPoint point, 311 int32 index) 312{ 313} 314 315 316void 317SmartTabView::Listener::TabRightClicked(SmartTabView* tabView, BPoint point, 318 int32 index) 319{ 320} 321