xref: /haiku/src/kits/interface/TabView.cpp (revision a381c8a06378de22ff08adf4282b4e3f7e50d250)
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 	if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) {
512 		BMessage reply(B_REPLY);
513 		bool handled = false;
514 
515 		BMessage specifier;
516 		int32 index;
517 		int32 form;
518 		const char *property;
519 		if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) {
520 			if (strcmp(property, "Selection") == 0) {
521 				if (message->what == B_GET_PROPERTY) {
522 					reply.AddInt32("result", fSelection);
523 					handled = true;
524 				} else {
525 					// B_GET_PROPERTY
526 					int32 selection;
527 					if (message->FindInt32("data", &selection) == B_OK) {
528 						Select(selection);
529 						reply.AddInt32("error", B_OK);
530 						handled = true;
531 					}
532 				}
533 			}
534 		}
535 
536 		if (handled) {
537 			message->SendReply(&reply);
538 			return;
539 		}
540 	}
541 	BView::MessageReceived(message);
542 }
543 
544 
545 void
546 BTabView::FrameMoved(BPoint newLocation)
547 {
548 	BView::FrameMoved(newLocation);
549 }
550 
551 
552 void
553 BTabView::FrameResized(float width,float height)
554 {
555 	BView::FrameResized(width, height);
556 }
557 
558 
559 void
560 BTabView::KeyDown(const char *bytes, int32 numBytes)
561 {
562 	if (IsHidden())
563 		return;
564 
565 	switch (bytes[0]) {
566 		case B_DOWN_ARROW:
567 		case B_LEFT_ARROW: {
568 			int32 focus = fFocus - 1;
569 			if (focus < 0)
570 				focus = CountTabs() - 1;
571 			SetFocusTab(focus, true);
572 			break;
573 		}
574 
575 		case B_UP_ARROW:
576 		case B_RIGHT_ARROW: {
577 			int32 focus = fFocus + 1;
578 			if (focus >= CountTabs())
579 				focus = 0;
580 			SetFocusTab(focus, true);
581 			break;
582 		}
583 
584 		case B_RETURN:
585 		case B_SPACE:
586 			Select(FocusTab());
587 			break;
588 
589 		default:
590 			BView::KeyDown(bytes, numBytes);
591 	}
592 }
593 
594 
595 void
596 BTabView::MouseDown(BPoint point)
597 {
598 	if (point.y > fTabHeight)
599 		return;
600 
601 	for (int32 i = 0; i < CountTabs(); i++) {
602 		if (TabFrame(i).Contains(point)
603 			&& i != Selection()) {
604 			Select(i);
605 			return;
606 		}
607 	}
608 
609 	BView::MouseDown(point);
610 }
611 
612 
613 void
614 BTabView::MouseUp(BPoint point)
615 {
616 	BView::MouseUp(point);
617 }
618 
619 
620 void
621 BTabView::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
622 {
623 	BView::MouseMoved(point, transit, message);
624 }
625 
626 
627 void
628 BTabView::Pulse()
629 {
630 	BView::Pulse();
631 }
632 
633 
634 void
635 BTabView::Select(int32 index)
636 {
637 	if (index < 0 || index >= CountTabs())
638 		index = Selection();
639 
640 	BTab *tab = TabAt(Selection());
641 	if (tab)
642 		tab->Deselect();
643 
644 	tab = TabAt(index);
645 	if (tab && ContainerView()) {
646 		if(index == 0)
647 			fTabOffset = 0.0f;
648 		tab->Select(ContainerView());
649 		fSelection = index;
650 	}
651 
652 	Invalidate();
653 
654 	if(index != 0 && !Bounds().Contains(TabFrame(index))){
655 		if(!Bounds().Contains(TabFrame(index).LeftTop()))
656 			fTabOffset += TabFrame(index).left - Bounds().left - 20.0f;
657 		else
658 			fTabOffset += TabFrame(index).right - Bounds().right + 20.0f;
659 
660 		Invalidate();
661 	}
662 
663 	/*MakeFocus();
664 	SetFocusTab(index, true);
665 	FocusTab();*/
666 }
667 
668 
669 int32
670 BTabView::Selection() const
671 {
672 	return fSelection;
673 }
674 
675 
676 void
677 BTabView::MakeFocus(bool focused)
678 {
679 	BView::MakeFocus(focused);
680 
681 	SetFocusTab(Selection(), focused);
682 }
683 
684 
685 void
686 BTabView::SetFocusTab(int32 tab, bool focused)
687 {
688 	if (tab >= CountTabs())
689 		tab = 0;
690 
691 	if(tab < 0)
692 		tab = CountTabs() - 1;
693 
694 	if (focused) {
695 		if (tab == fFocus)
696 			return;
697 
698 		if (fFocus != -1){
699 			if(TabAt (fFocus) != NULL)
700 				TabAt(fFocus)->MakeFocus(false);
701 			Invalidate(TabFrame(fFocus));
702 		}
703 		if(TabAt(tab) != NULL){
704 			TabAt(tab)->MakeFocus(true);
705 			Invalidate(TabFrame(tab));
706 			fFocus = tab;
707 		}
708 	} else if (fFocus != -1) {
709 		TabAt(fFocus)->MakeFocus(false);
710 		Invalidate(TabFrame(fFocus));
711 		fFocus = -1;
712 	}
713 }
714 
715 
716 int32
717 BTabView::FocusTab() const
718 {
719 	return fFocus;
720 }
721 
722 
723 void
724 BTabView::Draw(BRect updateRect)
725 {
726 	DrawBox(DrawTabs());
727 
728 	if (IsFocus() && fFocus != -1)
729 		TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus));
730 }
731 
732 
733 BRect
734 BTabView::DrawTabs()
735 {
736 	for (int32 i = 0; i < CountTabs(); i++) {
737 		TabAt(i)->DrawTab(this, TabFrame(i),
738 			i == fSelection ? B_TAB_FRONT : (i == 0) ? B_TAB_FIRST : B_TAB_ANY,
739 			i + 1 != fSelection);
740 	}
741 
742 	if (fSelection < CountTabs())
743 		return TabFrame(fSelection);
744 
745 	return BRect();
746 }
747 
748 
749 void
750 BTabView::DrawBox(BRect selTabRect)
751 {
752 	BRect rect = Bounds();
753 	BRect lastTabRect = TabFrame(CountTabs() - 1);
754 
755 	rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
756 	rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
757 	rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT);
758 	rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT);
759 	rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT);
760 
761 	BeginLineArray(12);
762 
763 	int32 offset = (int32)ceilf(selTabRect.Height() / 2.0);
764 
765 	// outer lines
766 	AddLine(BPoint(rect.left, rect.bottom - 1),
767 			BPoint(rect.left, selTabRect.bottom), darken2);
768 	if (selTabRect.left >= rect.left + 1)
769 		AddLine(BPoint(rect.left + 1, selTabRect.bottom),
770 				BPoint(selTabRect.left, selTabRect.bottom), darken2);
771 	if (lastTabRect.right + offset + 1 <= rect.right - 1)
772 		AddLine(BPoint(lastTabRect.right + offset + 1, selTabRect.bottom),
773 				BPoint(rect.right - 1, selTabRect.bottom), darken2);
774 	AddLine(BPoint(rect.right, selTabRect.bottom + 2),
775 			BPoint(rect.right, rect.bottom), darken2);
776 	AddLine(BPoint(rect.right - 1, rect.bottom),
777 			BPoint(rect.left + 2, rect.bottom), darken2);
778 
779 	// inner lines
780 	rect.InsetBy(1, 1);
781 	selTabRect.bottom += 1;
782 
783 	AddLine(BPoint(rect.left, rect.bottom - 2),
784 			BPoint(rect.left, selTabRect.bottom), lightenMax);
785 	if (selTabRect.left >= rect.left + 1)
786 		AddLine(BPoint(rect.left + 1, selTabRect.bottom),
787 				BPoint(selTabRect.left, selTabRect.bottom), lightenMax);
788 	if (selTabRect.right + offset + 1 <= rect.right - 2)
789 		AddLine(BPoint(selTabRect.right + offset + 1, selTabRect.bottom),
790 				BPoint(rect.right - 2, selTabRect.bottom), lightenMax);
791 	AddLine(BPoint(rect.right, selTabRect.bottom),
792 			BPoint(rect.right, rect.bottom), darken4);
793 	AddLine(BPoint(rect.right - 1, rect.bottom),
794 			BPoint(rect.left, rect.bottom), darken4);
795 
796 	// soft inner bevel at right/bottom
797 	rect.right--;
798 	rect.bottom--;
799 
800 	AddLine(BPoint(rect.right, selTabRect.bottom + 1),
801 			BPoint(rect.right, rect.bottom), darken1);
802 	AddLine(BPoint(rect.right - 1, rect.bottom),
803 			BPoint(rect.left + 1, rect.bottom), darken1);
804 
805 	EndLineArray();
806 }
807 
808 #define X_OFFSET 0.0f
809 
810 BRect
811 BTabView::TabFrame(int32 tab_index) const
812 {
813 	// TODO: fix to remove "offset" in DrawTab and DrawLabel ...
814 	switch (fTabWidthSetting) {
815 		case B_WIDTH_FROM_LABEL:
816 		{
817 			float x = 6.0f;
818 			for (int32 i = 0; i < tab_index; i++){
819 				x += StringWidth(TabAt(i)->Label()) + 20.0f;
820 			}
821 
822 			return BRect(x - fTabOffset, 0.0f,
823 				x - fTabOffset + StringWidth(TabAt(tab_index)->Label()) + 20.0f , fTabHeight);
824 
825 
826 			/*float x = X_OFFSET;
827 			for (int32 i = 0; i < tab_index; i++)
828 				x += StringWidth(TabAt(i)->Label()) + 20.0f;
829 
830 			return BRect(x, 0.0f,
831 				x + StringWidth(TabAt(tab_index)->Label()) + 20.0f, fTabHeight);*/
832 		}
833 
834 		case B_WIDTH_FROM_WIDEST:
835 		{
836 			float width = 0.0f;
837 
838 			for (int32 i = 0; i < CountTabs(); i++) {
839 				float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f;
840 				if (tabWidth > width)
841 					width = tabWidth;
842 			}
843 			return BRect((6.0f + tab_index * width) - fTabOffset, 0.0f,
844 				(6.0f + tab_index * width + width) - fTabOffset, fTabHeight);
845 			/*float width = 0.0f;
846 
847 			for (int32 i = 0; i < CountTabs(); i++) {
848 				float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f;
849 
850 				if (tabWidth > width)
851 					width = tabWidth;
852 			}
853 
854 			return BRect(X_OFFSET + tab_index * width, 0.0f,
855 				X_OFFSET + tab_index * width + width, fTabHeight);*/
856 		}
857 
858 		case B_WIDTH_AS_USUAL:
859 		default:
860 			return BRect((6.0f + tab_index * 100.0f) - fTabOffset, 0.0f,
861 						(6.0f + tab_index * 100.0f + 100.0f) - fTabOffset, fTabHeight);
862 			/*return BRect(X_OFFSET + tab_index * 100.0f, 0.0f,
863 				X_OFFSET + tab_index * 100.0f + 100.0f, fTabHeight);*/
864 	}
865 }
866 
867 
868 void
869 BTabView::SetFlags(uint32 flags)
870 {
871 	BView::SetFlags(flags);
872 }
873 
874 
875 void
876 BTabView::SetResizingMode(uint32 mode)
877 {
878 	BView::SetResizingMode(mode);
879 }
880 
881 
882 void
883 BTabView::GetPreferredSize(float *width, float *height)
884 {
885 	BView::GetPreferredSize(width, height);
886 }
887 
888 
889 void
890 BTabView::ResizeToPreferred()
891 {
892 	BView::ResizeToPreferred();
893 }
894 
895 
896 BHandler *
897 BTabView::ResolveSpecifier(BMessage *message, int32 index,
898 	BMessage *specifier, int32 what, const char *property)
899 {
900 	BPropertyInfo propInfo(sPropertyList);
901 
902 	if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
903 		return this;
904 
905 	return BView::ResolveSpecifier(message, index, specifier, what,
906 		property);
907 }
908 
909 
910 status_t
911 BTabView::GetSupportedSuites(BMessage *message)
912 {
913 	message->AddString("suites", "suite/vnd.Be-tab-view");
914 
915 	BPropertyInfo propInfo(sPropertyList);
916 	message->AddFlat("messages", &propInfo);
917 
918 	return BView::GetSupportedSuites(message);
919 }
920 
921 
922 void
923 BTabView::AddTab(BView *target, BTab *tab)
924 {
925 	if (tab == NULL)
926 		tab = new BTab(target);
927 	else
928 		tab->SetView(target);
929 
930 	fTabList->AddItem(tab);
931 }
932 
933 
934 BTab *
935 BTabView::RemoveTab(int32 index)
936 {
937 	if (index < 0 || index >= CountTabs())
938 		return NULL;
939 
940 	BTab *tab = (BTab *)fTabList->RemoveItem(index);
941 	if (tab == NULL)
942 		return NULL;
943 
944 	tab->Deselect();
945 
946 	if (index <= fSelection && fSelection != 0)
947 		fSelection--;
948 
949 	if(CountTabs() == 0)
950 		fFocus = -1;
951 	else
952 		Select(fSelection);
953 
954 	if (fFocus == CountTabs() - 1 || CountTabs() == 0)
955 		SetFocusTab(fFocus, false);
956 	else
957 		SetFocusTab(fFocus, true);
958 
959 	return tab;
960 }
961 
962 
963 BTab *
964 BTabView::TabAt(int32 index) const
965 {
966 	return (BTab *)fTabList->ItemAt(index);
967 }
968 
969 
970 void
971 BTabView::SetTabWidth(button_width width)
972 {
973 	fTabWidthSetting = width;
974 
975 	Invalidate();
976 }
977 
978 
979 button_width
980 BTabView::TabWidth() const
981 {
982 	return fTabWidthSetting;
983 }
984 
985 
986 void
987 BTabView::SetTabHeight(float height)
988 {
989 	if (fTabHeight == height)
990 		return;
991 
992 	fContainerView->MoveBy(0.0f, height - fTabHeight);
993 	fContainerView->ResizeBy(0.0f, height - fTabHeight);
994 
995 	fTabHeight = height;
996 
997 	Invalidate();
998 }
999 
1000 
1001 float
1002 BTabView::TabHeight() const
1003 {
1004 	return fTabHeight;
1005 }
1006 
1007 
1008 BView *
1009 BTabView::ContainerView() const
1010 {
1011 	return fContainerView;
1012 }
1013 
1014 
1015 int32
1016 BTabView::CountTabs() const
1017 {
1018 	return fTabList->CountItems();
1019 }
1020 
1021 
1022 BView *
1023 BTabView::ViewForTab(int32 tabIndex) const
1024 {
1025 	BTab *tab = TabAt(tabIndex);
1026 	if (tab)
1027 		return tab->View();
1028 
1029 	return NULL;
1030 }
1031 
1032 
1033 void
1034 BTabView::_InitObject()
1035 {
1036 	fTabList = new BList;
1037 
1038 	fTabWidthSetting = B_WIDTH_AS_USUAL;
1039 	fSelection = 0;
1040 	fFocus = -1;
1041 	fTabOffset = 0.0f;
1042 
1043 	rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR);
1044 
1045 	SetViewColor(color);
1046 	SetLowColor(color);
1047 
1048 	font_height fh;
1049 	GetFontHeight(&fh);
1050 	fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f;
1051 
1052 	BRect bounds = Bounds();
1053 
1054 	bounds.top += TabHeight();
1055 	bounds.InsetBy(3.0f, 3.0f);
1056 
1057 	fContainerView = new BView(bounds, "view container", B_FOLLOW_ALL,
1058 		B_WILL_DRAW);
1059 
1060 	fContainerView->SetViewColor(color);
1061 	fContainerView->SetLowColor(color);
1062 
1063 	AddChild(fContainerView);
1064 }
1065 
1066 
1067 void BTabView::_ReservedTabView1() {}
1068 void BTabView::_ReservedTabView2() {}
1069 void BTabView::_ReservedTabView3() {}
1070 void BTabView::_ReservedTabView4() {}
1071 void BTabView::_ReservedTabView5() {}
1072 void BTabView::_ReservedTabView6() {}
1073 void BTabView::_ReservedTabView7() {}
1074 void BTabView::_ReservedTabView8() {}
1075 void BTabView::_ReservedTabView9() {}
1076 void BTabView::_ReservedTabView10() {}
1077 void BTabView::_ReservedTabView11() {}
1078 void BTabView::_ReservedTabView12() {}
1079 
1080 
1081 BTabView::BTabView(const BTabView &tabView)
1082 	: BView(tabView)
1083 {
1084 	// this is private and not functional, but exported
1085 }
1086 
1087 
1088 BTabView &BTabView::operator=(const BTabView &)
1089 {
1090 	// this is private and not functional, but exported
1091 	return *this;
1092 }
1093