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 34SmartTabView::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 51SmartTabView::~SmartTabView() 52{ 53} 54 55 56void 57SmartTabView::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 66void 67SmartTabView::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 97void 98SmartTabView::AttachedToWindow() 99{ 100 BTabView::AttachedToWindow(); 101} 102 103 104void 105SmartTabView::AllAttached() 106{ 107 BTabView::AllAttached(); 108} 109 110 111void 112SmartTabView::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 128void 129SmartTabView::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 174BTab* 175SmartTabView::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 209void 210SmartTabView::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 232BRect 233SmartTabView::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*/ 245void 246SmartTabView::SetScrollView(BScrollView* scrollView) 247{ 248 fScrollView = scrollView; 249} 250 251 252int32 253SmartTabView::_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 269SmartTabView::Listener::~Listener() 270{ 271} 272 273 274void 275SmartTabView::Listener::TabSelected(SmartTabView* tabView, int32 index) 276{ 277} 278 279 280void 281SmartTabView::Listener::TabDoubleClicked(SmartTabView* tabView, BPoint point, 282 int32 index) 283{ 284} 285 286 287void 288SmartTabView::Listener::TabMiddleClicked(SmartTabView* tabView, BPoint point, 289 int32 index) 290{ 291} 292 293 294void 295SmartTabView::Listener::TabRightClicked(SmartTabView* tabView, BPoint point, 296 int32 index) 297{ 298} 299