1 /* 2 * Copyright 2007-2009, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stefano Ceccherini (burton666@libero.it) 7 */ 8 9 10 /*! The SmartTabView class is a BTabView descendant that hides the tab bar 11 as long as there is only a single tab. 12 Furthermore, it provides a tab context menu, as well as allowing you to 13 close buttons with the middle mouse button. 14 */ 15 16 17 #include "SmartTabView.h" 18 19 #include <Catalog.h> 20 #include <Locale.h> 21 #include <MenuItem.h> 22 #include <Message.h> 23 #include <Messenger.h> 24 #include <PopUpMenu.h> 25 #include <Screen.h> 26 #include <ScrollView.h> 27 #include <Window.h> 28 29 #include <stdio.h> 30 31 32 const static uint32 kCloseTab = 'ClTb'; 33 34 35 SmartTabView::SmartTabView(BRect frame, const char* name, button_width width, 36 uint32 resizingMode, uint32 flags) 37 : 38 BTabView(frame, name, width, resizingMode, flags), 39 fInsets(0, 0, 0, 0), 40 fScrollView(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 #undef B_TRANSLATE_CONTEXT 66 #define B_TRANSLATE_CONTEXT "Terminal SmartTabView" 67 68 void 69 SmartTabView::MouseDown(BPoint point) 70 { 71 bool handled = false; 72 73 if (CountTabs() > 1) { 74 int32 tabIndex = _ClickedTabIndex(point); 75 if (tabIndex >= 0) { 76 int32 buttons; 77 Window()->CurrentMessage()->FindInt32("buttons", &buttons); 78 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { 79 BMessage* message = new BMessage(kCloseTab); 80 message->AddInt32("index", tabIndex); 81 82 BPopUpMenu* popUpMenu = new BPopUpMenu("tab menu"); 83 popUpMenu->AddItem(new BMenuItem(B_TRANSLATE("Close tab"), 84 message)); 85 popUpMenu->SetAsyncAutoDestruct(true); 86 popUpMenu->SetTargetForItems(BMessenger(this)); 87 popUpMenu->Go(ConvertToScreen(point), true, true, true); 88 89 handled = true; 90 } else if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) { 91 RemoveAndDeleteTab(tabIndex); 92 handled = true; 93 } 94 } 95 } 96 97 if (!handled) 98 BTabView::MouseDown(point); 99 } 100 101 102 void 103 SmartTabView::AttachedToWindow() 104 { 105 BTabView::AttachedToWindow(); 106 } 107 108 109 void 110 SmartTabView::AllAttached() 111 { 112 BTabView::AllAttached(); 113 } 114 115 116 void 117 SmartTabView::MessageReceived(BMessage *message) 118 { 119 switch (message->what) { 120 case kCloseTab: 121 { 122 int32 tabIndex = 0; 123 if (message->FindInt32("index", &tabIndex) == B_OK) 124 RemoveAndDeleteTab(tabIndex); 125 break; 126 } 127 default: 128 BTabView::MessageReceived(message); 129 break; 130 } 131 } 132 133 134 void 135 SmartTabView::Select(int32 index) 136 { 137 BTabView::Select(index); 138 BView *view = ViewForTab(index); 139 if (view != NULL) { 140 view->MoveTo(fInsets.LeftTop()); 141 view->ResizeTo(ContainerView()->Bounds().Width() 142 - fInsets.left - fInsets.right, 143 ContainerView()->Bounds().Height() - fInsets.top - fInsets.bottom); 144 } 145 } 146 147 148 void 149 SmartTabView::RemoveAndDeleteTab(int32 index) 150 { 151 // Select another tab 152 if (index == Selection()) { 153 if (index > 0) 154 Select(index - 1); 155 else if (index < CountTabs()) 156 Select(index + 1); 157 } 158 delete RemoveTab(index); 159 } 160 161 162 void 163 SmartTabView::AddTab(BView* target, BTab* tab) 164 { 165 if (target == NULL) 166 return; 167 168 BTabView::AddTab(target, tab); 169 170 if (CountTabs() == 1) { 171 // Call select on the tab, since 172 // we're resizing the contained view 173 // inside that function 174 Select(0); 175 } else if (CountTabs() == 2) { 176 // Need to resize the view, since we're switching from "normal" 177 // to tabbed mode 178 ContainerView()->ResizeBy(0, -TabHeight()); 179 ContainerView()->MoveBy(0, TabHeight()); 180 181 // Make sure the content size stays the same, but take special care 182 // of full screen mode 183 BScreen screen(Window()); 184 if (Window()->DecoratorFrame().Height() + 2 * TabHeight() 185 < screen.Frame().Height()) { 186 if (Window()->Frame().bottom + TabHeight() 187 > screen.Frame().bottom - 5) { 188 Window()->MoveBy(0, -TabHeight()); 189 } 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 204 Invalidate(TabFrame(CountTabs() - 1).InsetByCopy(-2, -2)); 205 } 206 207 208 BTab* 209 SmartTabView::RemoveTab(int32 index) 210 { 211 if (CountTabs() == 2) { 212 // Hide the tab bar again by resizing the container view 213 214 // Make sure the content size stays the same, but take special care 215 // of full screen mode 216 BScreen screen(Window()); 217 if (Window()->DecoratorFrame().Height() + 2 * TabHeight() 218 < screen.Frame().Height()) { 219 if (Window()->Frame().bottom 220 > screen.Frame().bottom - 5 - TabHeight()) { 221 Window()->MoveBy(0, TabHeight()); 222 } 223 Window()->ResizeBy(0, -TabHeight()); 224 } 225 226 // Adapt scroll bar if there is one 227 if (fScrollView != NULL) { 228 BScrollBar* bar = fScrollView->ScrollBar(B_VERTICAL); 229 if (bar != NULL) { 230 bar->ResizeBy(0, 1); 231 bar->MoveBy(0, -1); 232 } 233 } 234 235 ContainerView()->MoveBy(0, -TabHeight()); 236 ContainerView()->ResizeBy(0, TabHeight()); 237 } 238 239 return BTabView::RemoveTab(index); 240 } 241 242 243 BRect 244 SmartTabView::DrawTabs() 245 { 246 if (CountTabs() > 1) 247 return BTabView::DrawTabs(); 248 249 return BRect(); 250 } 251 252 253 /*! If you have a vertical scroll view that overlaps with the menu bar, it will 254 be resized automatically when the tabs are hidden/shown. 255 */ 256 void 257 SmartTabView::SetScrollView(BScrollView* scrollView) 258 { 259 fScrollView = scrollView; 260 } 261 262 263 int32 264 SmartTabView::_ClickedTabIndex(const BPoint& point) 265 { 266 if (point.y <= TabHeight()) { 267 for (int32 i = 0; i < CountTabs(); i++) { 268 if (TabFrame(i).Contains(point)) 269 return i; 270 } 271 } 272 273 return -1; 274 } 275