xref: /haiku/src/apps/terminal/SmartTabView.cpp (revision 03187b607b2b5eec7ee059f1ead09bdba14991fb)
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 #include "SmartTabView.h"
10 
11 #include <MenuItem.h>
12 #include <Message.h>
13 #include <Messenger.h>
14 #include <PopUpMenu.h>
15 #include <Screen.h>
16 #include <Window.h>
17 
18 #include <stdio.h>
19 
20 const static uint32 kCloseTab = 'ClTb';
21 
22 SmartTabView::SmartTabView(BRect frame, const char *name, button_width width,
23 				uint32 resizingMode, uint32 flags)
24 	:
25 	BTabView(frame, name, width, resizingMode, flags),
26 	fInsets(0, 0, 0, 0)
27 {
28 	// Resize the container view to fill the complete tab view for single-tab
29 	// mode. Later, when more than one tab is added, we shrink the container
30 	// view again.
31 	frame.OffsetTo(B_ORIGIN);
32 	ContainerView()->MoveTo(frame.LeftTop());
33 	ContainerView()->ResizeTo(frame.Width(), frame.Height());
34 }
35 
36 
37 SmartTabView::~SmartTabView()
38 {
39 }
40 
41 
42 void
43 SmartTabView::SetInsets(float left, float top, float right, float bottom)
44 {
45 	fInsets.left = left;
46 	fInsets.top = top;
47 	fInsets.right = right;
48 	fInsets.bottom = bottom;
49 }
50 
51 
52 void
53 SmartTabView::MouseDown(BPoint point)
54 {
55 	bool handled = false;
56 
57 	if (CountTabs() > 1) {
58 		int32 tabIndex = _ClickedTabIndex(point);
59 		if (tabIndex >= 0) {
60 			int32 buttons;
61 			Window()->CurrentMessage()->FindInt32("buttons", &buttons);
62 			if (buttons & B_SECONDARY_MOUSE_BUTTON) {
63 				BMessage *message = new BMessage(kCloseTab);
64 				message->AddInt32("index", tabIndex);
65 
66 				BPopUpMenu *popUpMenu = new BPopUpMenu("tab menu");
67 				popUpMenu->AddItem(new BMenuItem("Close Tab", message));
68 				popUpMenu->SetAsyncAutoDestruct(true);
69 				popUpMenu->SetTargetForItems(BMessenger(this));
70 				popUpMenu->Go(ConvertToScreen(point), true, true, true);
71 
72 				handled = true;
73 			} else if (buttons & B_TERTIARY_MOUSE_BUTTON) {
74 				RemoveAndDeleteTab(tabIndex);
75 				handled = true;
76 			}
77 		}
78 	}
79 
80 	if (!handled)
81 		BTabView::MouseDown(point);
82 
83 }
84 
85 
86 void
87 SmartTabView::AttachedToWindow()
88 {
89 	BTabView::AttachedToWindow();
90 }
91 
92 
93 void
94 SmartTabView::AllAttached()
95 {
96 	BTabView::AllAttached();
97 }
98 
99 
100 void
101 SmartTabView::MessageReceived(BMessage *message)
102 {
103 	switch (message->what) {
104 		case kCloseTab:
105 		{
106 			int32 tabIndex = 0;
107 			if (message->FindInt32("index", &tabIndex) == B_OK)
108 				RemoveAndDeleteTab(tabIndex);
109 			break;
110 		}
111 		default:
112 			BTabView::MessageReceived(message);
113 			break;
114 	}
115 }
116 
117 
118 void
119 SmartTabView::Select(int32 index)
120 {
121 	BTabView::Select(index);
122 	BView *view = ViewForTab(index);
123 	if (view != NULL) {
124 		view->MoveTo(fInsets.LeftTop());
125 		view->ResizeTo(ContainerView()->Bounds().Width()
126 				- fInsets.left - fInsets.right,
127 			ContainerView()->Bounds().Height() - fInsets.top - fInsets.bottom);
128 	}
129 }
130 
131 
132 void
133 SmartTabView::RemoveAndDeleteTab(int32 index)
134 {
135 	// Select another tab
136 	if (index == Selection()) {
137 		if (index > 0)
138 			Select(index - 1);
139 		else if (index < CountTabs())
140 			Select(index + 1);
141 	}
142 	delete RemoveTab(index);
143 }
144 
145 
146 void
147 SmartTabView::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
161 		// switching from "normal" to tabbed mode
162 		ContainerView()->ResizeBy(0, -TabHeight());
163 		ContainerView()->MoveBy(0, TabHeight());
164 
165 		BScreen screen(Window());
166 		if (Window()->DecoratorFrame().Height() + 2 * TabHeight()
167 				< screen.Frame().Height()) {
168 			if (Window()->Frame().bottom + TabHeight()
169 				> screen.Frame().bottom - 5) {
170 				Window()->MoveBy(0, -TabHeight());
171 			}
172 
173 			Window()->ResizeBy(0, TabHeight());
174 		}
175 	}
176 
177 	Invalidate(TabFrame(CountTabs() - 1).InsetByCopy(-2, -2));
178 }
179 
180 
181 BTab *
182 SmartTabView::RemoveTab(int32 index)
183 {
184 	if (CountTabs() == 2) {
185 		// see AddTab()
186 		BScreen screen(Window());
187 		if (Window()->DecoratorFrame().Height() + 2 * TabHeight()
188 				< screen.Frame().Height()) {
189 			if (Window()->Frame().bottom
190 				> screen.Frame().bottom - 5 - TabHeight()) {
191 				Window()->MoveBy(0, TabHeight());
192 			}
193 			Window()->ResizeBy(0, -TabHeight());
194 		}
195 
196 		ContainerView()->MoveBy(0, -TabHeight());
197 		ContainerView()->ResizeBy(0, TabHeight());
198 	}
199 	return BTabView::RemoveTab(index);
200 }
201 
202 
203 BRect
204 SmartTabView::DrawTabs()
205 {
206 	if (CountTabs() > 1)
207 		return BTabView::DrawTabs();
208 	return BRect();
209 }
210 
211 
212 int32
213 SmartTabView::_ClickedTabIndex(const BPoint &point)
214 {
215 	if (point.y <= TabHeight()) {
216 		for (int32 i = 0; i < CountTabs(); i++) {
217 			if (TabFrame(i).Contains(point))
218 				return i;
219 		}
220 	}
221 
222 	return -1;
223 }
224