xref: /haiku/src/kits/interface/TabView.cpp (revision b00da680fbf441c7b5676f9910e41fde015aa544)
1 /*
2  * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Jérôme Duval (korli@users.berlios.de)
8  *		Stephan Aßmus <superstippi@gmx.de>
9  *		Artur Wyszynski
10  *		Rene Gollent (rene@gollent.com)
11  */
12 
13 
14 #include <TabView.h>
15 #include <TabViewPrivate.h>
16 
17 #include <new>
18 
19 #include <math.h>
20 #include <string.h>
21 
22 #include <CardLayout.h>
23 #include <ControlLook.h>
24 #include <GroupLayout.h>
25 #include <LayoutUtils.h>
26 #include <List.h>
27 #include <Message.h>
28 #include <PropertyInfo.h>
29 #include <Rect.h>
30 #include <Region.h>
31 #include <String.h>
32 #include <Window.h>
33 
34 #include <binary_compatibility/Support.h>
35 
36 
37 static property_info sPropertyList[] = {
38 	{
39 		"Selection",
40 		{ B_GET_PROPERTY, B_SET_PROPERTY },
41 		{ B_DIRECT_SPECIFIER },
42 		NULL, 0,
43 		{ B_INT32_TYPE }
44 	},
45 
46 	{ 0 }
47 };
48 
49 
BTab(BView * contentsView)50 BTab::BTab(BView* contentsView)
51 	:
52 	fEnabled(true),
53 	fSelected(false),
54 	fFocus(false),
55 	fView(contentsView),
56 	fTabView(NULL)
57 {
58 }
59 
60 
BTab(BMessage * archive)61 BTab::BTab(BMessage* archive)
62 	:
63 	BArchivable(archive),
64 	fSelected(false),
65 	fFocus(false),
66 	fView(NULL),
67 	fTabView(NULL)
68 {
69 	bool disable;
70 
71 	if (archive->FindBool("_disable", &disable) != B_OK)
72 		SetEnabled(true);
73 	else
74 		SetEnabled(!disable);
75 }
76 
77 
~BTab()78 BTab::~BTab()
79 {
80 	if (fView == NULL)
81 		return;
82 
83 	if (fSelected)
84 		fView->RemoveSelf();
85 
86 	delete fView;
87 }
88 
89 
90 BArchivable*
Instantiate(BMessage * archive)91 BTab::Instantiate(BMessage* archive)
92 {
93 	if (validate_instantiation(archive, "BTab"))
94 		return new BTab(archive);
95 
96 	return NULL;
97 }
98 
99 
100 status_t
Archive(BMessage * data,bool deep) const101 BTab::Archive(BMessage* data, bool deep) const
102 {
103 	status_t result = BArchivable::Archive(data, deep);
104 	if (result != B_OK)
105 		return result;
106 
107 	if (!fEnabled)
108 		result = data->AddBool("_disable", false);
109 
110 	return result;
111 }
112 
113 
114 status_t
Perform(uint32 d,void * arg)115 BTab::Perform(uint32 d, void* arg)
116 {
117 	return BArchivable::Perform(d, arg);
118 }
119 
120 
121 const char*
Label() const122 BTab::Label() const
123 {
124 	if (fView != NULL)
125 		return fView->Name();
126 	else
127 		return NULL;
128 }
129 
130 
131 void
SetLabel(const char * label)132 BTab::SetLabel(const char* label)
133 {
134 	if (label == NULL || fView == NULL)
135 		return;
136 
137 	fView->SetName(label);
138 
139 	if (fTabView != NULL)
140 		fTabView->Invalidate();
141 }
142 
143 
144 bool
IsSelected() const145 BTab::IsSelected() const
146 {
147 	return fSelected;
148 }
149 
150 
151 void
Select(BView * owner)152 BTab::Select(BView* owner)
153 {
154 	fSelected = true;
155 
156 	if (owner == NULL || fView == NULL)
157 		return;
158 
159 	// NOTE: Views are not added/removed, if there is layout,
160 	// they are made visible/invisible in that case.
161 	if (owner->GetLayout() == NULL && fView->Parent() == NULL)
162 		owner->AddChild(fView);
163 }
164 
165 
166 void
Deselect()167 BTab::Deselect()
168 {
169 	if (fView != NULL) {
170 		// NOTE: Views are not added/removed, if there is layout,
171 		// they are made visible/invisible in that case.
172 		bool removeView = false;
173 		BView* container = fView->Parent();
174 		if (container != NULL)
175 			removeView =
176 				dynamic_cast<BCardLayout*>(container->GetLayout()) == NULL;
177 		if (removeView)
178 			fView->RemoveSelf();
179 	}
180 
181 	fSelected = false;
182 }
183 
184 
185 void
SetEnabled(bool enable)186 BTab::SetEnabled(bool enable)
187 {
188 	fEnabled = enable;
189 }
190 
191 
192 bool
IsEnabled() const193 BTab::IsEnabled() const
194 {
195 	return fEnabled;
196 }
197 
198 
199 void
MakeFocus(bool focus)200 BTab::MakeFocus(bool focus)
201 {
202 	fFocus = focus;
203 }
204 
205 
206 bool
IsFocus() const207 BTab::IsFocus() const
208 {
209 	return fFocus;
210 }
211 
212 
213 void
SetView(BView * view)214 BTab::SetView(BView* view)
215 {
216 	if (view == NULL || fView == view)
217 		return;
218 
219 	if (fView != NULL) {
220 		fView->RemoveSelf();
221 		delete fView;
222 	}
223 	fView = view;
224 
225 	if (fTabView != NULL && fSelected) {
226 		Select(fTabView->ContainerView());
227 		fTabView->Invalidate();
228 	}
229 }
230 
231 
232 BView*
View() const233 BTab::View() const
234 {
235 	return fView;
236 }
237 
238 
239 void
DrawFocusMark(BView * owner,BRect frame)240 BTab::DrawFocusMark(BView* owner, BRect frame)
241 {
242 	float width = owner->StringWidth(Label());
243 
244 	owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
245 
246 	float offset = IsSelected() ? 3 : 2;
247 	switch (fTabView->TabSide()) {
248 		case BTabView::kTopSide:
249 			owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0,
250 					frame.bottom - offset),
251 				BPoint((frame.left + frame.right + width) / 2.0,
252 					frame.bottom - offset));
253 			break;
254 		case BTabView::kBottomSide:
255 			owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0,
256 					frame.top + offset),
257 				BPoint((frame.left + frame.right + width) / 2.0,
258 					frame.top + offset));
259 			break;
260 		case BTabView::kLeftSide:
261 			owner->StrokeLine(BPoint(frame.right - offset,
262 					(frame.top + frame.bottom - width) / 2.0),
263 				BPoint(frame.right - offset,
264 					(frame.top + frame.bottom + width) / 2.0));
265 			break;
266 		case BTabView::kRightSide:
267 			owner->StrokeLine(BPoint(frame.left + offset,
268 					(frame.top + frame.bottom - width) / 2.0),
269 				BPoint(frame.left + offset,
270 					(frame.top + frame.bottom + width) / 2.0));
271 			break;
272 	}
273 }
274 
275 
276 void
DrawLabel(BView * owner,BRect frame)277 BTab::DrawLabel(BView* owner, BRect frame)
278 {
279 	float rotation = 0.0f;
280 	BPoint center(frame.left + frame.Width() / 2,
281 		frame.top + frame.Height() / 2);
282 	switch (fTabView->TabSide()) {
283 		case BTabView::kTopSide:
284 		case BTabView::kBottomSide:
285 			rotation = 0.0f;
286 			break;
287 		case BTabView::kLeftSide:
288 			rotation = 270.0f;
289 			break;
290 		case BTabView::kRightSide:
291 			rotation = 90.0f;
292 			break;
293 	}
294 
295 	if (rotation != 0.0f) {
296 		// DrawLabel doesn't allow rendering rotated text
297 		// rotate frame first and BAffineTransform will handle the rotation
298 		// we can't give "unrotated" frame because it comes from
299 		// BTabView::TabFrame and it is also used to handle mouse clicks
300 		BRect originalFrame(frame);
301 		frame.top = center.y - originalFrame.Width() / 2;
302 		frame.bottom = center.y + originalFrame.Width() / 2;
303 		frame.left = center.x - originalFrame.Height() / 2;
304 		frame.right = center.x + originalFrame.Height() / 2;
305 	}
306 
307 	BAffineTransform transform;
308 	transform.RotateBy(center, rotation * M_PI / 180.0f);
309 	owner->SetTransform(transform);
310 	be_control_look->DrawLabel(owner, Label(), frame, frame,
311 		ui_color(B_PANEL_BACKGROUND_COLOR),
312 		IsEnabled() ? 0 : BControlLook::B_DISABLED,
313 		BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER));
314 	owner->SetTransform(BAffineTransform());
315 }
316 
317 
318 void
DrawTab(BView * owner,BRect frame,tab_position,bool)319 BTab::DrawTab(BView* owner, BRect frame, tab_position, bool)
320 {
321 	if (fTabView == NULL)
322 		return;
323 
324 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
325 	uint32 flags = 0;
326 	uint32 borders = _Borders(owner, frame);
327 
328 	int32 index = fTabView->IndexOf(this);
329 	int32 selected = fTabView->Selection();
330 	int32 first = 0;
331 	int32 last = fTabView->CountTabs() - 1;
332 
333 	if (index == selected) {
334 		be_control_look->DrawActiveTab(owner, frame, frame, base, flags,
335 			borders, fTabView->TabSide(), index, selected, first, last);
336 	} else {
337 		be_control_look->DrawInactiveTab(owner, frame, frame, base, flags,
338 			borders, fTabView->TabSide(), index, selected, first, last);
339 	}
340 
341 	DrawLabel(owner, frame);
342 }
343 
344 
345 //	#pragma mark - BTab private methods
346 
347 
348 uint32
_Borders(BView * owner,BRect frame)349 BTab::_Borders(BView* owner, BRect frame)
350 {
351 	uint32 borders = 0;
352 	if (owner == NULL || fTabView == NULL)
353 		return borders;
354 
355 	if (fTabView->TabSide() == BTabView::kTopSide
356 		|| fTabView->TabSide() == BTabView::kBottomSide) {
357 		borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
358 
359 		if (frame.left == owner->Bounds().left)
360 			borders |= BControlLook::B_LEFT_BORDER;
361 
362 		if (frame.right == owner->Bounds().right)
363 			borders |= BControlLook::B_RIGHT_BORDER;
364 	} else if (fTabView->TabSide() == BTabView::kLeftSide
365 		|| fTabView->TabSide() == BTabView::kRightSide) {
366 		borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER;
367 
368 		if (frame.top == owner->Bounds().top)
369 			borders |= BControlLook::B_TOP_BORDER;
370 
371 		if (frame.bottom == owner->Bounds().bottom)
372 			borders |= BControlLook::B_BOTTOM_BORDER;
373 	}
374 
375 	return borders;
376 }
377 
378 
379 //	#pragma mark - FBC padding and private methods
380 
381 
_ReservedTab1()382 void BTab::_ReservedTab1() {}
_ReservedTab2()383 void BTab::_ReservedTab2() {}
_ReservedTab3()384 void BTab::_ReservedTab3() {}
_ReservedTab4()385 void BTab::_ReservedTab4() {}
_ReservedTab5()386 void BTab::_ReservedTab5() {}
_ReservedTab6()387 void BTab::_ReservedTab6() {}
_ReservedTab7()388 void BTab::_ReservedTab7() {}
_ReservedTab8()389 void BTab::_ReservedTab8() {}
_ReservedTab9()390 void BTab::_ReservedTab9() {}
_ReservedTab10()391 void BTab::_ReservedTab10() {}
_ReservedTab11()392 void BTab::_ReservedTab11() {}
_ReservedTab12()393 void BTab::_ReservedTab12() {}
394 
operator =(const BTab &)395 BTab &BTab::operator=(const BTab &)
396 {
397 	// this is private and not functional, but exported
398 	return *this;
399 }
400 
401 
402 //	#pragma mark - BTabView
403 
404 
BTabView(const char * name,button_width width,uint32 flags)405 BTabView::BTabView(const char* name, button_width width, uint32 flags)
406 	:
407 	BView(name, flags)
408 {
409 	_InitObject(true, width);
410 }
411 
412 
BTabView(BRect frame,const char * name,button_width width,uint32 resizeMask,uint32 flags)413 BTabView::BTabView(BRect frame, const char* name, button_width width,
414 	uint32 resizeMask, uint32 flags)
415 	:
416 	BView(frame, name, resizeMask, flags)
417 {
418 	_InitObject(false, width);
419 }
420 
421 
~BTabView()422 BTabView::~BTabView()
423 {
424 	for (int32 i = 0; i < CountTabs(); i++)
425 		delete TabAt(i);
426 
427 	delete fTabList;
428 }
429 
430 
BTabView(BMessage * archive)431 BTabView::BTabView(BMessage* archive)
432 	:
433 	BView(BUnarchiver::PrepareArchive(archive)),
434 	fTabList(new BList),
435 	fContainerView(NULL),
436 	fFocus(-1)
437 {
438 	BUnarchiver unarchiver(archive);
439 
440 	int16 width;
441 	if (archive->FindInt16("_but_width", &width) == B_OK)
442 		fTabWidthSetting = (button_width)width;
443 	else
444 		fTabWidthSetting = B_WIDTH_AS_USUAL;
445 
446 	if (archive->FindFloat("_high", &fTabHeight) != B_OK) {
447 		font_height fh;
448 		GetFontHeight(&fh);
449 		fTabHeight = ceilf(fh.ascent + fh.descent + fh.leading + 8.0f);
450 	}
451 
452 	if (archive->FindInt32("_sel", &fSelection) != B_OK)
453 		fSelection = -1;
454 
455 	if (archive->FindInt32("_border_style", (int32*)&fBorderStyle) != B_OK)
456 		fBorderStyle = B_FANCY_BORDER;
457 
458 	if (archive->FindInt32("_TabSide", (int32*)&fTabSide) != B_OK)
459 		fTabSide = kTopSide;
460 
461 	int32 i = 0;
462 	BMessage tabMsg;
463 
464 	if (BUnarchiver::IsArchiveManaged(archive)) {
465 		int32 tabCount;
466 		archive->GetInfo("_l_items", NULL, &tabCount);
467 		for (int32 i = 0; i < tabCount; i++) {
468 			unarchiver.EnsureUnarchived("_l_items", i);
469 			unarchiver.EnsureUnarchived("_view_list", i);
470 		}
471 		return;
472 	}
473 
474 	fContainerView = ChildAt(0);
475 	_InitContainerView(Flags() & B_SUPPORTS_LAYOUT);
476 
477 	while (archive->FindMessage("_l_items", i, &tabMsg) == B_OK) {
478 		BArchivable* archivedTab = instantiate_object(&tabMsg);
479 
480 		if (archivedTab) {
481 			BTab* tab = dynamic_cast<BTab*>(archivedTab);
482 
483 			BMessage viewMsg;
484 			if (archive->FindMessage("_view_list", i, &viewMsg) == B_OK) {
485 				BArchivable* archivedView = instantiate_object(&viewMsg);
486 				if (archivedView)
487 					AddTab(dynamic_cast<BView*>(archivedView), tab);
488 			}
489 		}
490 
491 		tabMsg.MakeEmpty();
492 		i++;
493 	}
494 }
495 
496 
497 BArchivable*
Instantiate(BMessage * archive)498 BTabView::Instantiate(BMessage* archive)
499 {
500 	if ( validate_instantiation(archive, "BTabView"))
501 		return new BTabView(archive);
502 
503 	return NULL;
504 }
505 
506 
507 status_t
Archive(BMessage * archive,bool deep) const508 BTabView::Archive(BMessage* archive, bool deep) const
509 {
510 	BArchiver archiver(archive);
511 
512 	status_t result = BView::Archive(archive, deep);
513 
514 	if (result == B_OK)
515 		result = archive->AddInt16("_but_width", fTabWidthSetting);
516 	if (result == B_OK)
517 		result = archive->AddFloat("_high", fTabHeight);
518 	if (result == B_OK)
519 		result = archive->AddInt32("_sel", fSelection);
520 	if (result == B_OK && fBorderStyle != B_FANCY_BORDER)
521 		result = archive->AddInt32("_border_style", fBorderStyle);
522 	if (result == B_OK && fTabSide != kTopSide)
523 		result = archive->AddInt32("_TabSide", fTabSide);
524 
525 	if (result == B_OK && deep) {
526 		for (int32 i = 0; i < CountTabs(); i++) {
527 			BTab* tab = TabAt(i);
528 
529 			if ((result = archiver.AddArchivable("_l_items", tab, deep))
530 					!= B_OK) {
531 				break;
532 			}
533 			result = archiver.AddArchivable("_view_list", tab->View(), deep);
534 		}
535 	}
536 
537 	return archiver.Finish(result);
538 }
539 
540 
541 status_t
AllUnarchived(const BMessage * archive)542 BTabView::AllUnarchived(const BMessage* archive)
543 {
544 	status_t err = BView::AllUnarchived(archive);
545 	if (err != B_OK)
546 		return err;
547 
548 	fContainerView = ChildAt(0);
549 	_InitContainerView(Flags() & B_SUPPORTS_LAYOUT);
550 
551 	BUnarchiver unarchiver(archive);
552 
553 	int32 tabCount;
554 	archive->GetInfo("_l_items", NULL, &tabCount);
555 	for (int32 i = 0; i < tabCount && err == B_OK; i++) {
556 		BTab* tab;
557 		err = unarchiver.FindObject("_l_items", i, tab);
558 		if (err == B_OK && tab) {
559 			BView* view;
560 			if ((err = unarchiver.FindObject("_view_list", i,
561 				BUnarchiver::B_DONT_ASSUME_OWNERSHIP, view)) != B_OK)
562 				break;
563 
564 			tab->SetView(view);
565 			fTabList->AddItem(tab);
566 		}
567 	}
568 
569 	if (err == B_OK)
570 		Select(fSelection);
571 
572 	return err;
573 }
574 
575 
576 status_t
Perform(perform_code code,void * _data)577 BTabView::Perform(perform_code code, void* _data)
578 {
579 	switch (code) {
580 		case PERFORM_CODE_ALL_UNARCHIVED:
581 		{
582 			perform_data_all_unarchived* data
583 				= (perform_data_all_unarchived*)_data;
584 
585 			data->return_value = BTabView::AllUnarchived(data->archive);
586 			return B_OK;
587 		}
588 	}
589 
590 	return BView::Perform(code, _data);
591 }
592 
593 
594 void
AttachedToWindow()595 BTabView::AttachedToWindow()
596 {
597 	BView::AttachedToWindow();
598 
599 	if (fSelection < 0 && CountTabs() > 0)
600 		Select(0);
601 }
602 
603 
604 void
DetachedFromWindow()605 BTabView::DetachedFromWindow()
606 {
607 	BView::DetachedFromWindow();
608 }
609 
610 
611 void
AllAttached()612 BTabView::AllAttached()
613 {
614 	BView::AllAttached();
615 }
616 
617 
618 void
AllDetached()619 BTabView::AllDetached()
620 {
621 	BView::AllDetached();
622 }
623 
624 
625 // #pragma mark -
626 
627 
628 void
MessageReceived(BMessage * message)629 BTabView::MessageReceived(BMessage* message)
630 {
631 	switch (message->what) {
632 		case B_GET_PROPERTY:
633 		case B_SET_PROPERTY:
634 		{
635 			BMessage reply(B_REPLY);
636 			bool handled = false;
637 
638 			BMessage specifier;
639 			int32 index;
640 			int32 form;
641 			const char* property;
642 			if (message->GetCurrentSpecifier(&index, &specifier, &form,
643 					&property) == B_OK) {
644 				if (strcmp(property, "Selection") == 0) {
645 					if (message->what == B_GET_PROPERTY) {
646 						reply.AddInt32("result", fSelection);
647 						handled = true;
648 					} else {
649 						// B_GET_PROPERTY
650 						int32 selection;
651 						if (message->FindInt32("data", &selection) == B_OK) {
652 							Select(selection);
653 							reply.AddInt32("error", B_OK);
654 							handled = true;
655 						}
656 					}
657 				}
658 			}
659 
660 			if (handled)
661 				message->SendReply(&reply);
662 			else
663 				BView::MessageReceived(message);
664 			break;
665 		}
666 
667 #if 0
668 		// TODO this would be annoying as-is, but maybe it makes sense with
669 		// a modifier or using only deltaX (not the main mouse wheel)
670 		case B_MOUSE_WHEEL_CHANGED:
671 		{
672 			float deltaX = 0.0f;
673 			float deltaY = 0.0f;
674 			message->FindFloat("be:wheel_delta_x", &deltaX);
675 			message->FindFloat("be:wheel_delta_y", &deltaY);
676 
677 			if (deltaX == 0.0f && deltaY == 0.0f)
678 				return;
679 
680 			if (deltaY == 0.0f)
681 				deltaY = deltaX;
682 
683 			int32 selection = Selection();
684 			int32 numTabs = CountTabs();
685 			if (deltaY > 0  && selection < numTabs - 1) {
686 				// move to the right tab.
687 				Select(Selection() + 1);
688 			} else if (deltaY < 0 && selection > 0 && numTabs > 1) {
689 				// move to the left tab.
690 				Select(selection - 1);
691 			}
692 			break;
693 		}
694 #endif
695 
696 		default:
697 			BView::MessageReceived(message);
698 			break;
699 	}
700 }
701 
702 
703 void
KeyDown(const char * bytes,int32 numBytes)704 BTabView::KeyDown(const char* bytes, int32 numBytes)
705 {
706 	if (IsHidden())
707 		return;
708 
709 	switch (bytes[0]) {
710 		case B_DOWN_ARROW:
711 		case B_LEFT_ARROW: {
712 			int32 focus = fFocus - 1;
713 			if (focus < 0)
714 				focus = CountTabs() - 1;
715 			SetFocusTab(focus, true);
716 			break;
717 		}
718 
719 		case B_UP_ARROW:
720 		case B_RIGHT_ARROW: {
721 			int32 focus = fFocus + 1;
722 			if (focus >= CountTabs())
723 				focus = 0;
724 			SetFocusTab(focus, true);
725 			break;
726 		}
727 
728 		case B_RETURN:
729 		case B_SPACE:
730 			Select(FocusTab());
731 			break;
732 
733 		default:
734 			BView::KeyDown(bytes, numBytes);
735 	}
736 }
737 
738 
739 void
MouseDown(BPoint where)740 BTabView::MouseDown(BPoint where)
741 {
742 	// Which button is pressed?
743 	uint32 buttons = 0;
744 	BMessage* currentMessage = Window()->CurrentMessage();
745 	if (currentMessage != NULL) {
746 		currentMessage->FindInt32("buttons", (int32*)&buttons);
747 	}
748 
749 	int32 selection = Selection();
750 	int32 numTabs = CountTabs();
751 	if (buttons & B_MOUSE_BUTTON(4)) {
752 		// The "back" mouse button moves to previous tab
753 		if (selection > 0 && numTabs > 1)
754 			Select(Selection() - 1);
755 	} else if (buttons & B_MOUSE_BUTTON(5)) {
756 		// The "forward" mouse button moves to next tab
757 		if (selection < numTabs - 1)
758 			Select(Selection() + 1);
759 	} else {
760 		// Other buttons are used to select a tab by clicking directly on it
761 		for (int32 i = 0; i < CountTabs(); i++) {
762 			if (TabFrame(i).Contains(where)
763 					&& i != Selection()) {
764 				Select(i);
765 				return;
766 			}
767 		}
768 	}
769 
770 	BView::MouseDown(where);
771 }
772 
773 
774 void
MouseUp(BPoint where)775 BTabView::MouseUp(BPoint where)
776 {
777 	BView::MouseUp(where);
778 }
779 
780 
781 void
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)782 BTabView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
783 {
784 	BView::MouseMoved(where, transit, dragMessage);
785 }
786 
787 
788 void
Pulse()789 BTabView::Pulse()
790 {
791 	BView::Pulse();
792 }
793 
794 
795 void
Select(int32 index)796 BTabView::Select(int32 index)
797 {
798 	if (index == Selection())
799 		return;
800 
801 	if (index < 0 || index >= CountTabs())
802 		index = Selection();
803 
804 	BTab* tab = TabAt(Selection());
805 
806 	if (tab)
807 		tab->Deselect();
808 
809 	tab = TabAt(index);
810 	if (tab != NULL && fContainerView != NULL) {
811 		if (index == 0)
812 			fTabOffset = 0.0f;
813 
814 		tab->Select(fContainerView);
815 		fSelection = index;
816 
817 		// make the view visible through the layout if there is one
818 		BCardLayout* layout
819 			= dynamic_cast<BCardLayout*>(fContainerView->GetLayout());
820 		if (layout != NULL)
821 			layout->SetVisibleItem(index);
822 	}
823 
824 	Invalidate();
825 
826 	if (index != 0 && !Bounds().Contains(TabFrame(index))){
827 		if (!Bounds().Contains(TabFrame(index).LeftTop()))
828 			fTabOffset += TabFrame(index).left - Bounds().left - 20.0f;
829 		else
830 			fTabOffset += TabFrame(index).right - Bounds().right + 20.0f;
831 
832 		Invalidate();
833 	}
834 
835 	SetFocusTab(index, true);
836 }
837 
838 
839 int32
Selection() const840 BTabView::Selection() const
841 {
842 	return fSelection;
843 }
844 
845 
846 void
WindowActivated(bool active)847 BTabView::WindowActivated(bool active)
848 {
849 	BView::WindowActivated(active);
850 
851 	if (IsFocus())
852 		Invalidate();
853 }
854 
855 
856 void
MakeFocus(bool focus)857 BTabView::MakeFocus(bool focus)
858 {
859 	BView::MakeFocus(focus);
860 
861 	SetFocusTab(Selection(), focus);
862 }
863 
864 
865 void
SetFocusTab(int32 tab,bool focus)866 BTabView::SetFocusTab(int32 tab, bool focus)
867 {
868 	if (tab >= CountTabs())
869 		tab = 0;
870 
871 	if (tab < 0)
872 		tab = CountTabs() - 1;
873 
874 	if (focus) {
875 		if (tab == fFocus)
876 			return;
877 
878 		if (fFocus != -1){
879 			if (TabAt (fFocus) != NULL)
880 				TabAt(fFocus)->MakeFocus(false);
881 			Invalidate(TabFrame(fFocus));
882 		}
883 		if (TabAt(tab) != NULL){
884 			TabAt(tab)->MakeFocus(true);
885 			Invalidate(TabFrame(tab));
886 			fFocus = tab;
887 		}
888 	} else if (fFocus != -1) {
889 		TabAt(fFocus)->MakeFocus(false);
890 		Invalidate(TabFrame(fFocus));
891 		fFocus = -1;
892 	}
893 }
894 
895 
896 int32
FocusTab() const897 BTabView::FocusTab() const
898 {
899 	return fFocus;
900 }
901 
902 
903 void
Draw(BRect updateRect)904 BTabView::Draw(BRect updateRect)
905 {
906 	DrawTabs();
907 	DrawBox(TabFrame(fSelection));
908 
909 	if (IsFocus() && fFocus != -1)
910 		TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus));
911 }
912 
913 
914 BRect
DrawTabs()915 BTabView::DrawTabs()
916 {
917 	BRect bounds(Bounds());
918 	BRect tabFrame(bounds);
919 	uint32 borders = 0;
920 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
921 
922 	// set tabFrame to area around tabs
923 	if (fTabSide == kTopSide || fTabSide == kBottomSide) {
924 		if (fTabSide == kTopSide)
925 			tabFrame.bottom = fTabHeight;
926 		else
927 			tabFrame.top = tabFrame.bottom - fTabHeight;
928 	} else if (fTabSide == kLeftSide || fTabSide == kRightSide) {
929 		if (fTabSide == kLeftSide)
930 			tabFrame.right = fTabHeight;
931 		else
932 			tabFrame.left = tabFrame.right - fTabHeight;
933 	}
934 
935 	// draw frame behind tabs
936 	be_control_look->DrawTabFrame(this, tabFrame, bounds, base, 0,
937 		borders, fBorderStyle, fTabSide);
938 
939 	// draw the tabs on top of the tab frame
940 	int32 tabCount = CountTabs();
941 	for (int32 i = 0; i < tabCount; i++) {
942 		BRect tabFrame = TabFrame(i);
943 
944 		TabAt(i)->DrawTab(this, tabFrame,
945 			i == fSelection ? B_TAB_FRONT
946 				: (i == 0) ? B_TAB_FIRST : B_TAB_ANY,
947 			i != fSelection - 1);
948 	}
949 
950 	return fSelection < CountTabs() ? TabFrame(fSelection) : BRect();
951 }
952 
953 
954 void
DrawBox(BRect selectedTabRect)955 BTabView::DrawBox(BRect selectedTabRect)
956 {
957 	BRect rect(Bounds());
958 	uint32 bordersToDraw = BControlLook::B_ALL_BORDERS;
959 	switch (fTabSide) {
960 		case kTopSide:
961 			bordersToDraw &= ~BControlLook::B_TOP_BORDER;
962 			rect.top = fTabHeight;
963 			break;
964 		case kBottomSide:
965 			bordersToDraw &= ~BControlLook::B_BOTTOM_BORDER;
966 			rect.bottom -= fTabHeight;
967 			break;
968 		case kLeftSide:
969 			bordersToDraw &= ~BControlLook::B_LEFT_BORDER;
970 			rect.left = fTabHeight;
971 			break;
972 		case kRightSide:
973 			bordersToDraw &= ~BControlLook::B_RIGHT_BORDER;
974 			rect.right -= fTabHeight;
975 			break;
976 	}
977 
978 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
979 	if (fBorderStyle == B_FANCY_BORDER)
980 		be_control_look->DrawGroupFrame(this, rect, rect, base, bordersToDraw);
981 	else if (fBorderStyle == B_PLAIN_BORDER) {
982 		be_control_look->DrawBorder(this, rect, rect, base, B_PLAIN_BORDER,
983 			0, bordersToDraw);
984 	} else
985 		; // B_NO_BORDER draws no box
986 }
987 
988 
989 BRect
TabFrame(int32 index) const990 BTabView::TabFrame(int32 index) const
991 {
992 	if (index >= CountTabs() || index < 0)
993 		return BRect();
994 
995 	const float padding = ceilf(be_control_look->DefaultLabelSpacing() * 3.3f);
996 	const float height = fTabHeight;
997 	const float offset = BControlLook::ComposeSpacing(B_USE_WINDOW_SPACING);
998 	const BRect bounds(Bounds());
999 
1000 	float width = padding * 5.0f;
1001 	switch (fTabWidthSetting) {
1002 		case B_WIDTH_FROM_LABEL:
1003 		{
1004 			float x = 0.0f;
1005 			for (int32 i = 0; i < index; i++){
1006 				x += StringWidth(TabAt(i)->Label()) + padding;
1007 			}
1008 
1009 			switch (fTabSide) {
1010 				case kTopSide:
1011 					return BRect(offset + x, 0.0f,
1012 						offset + x + StringWidth(TabAt(index)->Label()) + padding,
1013 						height);
1014 				case kBottomSide:
1015 					return BRect(offset + x, bounds.bottom - height,
1016 						offset + x + StringWidth(TabAt(index)->Label()) + padding,
1017 						bounds.bottom);
1018 				case kLeftSide:
1019 					return BRect(0.0f, offset + x, height, offset + x
1020 						+ StringWidth(TabAt(index)->Label()) + padding);
1021 				case kRightSide:
1022 					return BRect(bounds.right - height, offset + x,
1023 						bounds.right, offset + x
1024 							+ StringWidth(TabAt(index)->Label()) + padding);
1025 				default:
1026 					return BRect();
1027 			}
1028 		}
1029 
1030 		case B_WIDTH_FROM_WIDEST:
1031 			width = 0.0;
1032 			for (int32 i = 0; i < CountTabs(); i++) {
1033 				float tabWidth = StringWidth(TabAt(i)->Label()) + padding;
1034 				if (tabWidth > width)
1035 					width = tabWidth;
1036 			}
1037 			// fall through
1038 
1039 		case B_WIDTH_AS_USUAL:
1040 		default:
1041 			switch (fTabSide) {
1042 				case kTopSide:
1043 					return BRect(offset + index * width, 0.0f,
1044 						offset + index * width + width, height);
1045 				case kBottomSide:
1046 					return BRect(offset + index * width, bounds.bottom - height,
1047 						offset + index * width + width, bounds.bottom);
1048 				case kLeftSide:
1049 					return BRect(0.0f, offset + index * width, height,
1050 						offset + index * width + width);
1051 				case kRightSide:
1052 					return BRect(bounds.right - height, offset + index * width,
1053 						bounds.right, offset + index * width + width);
1054 				default:
1055 					return BRect();
1056 			}
1057 	}
1058 }
1059 
1060 
1061 void
SetFlags(uint32 flags)1062 BTabView::SetFlags(uint32 flags)
1063 {
1064 	BView::SetFlags(flags);
1065 }
1066 
1067 
1068 void
SetResizingMode(uint32 mode)1069 BTabView::SetResizingMode(uint32 mode)
1070 {
1071 	BView::SetResizingMode(mode);
1072 }
1073 
1074 
1075 // #pragma mark -
1076 
1077 
1078 void
ResizeToPreferred()1079 BTabView::ResizeToPreferred()
1080 {
1081 	BView::ResizeToPreferred();
1082 }
1083 
1084 
1085 void
GetPreferredSize(float * _width,float * _height)1086 BTabView::GetPreferredSize(float* _width, float* _height)
1087 {
1088 	BView::GetPreferredSize(_width, _height);
1089 }
1090 
1091 
1092 BSize
MinSize()1093 BTabView::MinSize()
1094 {
1095 	BSize size;
1096 	if (GetLayout())
1097 		size = GetLayout()->MinSize();
1098 	else {
1099 		size = _TabsMinSize();
1100 		BSize containerSize = fContainerView->MinSize();
1101 		containerSize.width += 2 * _BorderWidth();
1102 		containerSize.height += 2 * _BorderWidth();
1103 		if (containerSize.width > size.width)
1104 			size.width = containerSize.width;
1105 		size.height += containerSize.height;
1106 	}
1107 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
1108 }
1109 
1110 
1111 BSize
MaxSize()1112 BTabView::MaxSize()
1113 {
1114 	BSize size;
1115 	if (GetLayout())
1116 		size = GetLayout()->MaxSize();
1117 	else {
1118 		size = _TabsMinSize();
1119 		BSize containerSize = fContainerView->MaxSize();
1120 		containerSize.width += 2 * _BorderWidth();
1121 		containerSize.height += 2 * _BorderWidth();
1122 		if (containerSize.width > size.width)
1123 			size.width = containerSize.width;
1124 		size.height += containerSize.height;
1125 	}
1126 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
1127 }
1128 
1129 
1130 BSize
PreferredSize()1131 BTabView::PreferredSize()
1132 {
1133 	BSize size;
1134 	if (GetLayout() != NULL)
1135 		size = GetLayout()->PreferredSize();
1136 	else {
1137 		size = _TabsMinSize();
1138 		BSize containerSize = fContainerView->PreferredSize();
1139 		containerSize.width += 2 * _BorderWidth();
1140 		containerSize.height += 2 * _BorderWidth();
1141 		if (containerSize.width > size.width)
1142 			size.width = containerSize.width;
1143 		size.height += containerSize.height;
1144 	}
1145 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
1146 }
1147 
1148 
1149 void
FrameMoved(BPoint newPosition)1150 BTabView::FrameMoved(BPoint newPosition)
1151 {
1152 	BView::FrameMoved(newPosition);
1153 }
1154 
1155 
1156 void
FrameResized(float newWidth,float newHeight)1157 BTabView::FrameResized(float newWidth, float newHeight)
1158 {
1159 	BView::FrameResized(newWidth, newHeight);
1160 }
1161 
1162 
1163 // #pragma mark -
1164 
1165 
1166 BHandler*
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 what,const char * property)1167 BTabView::ResolveSpecifier(BMessage* message, int32 index,
1168 	BMessage* specifier, int32 what, const char* property)
1169 {
1170 	BPropertyInfo propInfo(sPropertyList);
1171 
1172 	if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
1173 		return this;
1174 
1175 	return BView::ResolveSpecifier(message, index, specifier, what, property);
1176 }
1177 
1178 
1179 status_t
GetSupportedSuites(BMessage * message)1180 BTabView::GetSupportedSuites(BMessage* message)
1181 {
1182 	message->AddString("suites", "suite/vnd.Be-tab-view");
1183 
1184 	BPropertyInfo propInfo(sPropertyList);
1185 	message->AddFlat("messages", &propInfo);
1186 
1187 	return BView::GetSupportedSuites(message);
1188 }
1189 
1190 
1191 // #pragma mark -
1192 
1193 
1194 void
AddTab(BView * target,BTab * tab)1195 BTabView::AddTab(BView* target, BTab* tab)
1196 {
1197 	if (tab == NULL)
1198 		tab = new BTab(target);
1199 	else
1200 		tab->SetView(target);
1201 
1202 	if (fContainerView->GetLayout())
1203 		fContainerView->GetLayout()->AddView(CountTabs(), target);
1204 
1205 	fTabList->AddItem(tab);
1206 	BTab::Private(tab).SetTabView(this);
1207 
1208 	// When we haven't had a any tabs before, but are already attached to the
1209 	// window, select this one.
1210 	if (CountTabs() == 1 && Window() != NULL)
1211 		Select(0);
1212 }
1213 
1214 
1215 BTab*
RemoveTab(int32 index)1216 BTabView::RemoveTab(int32 index)
1217 {
1218 	if (index < 0 || index >= CountTabs())
1219 		return NULL;
1220 
1221 	BTab* tab = (BTab*)fTabList->RemoveItem(index);
1222 	if (tab == NULL)
1223 		return NULL;
1224 
1225 	tab->Deselect();
1226 	BTab::Private(tab).SetTabView(NULL);
1227 
1228 	if (fContainerView->GetLayout())
1229 		fContainerView->GetLayout()->RemoveItem(index);
1230 
1231 	if (CountTabs() == 0)
1232 		fFocus = -1;
1233 	else if (index <= fSelection)
1234 		Select(fSelection - 1);
1235 
1236 	if (fFocus >= 0) {
1237 		if (fFocus == CountTabs() - 1 || CountTabs() == 0)
1238 			SetFocusTab(fFocus, false);
1239 		else
1240 			SetFocusTab(fFocus, true);
1241 	}
1242 
1243 	return tab;
1244 }
1245 
1246 
1247 BTab*
TabAt(int32 index) const1248 BTabView::TabAt(int32 index) const
1249 {
1250 	return (BTab*)fTabList->ItemAt(index);
1251 }
1252 
1253 
1254 void
SetTabWidth(button_width width)1255 BTabView::SetTabWidth(button_width width)
1256 {
1257 	fTabWidthSetting = width;
1258 
1259 	Invalidate();
1260 }
1261 
1262 
1263 button_width
TabWidth() const1264 BTabView::TabWidth() const
1265 {
1266 	return fTabWidthSetting;
1267 }
1268 
1269 
1270 void
SetTabHeight(float height)1271 BTabView::SetTabHeight(float height)
1272 {
1273 	if (fTabHeight == height)
1274 		return;
1275 
1276 	fTabHeight = height;
1277 	_LayoutContainerView(GetLayout() != NULL);
1278 
1279 	Invalidate();
1280 }
1281 
1282 
1283 float
TabHeight() const1284 BTabView::TabHeight() const
1285 {
1286 	return fTabHeight;
1287 }
1288 
1289 
1290 void
SetBorder(border_style borderStyle)1291 BTabView::SetBorder(border_style borderStyle)
1292 {
1293 	if (fBorderStyle == borderStyle)
1294 		return;
1295 
1296 	fBorderStyle = borderStyle;
1297 
1298 	_LayoutContainerView((Flags() & B_SUPPORTS_LAYOUT) != 0);
1299 }
1300 
1301 
1302 border_style
Border() const1303 BTabView::Border() const
1304 {
1305 	return fBorderStyle;
1306 }
1307 
1308 
1309 void
SetTabSide(tab_side tabSide)1310 BTabView::SetTabSide(tab_side tabSide)
1311 {
1312 	if (fTabSide == tabSide)
1313 		return;
1314 
1315 	fTabSide = tabSide;
1316 	_LayoutContainerView(Flags() & B_SUPPORTS_LAYOUT);
1317 }
1318 
1319 
1320 BTabView::tab_side
TabSide() const1321 BTabView::TabSide() const
1322 {
1323 	return fTabSide;
1324 }
1325 
1326 
1327 BView*
ContainerView() const1328 BTabView::ContainerView() const
1329 {
1330 	return fContainerView;
1331 }
1332 
1333 
1334 int32
CountTabs() const1335 BTabView::CountTabs() const
1336 {
1337 	return fTabList->CountItems();
1338 }
1339 
1340 
1341 BView*
ViewForTab(int32 tabIndex) const1342 BTabView::ViewForTab(int32 tabIndex) const
1343 {
1344 	BTab* tab = TabAt(tabIndex);
1345 	if (tab != NULL)
1346 		return tab->View();
1347 
1348 	return NULL;
1349 }
1350 
1351 
1352 int32
IndexOf(BTab * tab) const1353 BTabView::IndexOf(BTab* tab) const
1354 {
1355 	if (tab != NULL) {
1356 		int32 tabCount = CountTabs();
1357 		for (int32 index = 0; index < tabCount; index++) {
1358 			if (TabAt(index) == tab)
1359 				return index;
1360 		}
1361 	}
1362 
1363 	return -1;
1364 }
1365 
1366 
1367 void
_InitObject(bool layouted,button_width width)1368 BTabView::_InitObject(bool layouted, button_width width)
1369 {
1370 	fTabList = new BList;
1371 
1372 	fTabWidthSetting = width;
1373 	fSelection = -1;
1374 	fFocus = -1;
1375 	fTabOffset = 0.0f;
1376 	fBorderStyle = B_FANCY_BORDER;
1377 	fTabSide = kTopSide;
1378 
1379 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1380 	SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1381 
1382 	font_height fh;
1383 	GetFontHeight(&fh);
1384 	fTabHeight = ceilf(fh.ascent + fh.descent + fh.leading +
1385 		(be_control_look->DefaultLabelSpacing() * 1.3f));
1386 
1387 	fContainerView = NULL;
1388 	_InitContainerView(layouted);
1389 }
1390 
1391 
1392 void
_InitContainerView(bool layouted)1393 BTabView::_InitContainerView(bool layouted)
1394 {
1395 	bool needsLayout = false;
1396 	bool createdContainer = false;
1397 	if (layouted) {
1398 		if (GetLayout() == NULL) {
1399 			SetLayout(new(std::nothrow) BGroupLayout(B_HORIZONTAL));
1400 			needsLayout = true;
1401 		}
1402 
1403 		if (fContainerView == NULL) {
1404 			fContainerView = new BView("view container", B_WILL_DRAW);
1405 			fContainerView->SetLayout(new(std::nothrow) BCardLayout());
1406 			createdContainer = true;
1407 		}
1408 	} else if (fContainerView == NULL) {
1409 		fContainerView = new BView(Bounds(), "view container", B_FOLLOW_ALL,
1410 			B_WILL_DRAW);
1411 		createdContainer = true;
1412 	}
1413 
1414 	if (needsLayout || createdContainer)
1415 		_LayoutContainerView(layouted);
1416 
1417 	if (createdContainer) {
1418 		fContainerView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1419 		fContainerView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1420 		AddChild(fContainerView);
1421 	}
1422 }
1423 
1424 
1425 BSize
_TabsMinSize() const1426 BTabView::_TabsMinSize() const
1427 {
1428 	BSize size(0.0f, TabHeight());
1429 	int32 count = min_c(2, CountTabs());
1430 	for (int32 i = 0; i < count; i++) {
1431 		BRect frame = TabFrame(i);
1432 		size.width += frame.Width();
1433 	}
1434 
1435 	if (count < CountTabs()) {
1436 		// TODO: Add size for yet to be implemented buttons that allow
1437 		// "scrolling" the displayed tabs left/right.
1438 	}
1439 
1440 	return size;
1441 }
1442 
1443 
1444 float
_BorderWidth() const1445 BTabView::_BorderWidth() const
1446 {
1447 	switch (fBorderStyle) {
1448 		default:
1449 		case B_FANCY_BORDER:
1450 			return 3.0f;
1451 
1452 		case B_PLAIN_BORDER:
1453 			return 1.0f;
1454 
1455 		case B_NO_BORDER:
1456 			return 0.0f;
1457 	}
1458 }
1459 
1460 
1461 void
_LayoutContainerView(bool layouted)1462 BTabView::_LayoutContainerView(bool layouted)
1463 {
1464 	float borderWidth = _BorderWidth();
1465 	if (layouted) {
1466 		float topBorderOffset;
1467 		switch (fBorderStyle) {
1468 			default:
1469 			case B_FANCY_BORDER:
1470 				topBorderOffset = 1.0f;
1471 				break;
1472 
1473 			case B_PLAIN_BORDER:
1474 				topBorderOffset = 0.0f;
1475 				break;
1476 
1477 			case B_NO_BORDER:
1478 				topBorderOffset = -1.0f;
1479 				break;
1480 		}
1481 		BGroupLayout* layout = dynamic_cast<BGroupLayout*>(GetLayout());
1482 		if (layout != NULL) {
1483 			float inset = borderWidth + TabHeight() - topBorderOffset;
1484 			switch (fTabSide) {
1485 				case kTopSide:
1486 					layout->SetInsets(borderWidth, inset, borderWidth,
1487 						borderWidth);
1488 					break;
1489 				case kBottomSide:
1490 					layout->SetInsets(borderWidth, borderWidth, borderWidth,
1491 						inset);
1492 					break;
1493 				case kLeftSide:
1494 					layout->SetInsets(inset, borderWidth, borderWidth,
1495 						borderWidth);
1496 					break;
1497 				case kRightSide:
1498 					layout->SetInsets(borderWidth, borderWidth, inset,
1499 						borderWidth);
1500 					break;
1501 			}
1502 		}
1503 	} else {
1504 		BRect bounds = Bounds();
1505 		switch (fTabSide) {
1506 			case kTopSide:
1507 				bounds.top += TabHeight();
1508 				break;
1509 			case kBottomSide:
1510 				bounds.bottom -= TabHeight();
1511 				break;
1512 			case kLeftSide:
1513 				bounds.left += TabHeight();
1514 				break;
1515 			case kRightSide:
1516 				bounds.right -= TabHeight();
1517 				break;
1518 		}
1519 		bounds.InsetBy(borderWidth, borderWidth);
1520 
1521 		fContainerView->MoveTo(bounds.left, bounds.top);
1522 		fContainerView->ResizeTo(bounds.Width(), bounds.Height());
1523 	}
1524 }
1525 
1526 
1527 // #pragma mark - FBC and forbidden
1528 
1529 
_ReservedTabView3()1530 void BTabView::_ReservedTabView3() {}
_ReservedTabView4()1531 void BTabView::_ReservedTabView4() {}
_ReservedTabView5()1532 void BTabView::_ReservedTabView5() {}
_ReservedTabView6()1533 void BTabView::_ReservedTabView6() {}
_ReservedTabView7()1534 void BTabView::_ReservedTabView7() {}
_ReservedTabView8()1535 void BTabView::_ReservedTabView8() {}
_ReservedTabView9()1536 void BTabView::_ReservedTabView9() {}
_ReservedTabView10()1537 void BTabView::_ReservedTabView10() {}
_ReservedTabView11()1538 void BTabView::_ReservedTabView11() {}
_ReservedTabView12()1539 void BTabView::_ReservedTabView12() {}
1540 
1541 
BTabView(const BTabView & tabView)1542 BTabView::BTabView(const BTabView& tabView)
1543 	: BView(tabView)
1544 {
1545 	// this is private and not functional, but exported
1546 }
1547 
1548 
1549 BTabView&
operator =(const BTabView &)1550 BTabView::operator=(const BTabView&)
1551 {
1552 	// this is private and not functional, but exported
1553 	return *this;
1554 }
1555 
1556 //	#pragma mark - binary compatibility
1557 
1558 
1559 extern "C" void
B_IF_GCC_2(_ReservedTabView1__8BTabView,_ZN8BTabView17_ReservedTabView1Ev)1560 B_IF_GCC_2(_ReservedTabView1__8BTabView, _ZN8BTabView17_ReservedTabView1Ev)(
1561 	BTabView* tabView, border_style borderStyle)
1562 {
1563 	tabView->BTabView::SetBorder(borderStyle);
1564 }
1565 
1566 extern "C" void
B_IF_GCC_2(_ReservedTabView2__8BTabView,_ZN8BTabView17_ReservedTabView2Ev)1567 B_IF_GCC_2(_ReservedTabView2__8BTabView, _ZN8BTabView17_ReservedTabView2Ev)(
1568 	BTabView* tabView, BTabView::tab_side tabSide)
1569 {
1570 	tabView->BTabView::SetTabSide(tabSide);
1571 }
1572