xref: /haiku/src/kits/interface/TabView.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
1 /*
2  * Copyright (c) 2001-2008, Haiku, Inc.
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  */
9 #include <TabView.h>
10 
11 #include <string.h>
12 
13 #include <List.h>
14 #include <Message.h>
15 #include <PropertyInfo.h>
16 #include <Rect.h>
17 #include <String.h>
18 
19 
20 static property_info sPropertyList[] = {
21 	{
22 		"Selection",
23 		{ B_GET_PROPERTY, B_SET_PROPERTY },
24 		{ B_DIRECT_SPECIFIER },
25 		NULL, 0,
26 		{ B_INT32_TYPE }
27 	},
28 	{}
29 };
30 
31 
32 
33 BTab::BTab(BView *tabView)
34 	:
35 	fEnabled(true),
36 	fSelected(false),
37 	fFocus(false),
38 	fView(tabView)
39 {
40 }
41 
42 
43 BTab::BTab(BMessage *archive)
44 	:
45 	fSelected(false),
46 	fFocus(false),
47 	fView(NULL)
48 {
49 	bool disable;
50 
51 	if (archive->FindBool("_disable", &disable) != B_OK)
52 		SetEnabled(true);
53 	else
54 		SetEnabled(!disable);
55 }
56 
57 
58 BTab::~BTab()
59 {
60 	if (!fView)
61 		return;
62 
63 	if (fSelected)
64 		fView->RemoveSelf();
65 
66 	delete fView;
67 }
68 
69 
70 BArchivable *
71 BTab::Instantiate(BMessage *archive)
72 {
73 	if (validate_instantiation(archive, "BTab"))
74 		return new BTab(archive);
75 
76 	return NULL;
77 }
78 
79 
80 status_t
81 BTab::Archive(BMessage *archive, bool deep) const
82 {
83 	status_t err = BArchivable::Archive(archive, deep);
84 	if (err != B_OK)
85 		return err;
86 
87 	if (!fEnabled)
88 		err = archive->AddBool("_disable", false);
89 
90 	return err;
91 }
92 
93 
94 status_t
95 BTab::Perform(uint32 d, void *arg)
96 {
97 	return BArchivable::Perform(d, arg);
98 }
99 
100 
101 const char *
102 BTab::Label() const
103 {
104 	if (fView)
105 		return fView->Name();
106 	else
107 		return NULL;
108 }
109 
110 
111 void
112 BTab::SetLabel(const char *label)
113 {
114 	if (!label || !fView)
115 		return;
116 
117 	fView->SetName(label);
118 }
119 
120 
121 bool
122 BTab::IsSelected() const
123 {
124 	return fSelected;
125 }
126 
127 
128 void
129 BTab::Select(BView *owner)
130 {
131 	if (!owner || !View() || !owner->Window())
132 		return;
133 
134 	owner->AddChild(fView);
135 	//fView->Show();
136 
137 	fSelected = true;
138 }
139 
140 
141 void
142 BTab::Deselect()
143 {
144 	if (View())
145 		View()->RemoveSelf();
146 
147 	fSelected = false;
148 }
149 
150 
151 void
152 BTab::SetEnabled(bool enabled)
153 {
154 	fEnabled = enabled;
155 }
156 
157 
158 bool
159 BTab::IsEnabled() const
160 {
161 	return fEnabled;
162 }
163 
164 
165 void
166 BTab::MakeFocus(bool inFocus)
167 {
168 	fFocus = inFocus;
169 }
170 
171 
172 bool
173 BTab::IsFocus() const
174 {
175 	return fFocus;
176 }
177 
178 
179 void
180 BTab::SetView(BView *view)
181 {
182 	if (!view || fView == view)
183 		return;
184 
185 	if (fView != NULL) {
186 		fView->RemoveSelf();
187 		delete fView;
188 	}
189 	fView = view;
190 }
191 
192 
193 BView *
194 BTab::View() const
195 {
196 	return fView;
197 }
198 
199 
200 void
201 BTab::DrawFocusMark(BView *owner, BRect frame)
202 {
203 	float width = owner->StringWidth(Label());
204 
205 	owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
206 	// TODO: remove offset
207 	float offset = frame.Height() / 2.0;
208 	owner->StrokeLine(BPoint((frame.left + frame.right - width + offset) / 2.0,
209 			frame.bottom - 3),
210 		BPoint((frame.left + frame.right + width + offset) / 2.0,
211 			frame.bottom - 3));
212 }
213 
214 
215 void
216 BTab::DrawLabel(BView *owner, BRect frame)
217 {
218 	if (Label() == NULL)
219 		return;
220 
221 	BString label = Label();
222 	float frameWidth = frame.Width();
223 	float width = owner->StringWidth(label.String());
224 	font_height fh;
225 
226 	if (width > frameWidth) {
227 		BFont font;
228 		owner->GetFont(&font);
229 		font.TruncateString(&label, B_TRUNCATE_END, frameWidth);
230 		width = frameWidth;
231 		font.GetHeight(&fh);
232 	} else {
233 		owner->GetFontHeight(&fh);
234 	}
235 
236 	owner->SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
237 	owner->DrawString(label.String(),
238 		BPoint((frame.left + frame.right - width) / 2.0,
239  			(frame.top + frame.bottom - fh.ascent - fh.descent) / 2.0
240  			+ fh.ascent));
241 }
242 
243 
244 void
245 BTab::DrawTab(BView *owner, BRect frame, tab_position position, bool full)
246 {
247 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
248 	rgb_color lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT);
249 	rgb_color darken2 = tint_color(no_tint, B_DARKEN_2_TINT);
250 	rgb_color darken3 = tint_color(no_tint, B_DARKEN_3_TINT);
251 	rgb_color darken4 = tint_color(no_tint, B_DARKEN_4_TINT);
252 	rgb_color darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT);
253 
254 	owner->SetHighColor(darkenmax);
255 	owner->SetLowColor(no_tint);
256 	// NOTE: "frame" goes from the beginning of the left slope to the beginning
257 	// of the right slope - "lableFrame" is the frame between both slopes
258 	BRect lableFrame = frame;
259 	lableFrame.left = lableFrame.left + frame.Height() / 2.0;
260 	DrawLabel(owner, lableFrame);
261 
262 	owner->SetDrawingMode(B_OP_OVER);
263 
264 	owner->BeginLineArray(12);
265 
266 	int32 slopeWidth = (int32)ceilf(frame.Height() / 2.0);
267 
268 	if (position != B_TAB_ANY) {
269 		// full height left side
270 		owner->AddLine(BPoint(frame.left, frame.bottom),
271 			BPoint(frame.left + slopeWidth, frame.top), darken3);
272 		owner->AddLine(BPoint(frame.left, frame.bottom + 1),
273 			BPoint(frame.left + slopeWidth, frame.top + 1), lightenmax);
274 	} else {
275 		// upper half of left side
276 		owner->AddLine(BPoint(frame.left + slopeWidth / 2,
277 				frame.bottom - slopeWidth),
278 			BPoint(frame.left + slopeWidth, frame.top), darken3);
279 		owner->AddLine(BPoint(frame.left + slopeWidth / 2 + 2,
280 				frame.bottom - slopeWidth - 1),
281 			BPoint(frame.left + slopeWidth, frame.top + 1), lightenmax);
282 	}
283 
284 	// lines along the top
285 	owner->AddLine(BPoint(frame.left + slopeWidth, frame.top),
286 		BPoint(frame.right, frame.top), darken3);
287 	owner->AddLine(BPoint(frame.left + slopeWidth, frame.top + 1),
288 		BPoint(frame.right, frame.top + 1), lightenmax);
289 
290 	if (full) {
291 		// full height right side
292 		owner->AddLine(BPoint(frame.right, frame.top),
293 			BPoint(frame.right + slopeWidth + 2, frame.bottom), darken2);
294 		owner->AddLine(BPoint(frame.right, frame.top + 1),
295 			BPoint(frame.right + slopeWidth + 1, frame.bottom), darken4);
296 	} else {
297 		// upper half of right side
298 		owner->AddLine(BPoint(frame.right, frame.top),
299 			BPoint(frame.right + slopeWidth / 2 + 1,
300 				frame.bottom - slopeWidth), darken2);
301 		owner->AddLine(BPoint(frame.right, frame.top + 1),
302 			BPoint(frame.right + slopeWidth / 2,
303 				frame.bottom - slopeWidth), darken4);
304 	}
305 
306 	owner->EndLineArray();
307 }
308 
309 
310 void BTab::_ReservedTab1() {}
311 void BTab::_ReservedTab2() {}
312 void BTab::_ReservedTab3() {}
313 void BTab::_ReservedTab4() {}
314 void BTab::_ReservedTab5() {}
315 void BTab::_ReservedTab6() {}
316 void BTab::_ReservedTab7() {}
317 void BTab::_ReservedTab8() {}
318 void BTab::_ReservedTab9() {}
319 void BTab::_ReservedTab10() {}
320 void BTab::_ReservedTab11() {}
321 void BTab::_ReservedTab12() {}
322 
323 BTab &BTab::operator=(const BTab &)
324 {
325 	// this is private and not functional, but exported
326 	return *this;
327 }
328 
329 
330 //	#pragma mark -
331 
332 
333 BTabView::BTabView(BRect frame, const char *name, button_width width,
334 	uint32 resizingMode, uint32 flags)
335 	: BView(frame, name, resizingMode, flags)
336 {
337 	SetFont(be_bold_font);
338 
339 	_InitObject();
340 
341 	fTabWidthSetting = width;
342 }
343 
344 
345 BTabView::~BTabView()
346 {
347 	for (int32 i = 0; i < CountTabs(); i++) {
348 		delete TabAt(i);
349 	}
350 
351 	delete fTabList;
352 }
353 
354 
355 BTabView::BTabView(BMessage *archive)
356 	: BView(archive),
357 	fFocus(-1)
358 {
359 	fContainerView = NULL;
360 	fTabList = new BList;
361 
362 	int16 width;
363 
364 	if (archive->FindInt16("_but_width", &width) == B_OK)
365 		fTabWidthSetting = (button_width)width;
366 	else
367 		fTabWidthSetting = B_WIDTH_AS_USUAL;
368 
369 	if (archive->FindFloat("_high", &fTabHeight) != B_OK) {
370 		font_height fh;
371 		GetFontHeight(&fh);
372 		fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f;
373 	}
374 
375 	fFocus = -1;
376 
377 	if (archive->FindInt32("_sel", &fSelection) != B_OK)
378 		fSelection = 0;
379 
380 	if (fContainerView == NULL)
381 		fContainerView = ChildAt(0);
382 
383 	int32 i = 0;
384 	BMessage tabMsg;
385 
386 	while (archive->FindMessage("_l_items", i, &tabMsg) == B_OK) {
387 		BArchivable *archivedTab = instantiate_object(&tabMsg);
388 
389 		if (archivedTab) {
390 			BTab *tab = dynamic_cast<BTab *>(archivedTab);
391 
392 			BMessage viewMsg;
393 			if (archive->FindMessage("_view_list", i, &viewMsg) == B_OK) {
394 				BArchivable *archivedView = instantiate_object(&viewMsg);
395 				if (archivedView)
396 					AddTab(dynamic_cast<BView*>(archivedView), tab);
397 			}
398 		}
399 
400 		tabMsg.MakeEmpty();
401 		i++;
402 	}
403 }
404 
405 
406 BArchivable *
407 BTabView::Instantiate(BMessage *archive)
408 {
409 	if ( validate_instantiation(archive, "BTabView"))
410 		return new BTabView(archive);
411 
412 	return NULL;
413 }
414 
415 
416 status_t
417 BTabView::Archive(BMessage *archive, bool deep) const
418 {
419 	if (CountTabs() > 0)
420 		TabAt(Selection())->View()->RemoveSelf();
421 
422 	status_t ret = BView::Archive(archive, deep);
423 
424 	if (ret == B_OK)
425 		ret = archive->AddInt16("_but_width", fTabWidthSetting);
426 	if (ret == B_OK)
427 		ret = archive->AddFloat("_high", fTabHeight);
428 	if (ret == B_OK)
429 		ret = archive->AddInt32("_sel", fSelection);
430 
431 	if (ret == B_OK && deep) {
432 		for (int32 i = 0; i < CountTabs(); i++) {
433 			BMessage tabArchive;
434 			BTab *tab = TabAt(i);
435 
436 			if (!tab)
437 				continue;
438 			ret = tab->Archive(&tabArchive, true);
439 			if (ret == B_OK)
440 				ret = archive->AddMessage("_l_items", &tabArchive);
441 
442 			if (!tab->View())
443 				continue;
444 
445 			BMessage viewArchive;
446 			ret = tab->View()->Archive(&viewArchive, true);
447 			if (ret == B_OK)
448 				ret = archive->AddMessage("_view_list", &viewArchive);
449 		}
450 	}
451 
452 	if (CountTabs() > 0) {
453 		if (TabAt(Selection())->View() && ContainerView())
454 			TabAt(Selection())->Select(ContainerView());
455 	}
456 
457 	return ret;
458 }
459 
460 
461 status_t
462 BTabView::Perform(perform_code d, void *arg)
463 {
464 	return BView::Perform(d, arg);
465 }
466 
467 
468 void
469 BTabView::WindowActivated(bool active)
470 {
471 	BView::WindowActivated(active);
472 
473 	if (IsFocus())
474 		Invalidate();
475 }
476 
477 
478 void
479 BTabView::AttachedToWindow()
480 {
481 	BView::AttachedToWindow();
482 
483 	Select(fSelection);
484 }
485 
486 
487 void
488 BTabView::AllAttached()
489 {
490 	BView::AllAttached();
491 }
492 
493 
494 void
495 BTabView::AllDetached()
496 {
497 	BView::AllDetached();
498 }
499 
500 
501 void
502 BTabView::DetachedFromWindow()
503 {
504 	BView::DetachedFromWindow();
505 }
506 
507 
508 void
509 BTabView::MessageReceived(BMessage *message)
510 {
511 	switch (message->what) {
512 		case B_GET_PROPERTY:
513 		case B_SET_PROPERTY:
514 		{
515 			BMessage reply(B_REPLY);
516 			bool handled = false;
517 
518 			BMessage specifier;
519 			int32 index;
520 			int32 form;
521 			const char *property;
522 			if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) {
523 				if (strcmp(property, "Selection") == 0) {
524 					if (message->what == B_GET_PROPERTY) {
525 						reply.AddInt32("result", fSelection);
526 						handled = true;
527 					} else {
528 						// B_GET_PROPERTY
529 						int32 selection;
530 						if (message->FindInt32("data", &selection) == B_OK) {
531 							Select(selection);
532 							reply.AddInt32("error", B_OK);
533 							handled = true;
534 						}
535 					}
536 				}
537 			}
538 
539 			if (handled)
540 				message->SendReply(&reply);
541 			else
542 				BView::MessageReceived(message);
543 			break;
544 		}
545 
546 		case B_MOUSE_WHEEL_CHANGED:
547 		{
548 			float deltaX = 0.0f;
549 			float deltaY = 0.0f;
550 			message->FindFloat("be:wheel_delta_x", &deltaX);
551 			message->FindFloat("be:wheel_delta_y", &deltaY);
552 
553 			if (deltaX == 0.0f && deltaY == 0.0f)
554 				return;
555 
556 			if (deltaY == 0.0f)
557 				deltaY = deltaX;
558 
559 			int32 selection = Selection();
560 			int32 numTabs = CountTabs();
561 			if (deltaY > 0  && selection < numTabs - 1) {
562 				//move to the right tab.
563 				Select(Selection() + 1);
564 			} else if (deltaY < 0 && selection > 0 && numTabs > 1) {
565 				//move to the left tab.
566 				Select(selection - 1);
567 			}
568 		}
569 		default:
570 			BView::MessageReceived(message);
571 			break;
572 	}
573 }
574 
575 
576 void
577 BTabView::FrameMoved(BPoint newLocation)
578 {
579 	BView::FrameMoved(newLocation);
580 }
581 
582 
583 void
584 BTabView::FrameResized(float width,float height)
585 {
586 	BView::FrameResized(width, height);
587 }
588 
589 
590 void
591 BTabView::KeyDown(const char *bytes, int32 numBytes)
592 {
593 	if (IsHidden())
594 		return;
595 
596 	switch (bytes[0]) {
597 		case B_DOWN_ARROW:
598 		case B_LEFT_ARROW: {
599 			int32 focus = fFocus - 1;
600 			if (focus < 0)
601 				focus = CountTabs() - 1;
602 			SetFocusTab(focus, true);
603 			break;
604 		}
605 
606 		case B_UP_ARROW:
607 		case B_RIGHT_ARROW: {
608 			int32 focus = fFocus + 1;
609 			if (focus >= CountTabs())
610 				focus = 0;
611 			SetFocusTab(focus, true);
612 			break;
613 		}
614 
615 		case B_RETURN:
616 		case B_SPACE:
617 			Select(FocusTab());
618 			break;
619 
620 		default:
621 			BView::KeyDown(bytes, numBytes);
622 	}
623 }
624 
625 
626 void
627 BTabView::MouseDown(BPoint point)
628 {
629 	if (point.y > fTabHeight)
630 		return;
631 
632 	for (int32 i = 0; i < CountTabs(); i++) {
633 		if (TabFrame(i).Contains(point)
634 			&& i != Selection()) {
635 			Select(i);
636 			return;
637 		}
638 	}
639 
640 	BView::MouseDown(point);
641 }
642 
643 
644 void
645 BTabView::MouseUp(BPoint point)
646 {
647 	BView::MouseUp(point);
648 }
649 
650 
651 void
652 BTabView::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
653 {
654 	BView::MouseMoved(point, transit, message);
655 }
656 
657 
658 void
659 BTabView::Pulse()
660 {
661 	BView::Pulse();
662 }
663 
664 
665 void
666 BTabView::Select(int32 index)
667 {
668 	if (index < 0 || index >= CountTabs())
669 		index = Selection();
670 
671 	BTab *tab = TabAt(Selection());
672 	if (tab)
673 		tab->Deselect();
674 
675 	tab = TabAt(index);
676 	if (tab && ContainerView()) {
677 		if(index == 0)
678 			fTabOffset = 0.0f;
679 		tab->Select(ContainerView());
680 		fSelection = index;
681 	}
682 
683 	Invalidate();
684 
685 	if(index != 0 && !Bounds().Contains(TabFrame(index))){
686 		if(!Bounds().Contains(TabFrame(index).LeftTop()))
687 			fTabOffset += TabFrame(index).left - Bounds().left - 20.0f;
688 		else
689 			fTabOffset += TabFrame(index).right - Bounds().right + 20.0f;
690 
691 		Invalidate();
692 	}
693 
694 	/*MakeFocus();
695 	SetFocusTab(index, true);
696 	FocusTab();*/
697 }
698 
699 
700 int32
701 BTabView::Selection() const
702 {
703 	return fSelection;
704 }
705 
706 
707 void
708 BTabView::MakeFocus(bool focused)
709 {
710 	BView::MakeFocus(focused);
711 
712 	SetFocusTab(Selection(), focused);
713 }
714 
715 
716 void
717 BTabView::SetFocusTab(int32 tab, bool focused)
718 {
719 	if (tab >= CountTabs())
720 		tab = 0;
721 
722 	if(tab < 0)
723 		tab = CountTabs() - 1;
724 
725 	if (focused) {
726 		if (tab == fFocus)
727 			return;
728 
729 		if (fFocus != -1){
730 			if(TabAt (fFocus) != NULL)
731 				TabAt(fFocus)->MakeFocus(false);
732 			Invalidate(TabFrame(fFocus));
733 		}
734 		if(TabAt(tab) != NULL){
735 			TabAt(tab)->MakeFocus(true);
736 			Invalidate(TabFrame(tab));
737 			fFocus = tab;
738 		}
739 	} else if (fFocus != -1) {
740 		TabAt(fFocus)->MakeFocus(false);
741 		Invalidate(TabFrame(fFocus));
742 		fFocus = -1;
743 	}
744 }
745 
746 
747 int32
748 BTabView::FocusTab() const
749 {
750 	return fFocus;
751 }
752 
753 
754 void
755 BTabView::Draw(BRect updateRect)
756 {
757 	DrawBox(DrawTabs());
758 
759 	if (IsFocus() && fFocus != -1)
760 		TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus));
761 }
762 
763 
764 BRect
765 BTabView::DrawTabs()
766 {
767 	for (int32 i = 0; i < CountTabs(); i++) {
768 		TabAt(i)->DrawTab(this, TabFrame(i),
769 			i == fSelection ? B_TAB_FRONT : (i == 0) ? B_TAB_FIRST : B_TAB_ANY,
770 			i + 1 != fSelection);
771 	}
772 
773 	if (fSelection < CountTabs())
774 		return TabFrame(fSelection);
775 
776 	return BRect();
777 }
778 
779 
780 void
781 BTabView::DrawBox(BRect selTabRect)
782 {
783 	BRect rect = Bounds();
784 	BRect lastTabRect = TabFrame(CountTabs() - 1);
785 
786 	rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
787 	rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
788 	rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT);
789 	rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT);
790 	rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT);
791 
792 	BeginLineArray(12);
793 
794 	int32 offset = (int32)ceilf(selTabRect.Height() / 2.0);
795 
796 	// outer lines
797 	AddLine(BPoint(rect.left, rect.bottom - 1),
798 			BPoint(rect.left, selTabRect.bottom), darken2);
799 	if (selTabRect.left >= rect.left + 1)
800 		AddLine(BPoint(rect.left + 1, selTabRect.bottom),
801 				BPoint(selTabRect.left, selTabRect.bottom), darken2);
802 	if (lastTabRect.right + offset + 1 <= rect.right - 1)
803 		AddLine(BPoint(lastTabRect.right + offset + 1, selTabRect.bottom),
804 				BPoint(rect.right - 1, selTabRect.bottom), darken2);
805 	AddLine(BPoint(rect.right, selTabRect.bottom + 2),
806 			BPoint(rect.right, rect.bottom), darken2);
807 	AddLine(BPoint(rect.right - 1, rect.bottom),
808 			BPoint(rect.left + 2, rect.bottom), darken2);
809 
810 	// inner lines
811 	rect.InsetBy(1, 1);
812 	selTabRect.bottom += 1;
813 
814 	AddLine(BPoint(rect.left, rect.bottom - 2),
815 			BPoint(rect.left, selTabRect.bottom), lightenMax);
816 	if (selTabRect.left >= rect.left + 1)
817 		AddLine(BPoint(rect.left + 1, selTabRect.bottom),
818 				BPoint(selTabRect.left, selTabRect.bottom), lightenMax);
819 	if (selTabRect.right + offset + 1 <= rect.right - 2)
820 		AddLine(BPoint(selTabRect.right + offset + 1, selTabRect.bottom),
821 				BPoint(rect.right - 2, selTabRect.bottom), lightenMax);
822 	AddLine(BPoint(rect.right, selTabRect.bottom),
823 			BPoint(rect.right, rect.bottom), darken4);
824 	AddLine(BPoint(rect.right - 1, rect.bottom),
825 			BPoint(rect.left, rect.bottom), darken4);
826 
827 	// soft inner bevel at right/bottom
828 	rect.right--;
829 	rect.bottom--;
830 
831 	AddLine(BPoint(rect.right, selTabRect.bottom + 1),
832 			BPoint(rect.right, rect.bottom), darken1);
833 	AddLine(BPoint(rect.right - 1, rect.bottom),
834 			BPoint(rect.left + 1, rect.bottom), darken1);
835 
836 	EndLineArray();
837 }
838 
839 #define X_OFFSET 0.0f
840 
841 BRect
842 BTabView::TabFrame(int32 tab_index) const
843 {
844 	// TODO: fix to remove "offset" in DrawTab and DrawLabel ...
845 	switch (fTabWidthSetting) {
846 		case B_WIDTH_FROM_LABEL:
847 		{
848 			float x = 6.0f;
849 			for (int32 i = 0; i < tab_index; i++){
850 				x += StringWidth(TabAt(i)->Label()) + 20.0f;
851 			}
852 
853 			return BRect(x - fTabOffset, 0.0f,
854 				x - fTabOffset + StringWidth(TabAt(tab_index)->Label()) + 20.0f , fTabHeight);
855 
856 
857 			/*float x = X_OFFSET;
858 			for (int32 i = 0; i < tab_index; i++)
859 				x += StringWidth(TabAt(i)->Label()) + 20.0f;
860 
861 			return BRect(x, 0.0f,
862 				x + StringWidth(TabAt(tab_index)->Label()) + 20.0f, fTabHeight);*/
863 		}
864 
865 		case B_WIDTH_FROM_WIDEST:
866 		{
867 			float width = 0.0f;
868 
869 			for (int32 i = 0; i < CountTabs(); i++) {
870 				float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f;
871 				if (tabWidth > width)
872 					width = tabWidth;
873 			}
874 			return BRect((6.0f + tab_index * width) - fTabOffset, 0.0f,
875 				(6.0f + tab_index * width + width) - fTabOffset, fTabHeight);
876 			/*float width = 0.0f;
877 
878 			for (int32 i = 0; i < CountTabs(); i++) {
879 				float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f;
880 
881 				if (tabWidth > width)
882 					width = tabWidth;
883 			}
884 
885 			return BRect(X_OFFSET + tab_index * width, 0.0f,
886 				X_OFFSET + tab_index * width + width, fTabHeight);*/
887 		}
888 
889 		case B_WIDTH_AS_USUAL:
890 		default:
891 			return BRect((6.0f + tab_index * 100.0f) - fTabOffset, 0.0f,
892 						(6.0f + tab_index * 100.0f + 100.0f) - fTabOffset, fTabHeight);
893 			/*return BRect(X_OFFSET + tab_index * 100.0f, 0.0f,
894 				X_OFFSET + tab_index * 100.0f + 100.0f, fTabHeight);*/
895 	}
896 }
897 
898 
899 void
900 BTabView::SetFlags(uint32 flags)
901 {
902 	BView::SetFlags(flags);
903 }
904 
905 
906 void
907 BTabView::SetResizingMode(uint32 mode)
908 {
909 	BView::SetResizingMode(mode);
910 }
911 
912 
913 void
914 BTabView::GetPreferredSize(float *width, float *height)
915 {
916 	BView::GetPreferredSize(width, height);
917 }
918 
919 
920 void
921 BTabView::ResizeToPreferred()
922 {
923 	BView::ResizeToPreferred();
924 }
925 
926 
927 BHandler *
928 BTabView::ResolveSpecifier(BMessage *message, int32 index,
929 	BMessage *specifier, int32 what, const char *property)
930 {
931 	BPropertyInfo propInfo(sPropertyList);
932 
933 	if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
934 		return this;
935 
936 	return BView::ResolveSpecifier(message, index, specifier, what,
937 		property);
938 }
939 
940 
941 status_t
942 BTabView::GetSupportedSuites(BMessage *message)
943 {
944 	message->AddString("suites", "suite/vnd.Be-tab-view");
945 
946 	BPropertyInfo propInfo(sPropertyList);
947 	message->AddFlat("messages", &propInfo);
948 
949 	return BView::GetSupportedSuites(message);
950 }
951 
952 
953 void
954 BTabView::AddTab(BView *target, BTab *tab)
955 {
956 	if (tab == NULL)
957 		tab = new BTab(target);
958 	else
959 		tab->SetView(target);
960 
961 	fTabList->AddItem(tab);
962 }
963 
964 
965 BTab *
966 BTabView::RemoveTab(int32 index)
967 {
968 	if (index < 0 || index >= CountTabs())
969 		return NULL;
970 
971 	BTab *tab = (BTab *)fTabList->RemoveItem(index);
972 	if (tab == NULL)
973 		return NULL;
974 
975 	tab->Deselect();
976 
977 	if (index <= fSelection && fSelection != 0)
978 		fSelection--;
979 
980 	if(CountTabs() == 0)
981 		fFocus = -1;
982 	else
983 		Select(fSelection);
984 
985 	if (fFocus == CountTabs() - 1 || CountTabs() == 0)
986 		SetFocusTab(fFocus, false);
987 	else
988 		SetFocusTab(fFocus, true);
989 
990 	return tab;
991 }
992 
993 
994 BTab *
995 BTabView::TabAt(int32 index) const
996 {
997 	return (BTab *)fTabList->ItemAt(index);
998 }
999 
1000 
1001 void
1002 BTabView::SetTabWidth(button_width width)
1003 {
1004 	fTabWidthSetting = width;
1005 
1006 	Invalidate();
1007 }
1008 
1009 
1010 button_width
1011 BTabView::TabWidth() const
1012 {
1013 	return fTabWidthSetting;
1014 }
1015 
1016 
1017 void
1018 BTabView::SetTabHeight(float height)
1019 {
1020 	if (fTabHeight == height)
1021 		return;
1022 
1023 	fContainerView->MoveBy(0.0f, height - fTabHeight);
1024 	fContainerView->ResizeBy(0.0f, height - fTabHeight);
1025 
1026 	fTabHeight = height;
1027 
1028 	Invalidate();
1029 }
1030 
1031 
1032 float
1033 BTabView::TabHeight() const
1034 {
1035 	return fTabHeight;
1036 }
1037 
1038 
1039 BView *
1040 BTabView::ContainerView() const
1041 {
1042 	return fContainerView;
1043 }
1044 
1045 
1046 int32
1047 BTabView::CountTabs() const
1048 {
1049 	return fTabList->CountItems();
1050 }
1051 
1052 
1053 BView *
1054 BTabView::ViewForTab(int32 tabIndex) const
1055 {
1056 	BTab *tab = TabAt(tabIndex);
1057 	if (tab)
1058 		return tab->View();
1059 
1060 	return NULL;
1061 }
1062 
1063 
1064 void
1065 BTabView::_InitObject()
1066 {
1067 	fTabList = new BList;
1068 
1069 	fTabWidthSetting = B_WIDTH_AS_USUAL;
1070 	fSelection = 0;
1071 	fFocus = -1;
1072 	fTabOffset = 0.0f;
1073 
1074 	rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR);
1075 
1076 	SetViewColor(color);
1077 	SetLowColor(color);
1078 
1079 	font_height fh;
1080 	GetFontHeight(&fh);
1081 	fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f;
1082 
1083 	BRect bounds = Bounds();
1084 
1085 	bounds.top += TabHeight();
1086 	bounds.InsetBy(3.0f, 3.0f);
1087 
1088 	fContainerView = new BView(bounds, "view container", B_FOLLOW_ALL,
1089 		B_WILL_DRAW);
1090 
1091 	fContainerView->SetViewColor(color);
1092 	fContainerView->SetLowColor(color);
1093 
1094 	AddChild(fContainerView);
1095 }
1096 
1097 
1098 void BTabView::_ReservedTabView1() {}
1099 void BTabView::_ReservedTabView2() {}
1100 void BTabView::_ReservedTabView3() {}
1101 void BTabView::_ReservedTabView4() {}
1102 void BTabView::_ReservedTabView5() {}
1103 void BTabView::_ReservedTabView6() {}
1104 void BTabView::_ReservedTabView7() {}
1105 void BTabView::_ReservedTabView8() {}
1106 void BTabView::_ReservedTabView9() {}
1107 void BTabView::_ReservedTabView10() {}
1108 void BTabView::_ReservedTabView11() {}
1109 void BTabView::_ReservedTabView12() {}
1110 
1111 
1112 BTabView::BTabView(const BTabView &tabView)
1113 	: BView(tabView)
1114 {
1115 	// this is private and not functional, but exported
1116 }
1117 
1118 
1119 BTabView &BTabView::operator=(const BTabView &)
1120 {
1121 	// this is private and not functional, but exported
1122 	return *this;
1123 }
1124