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 #include <string.h> 19 20 #include <CardLayout.h> 21 #include <ControlLook.h> 22 #include <GroupLayout.h> 23 #include <LayoutUtils.h> 24 #include <List.h> 25 #include <Message.h> 26 #include <PropertyInfo.h> 27 #include <Rect.h> 28 #include <Region.h> 29 #include <String.h> 30 31 #include <binary_compatibility/Support.h> 32 33 34 using std::nothrow; 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 {} 47 }; 48 49 50 51 BTab::BTab(BView* contentsView) 52 : 53 fEnabled(true), 54 fSelected(false), 55 fFocus(false), 56 fView(contentsView), 57 fTabView(NULL) 58 { 59 } 60 61 62 BTab::BTab(BMessage* archive) 63 : 64 BArchivable(archive), 65 fSelected(false), 66 fFocus(false), 67 fView(NULL), 68 fTabView(NULL) 69 { 70 bool disable; 71 72 if (archive->FindBool("_disable", &disable) != B_OK) 73 SetEnabled(true); 74 else 75 SetEnabled(!disable); 76 } 77 78 79 BTab::~BTab() 80 { 81 if (fView == NULL) 82 return; 83 84 if (fSelected) 85 fView->RemoveSelf(); 86 87 delete fView; 88 } 89 90 91 BArchivable* 92 BTab::Instantiate(BMessage* archive) 93 { 94 if (validate_instantiation(archive, "BTab")) 95 return new BTab(archive); 96 97 return NULL; 98 } 99 100 101 status_t 102 BTab::Archive(BMessage* data, bool deep) const 103 { 104 status_t result = BArchivable::Archive(data, deep); 105 if (result != B_OK) 106 return result; 107 108 if (!fEnabled) 109 result = data->AddBool("_disable", false); 110 111 return result; 112 } 113 114 115 status_t 116 BTab::Perform(uint32 d, void* arg) 117 { 118 return BArchivable::Perform(d, arg); 119 } 120 121 122 const char* 123 BTab::Label() const 124 { 125 if (fView != NULL) 126 return fView->Name(); 127 else 128 return NULL; 129 } 130 131 132 void 133 BTab::SetLabel(const char* label) 134 { 135 if (label == NULL || fView == NULL) 136 return; 137 138 fView->SetName(label); 139 140 if (fTabView != NULL) 141 fTabView->Invalidate(); 142 } 143 144 145 bool 146 BTab::IsSelected() const 147 { 148 return fSelected; 149 } 150 151 152 void 153 BTab::Select(BView* owner) 154 { 155 fSelected = true; 156 157 if (owner == NULL || fView == NULL) 158 return; 159 160 // NOTE: Views are not added/removed, if there is layout, 161 // they are made visible/invisible in that case. 162 if (owner->GetLayout() == NULL && fView->Parent() == NULL) 163 owner->AddChild(fView); 164 } 165 166 167 void 168 BTab::Deselect() 169 { 170 if (fView != NULL) { 171 // NOTE: Views are not added/removed, if there is layout, 172 // they are made visible/invisible in that case. 173 bool removeView = false; 174 BView* container = fView->Parent(); 175 if (container != NULL) 176 removeView = 177 dynamic_cast<BCardLayout*>(container->GetLayout()) == NULL; 178 if (removeView) 179 fView->RemoveSelf(); 180 } 181 182 fSelected = false; 183 } 184 185 186 void 187 BTab::SetEnabled(bool enable) 188 { 189 fEnabled = enable; 190 } 191 192 193 bool 194 BTab::IsEnabled() const 195 { 196 return fEnabled; 197 } 198 199 200 void 201 BTab::MakeFocus(bool focus) 202 { 203 fFocus = focus; 204 } 205 206 207 bool 208 BTab::IsFocus() const 209 { 210 return fFocus; 211 } 212 213 214 void 215 BTab::SetView(BView* view) 216 { 217 if (view == NULL || fView == view) 218 return; 219 220 if (fView != NULL) { 221 fView->RemoveSelf(); 222 delete fView; 223 } 224 fView = view; 225 226 if (fTabView != NULL && fSelected) { 227 Select(fTabView->ContainerView()); 228 fTabView->Invalidate(); 229 } 230 } 231 232 233 BView* 234 BTab::View() const 235 { 236 return fView; 237 } 238 239 240 void 241 BTab::DrawFocusMark(BView* owner, BRect frame) 242 { 243 float width = owner->StringWidth(Label()); 244 245 owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 246 247 float offset = IsSelected() ? 3 : 2; 248 owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0, 249 frame.bottom - offset), 250 BPoint((frame.left + frame.right + width) / 2.0, 251 frame.bottom - offset)); 252 } 253 254 255 void 256 BTab::DrawLabel(BView* owner, BRect frame) 257 { 258 be_control_look->DrawLabel(owner, Label(), frame, frame, 259 ui_color(B_PANEL_BACKGROUND_COLOR), 260 IsEnabled() ? 0 : BPrivate::BControlLook::B_DISABLED, 261 BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); 262 } 263 264 265 void 266 BTab::DrawTab(BView* owner, BRect frame, tab_position position, bool full) 267 { 268 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 269 uint32 borders = BControlLook::B_TOP_BORDER 270 | BControlLook::B_BOTTOM_BORDER; 271 272 if (frame.left == owner->Bounds().left) 273 borders |= BControlLook::B_LEFT_BORDER; 274 275 if (frame.right == owner->Bounds().right) 276 borders |= BControlLook::B_RIGHT_BORDER; 277 278 if (position == B_TAB_FRONT) { 279 frame.bottom += 1; 280 be_control_look->DrawActiveTab(owner, frame, frame, no_tint, 0, 281 borders); 282 } else { 283 be_control_look->DrawInactiveTab(owner, frame, frame, no_tint, 0, 284 borders); 285 } 286 287 DrawLabel(owner, frame); 288 } 289 290 291 // #pragma mark - FBC padding and private methods 292 293 294 void BTab::_ReservedTab1() {} 295 void BTab::_ReservedTab2() {} 296 void BTab::_ReservedTab3() {} 297 void BTab::_ReservedTab4() {} 298 void BTab::_ReservedTab5() {} 299 void BTab::_ReservedTab6() {} 300 void BTab::_ReservedTab7() {} 301 void BTab::_ReservedTab8() {} 302 void BTab::_ReservedTab9() {} 303 void BTab::_ReservedTab10() {} 304 void BTab::_ReservedTab11() {} 305 void BTab::_ReservedTab12() {} 306 307 BTab &BTab::operator=(const BTab &) 308 { 309 // this is private and not functional, but exported 310 return *this; 311 } 312 313 314 // #pragma mark - BTabView 315 316 317 BTabView::BTabView(const char* name, button_width width, uint32 flags) 318 : 319 BView(name, flags) 320 { 321 _InitObject(true, width); 322 } 323 324 325 BTabView::BTabView(BRect frame, const char* name, button_width width, 326 uint32 resizeMask, uint32 flags) 327 : 328 BView(frame, name, resizeMask, flags) 329 { 330 _InitObject(false, width); 331 } 332 333 334 BTabView::~BTabView() 335 { 336 for (int32 i = 0; i < CountTabs(); i++) 337 delete TabAt(i); 338 339 delete fTabList; 340 } 341 342 343 BTabView::BTabView(BMessage* archive) 344 : 345 BView(BUnarchiver::PrepareArchive(archive)), 346 fTabList(new BList), 347 fContainerView(NULL), 348 fFocus(-1) 349 { 350 BUnarchiver unarchiver(archive); 351 352 int16 width; 353 if (archive->FindInt16("_but_width", &width) == B_OK) 354 fTabWidthSetting = (button_width)width; 355 else 356 fTabWidthSetting = B_WIDTH_AS_USUAL; 357 358 if (archive->FindFloat("_high", &fTabHeight) != B_OK) { 359 font_height fh; 360 GetFontHeight(&fh); 361 fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f; 362 } 363 364 if (archive->FindInt32("_sel", &fSelection) != B_OK) 365 fSelection = -1; 366 367 if (archive->FindInt32("_border_style", (int32*)&fBorderStyle) != B_OK) 368 fBorderStyle = B_FANCY_BORDER; 369 370 int32 i = 0; 371 BMessage tabMsg; 372 373 if (BUnarchiver::IsArchiveManaged(archive)) { 374 int32 tabCount; 375 archive->GetInfo("_l_items", NULL, &tabCount); 376 for (int32 i = 0; i < tabCount; i++) { 377 unarchiver.EnsureUnarchived("_l_items", i); 378 unarchiver.EnsureUnarchived("_view_list", i); 379 } 380 return; 381 } 382 383 fContainerView = ChildAt(0); 384 _InitContainerView(Flags() & B_SUPPORTS_LAYOUT); 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 BArchiver archiver(archive); 420 421 status_t result = BView::Archive(archive, deep); 422 423 if (result == B_OK) 424 result = archive->AddInt16("_but_width", fTabWidthSetting); 425 if (result == B_OK) 426 result = archive->AddFloat("_high", fTabHeight); 427 if (result == B_OK) 428 result = archive->AddInt32("_sel", fSelection); 429 if (result == B_OK && fBorderStyle != B_FANCY_BORDER) 430 result = archive->AddInt32("_border_style", fBorderStyle); 431 432 if (result == B_OK && deep) { 433 for (int32 i = 0; i < CountTabs(); i++) { 434 BTab* tab = TabAt(i); 435 436 if ((result = archiver.AddArchivable("_l_items", tab, deep)) != B_OK) 437 break; 438 result = archiver.AddArchivable("_view_list", tab->View(), deep); 439 } 440 } 441 442 return archiver.Finish(result); 443 } 444 445 446 status_t 447 BTabView::AllUnarchived(const BMessage* archive) 448 { 449 status_t err = BView::AllUnarchived(archive); 450 if (err != B_OK) 451 return err; 452 453 fContainerView = ChildAt(0); 454 _InitContainerView(Flags() & B_SUPPORTS_LAYOUT); 455 456 BUnarchiver unarchiver(archive); 457 458 int32 tabCount; 459 archive->GetInfo("_l_items", NULL, &tabCount); 460 for (int32 i = 0; i < tabCount && err == B_OK; i++) { 461 BTab* tab; 462 err = unarchiver.FindObject("_l_items", i, tab); 463 if (err == B_OK && tab) { 464 BView* view; 465 if ((err = unarchiver.FindObject("_view_list", i, 466 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, view)) != B_OK) 467 break; 468 469 tab->SetView(view); 470 fTabList->AddItem(tab); 471 } 472 } 473 474 if (err == B_OK) 475 Select(fSelection); 476 477 return err; 478 } 479 480 481 status_t 482 BTabView::Perform(perform_code code, void* _data) 483 { 484 switch (code) { 485 case PERFORM_CODE_ALL_UNARCHIVED: 486 { 487 perform_data_all_unarchived* data 488 = (perform_data_all_unarchived*)_data; 489 490 data->return_value = BTabView::AllUnarchived(data->archive); 491 return B_OK; 492 } 493 } 494 495 return BView::Perform(code, _data); 496 } 497 498 499 void 500 BTabView::AttachedToWindow() 501 { 502 BView::AttachedToWindow(); 503 504 if (fSelection < 0 && CountTabs() > 0) 505 Select(0); 506 } 507 508 509 void 510 BTabView::DetachedFromWindow() 511 { 512 BView::DetachedFromWindow(); 513 } 514 515 516 void 517 BTabView::AllAttached() 518 { 519 BView::AllAttached(); 520 } 521 522 523 void 524 BTabView::AllDetached() 525 { 526 BView::AllDetached(); 527 } 528 529 530 // #pragma mark - 531 532 533 void 534 BTabView::MessageReceived(BMessage* message) 535 { 536 switch (message->what) { 537 case B_GET_PROPERTY: 538 case B_SET_PROPERTY: 539 { 540 BMessage reply(B_REPLY); 541 bool handled = false; 542 543 BMessage specifier; 544 int32 index; 545 int32 form; 546 const char* property; 547 if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) { 548 if (strcmp(property, "Selection") == 0) { 549 if (message->what == B_GET_PROPERTY) { 550 reply.AddInt32("result", fSelection); 551 handled = true; 552 } else { 553 // B_GET_PROPERTY 554 int32 selection; 555 if (message->FindInt32("data", &selection) == B_OK) { 556 Select(selection); 557 reply.AddInt32("error", B_OK); 558 handled = true; 559 } 560 } 561 } 562 } 563 564 if (handled) 565 message->SendReply(&reply); 566 else 567 BView::MessageReceived(message); 568 break; 569 } 570 571 #if 0 572 case B_MOUSE_WHEEL_CHANGED: 573 { 574 float deltaX = 0.0f; 575 float deltaY = 0.0f; 576 message->FindFloat("be:wheel_delta_x", &deltaX); 577 message->FindFloat("be:wheel_delta_y", &deltaY); 578 579 if (deltaX == 0.0f && deltaY == 0.0f) 580 return; 581 582 if (deltaY == 0.0f) 583 deltaY = deltaX; 584 585 int32 selection = Selection(); 586 int32 numTabs = CountTabs(); 587 if (deltaY > 0 && selection < numTabs - 1) { 588 // move to the right tab. 589 Select(Selection() + 1); 590 } else if (deltaY < 0 && selection > 0 && numTabs > 1) { 591 // move to the left tab. 592 Select(selection - 1); 593 } 594 break; 595 } 596 #endif 597 598 default: 599 BView::MessageReceived(message); 600 break; 601 } 602 } 603 604 605 void 606 BTabView::KeyDown(const char* bytes, int32 numBytes) 607 { 608 if (IsHidden()) 609 return; 610 611 switch (bytes[0]) { 612 case B_DOWN_ARROW: 613 case B_LEFT_ARROW: { 614 int32 focus = fFocus - 1; 615 if (focus < 0) 616 focus = CountTabs() - 1; 617 SetFocusTab(focus, true); 618 break; 619 } 620 621 case B_UP_ARROW: 622 case B_RIGHT_ARROW: { 623 int32 focus = fFocus + 1; 624 if (focus >= CountTabs()) 625 focus = 0; 626 SetFocusTab(focus, true); 627 break; 628 } 629 630 case B_RETURN: 631 case B_SPACE: 632 Select(FocusTab()); 633 break; 634 635 default: 636 BView::KeyDown(bytes, numBytes); 637 } 638 } 639 640 641 void 642 BTabView::MouseDown(BPoint where) 643 { 644 if (where.y > fTabHeight) 645 return; 646 647 for (int32 i = 0; i < CountTabs(); i++) { 648 if (TabFrame(i).Contains(where) 649 && i != Selection()) { 650 Select(i); 651 return; 652 } 653 } 654 655 BView::MouseDown(where); 656 } 657 658 659 void 660 BTabView::MouseUp(BPoint where) 661 { 662 BView::MouseUp(where); 663 } 664 665 666 void 667 BTabView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 668 { 669 BView::MouseMoved(where, transit, dragMessage); 670 } 671 672 673 void 674 BTabView::Pulse() 675 { 676 BView::Pulse(); 677 } 678 679 680 void 681 BTabView::Select(int32 index) 682 { 683 if (index == Selection()) 684 return; 685 686 if (index < 0 || index >= CountTabs()) 687 index = Selection(); 688 689 BTab* tab = TabAt(Selection()); 690 691 if (tab) 692 tab->Deselect(); 693 694 tab = TabAt(index); 695 if (tab != NULL && fContainerView != NULL) { 696 if (index == 0) 697 fTabOffset = 0.0f; 698 tab->Select(fContainerView); 699 fSelection = index; 700 701 // make the view visible through the layout if there is one 702 BCardLayout* layout 703 = dynamic_cast<BCardLayout*>(fContainerView->GetLayout()); 704 if (layout != NULL) 705 layout->SetVisibleItem(index); 706 } 707 708 Invalidate(); 709 710 if (index != 0 && !Bounds().Contains(TabFrame(index))){ 711 if (!Bounds().Contains(TabFrame(index).LeftTop())) 712 fTabOffset += TabFrame(index).left - Bounds().left - 20.0f; 713 else 714 fTabOffset += TabFrame(index).right - Bounds().right + 20.0f; 715 716 Invalidate(); 717 } 718 719 SetFocusTab(index, true); 720 } 721 722 723 int32 724 BTabView::Selection() const 725 { 726 return fSelection; 727 } 728 729 730 void 731 BTabView::WindowActivated(bool active) 732 { 733 BView::WindowActivated(active); 734 735 if (IsFocus()) 736 Invalidate(); 737 } 738 739 740 void 741 BTabView::MakeFocus(bool focus) 742 { 743 BView::MakeFocus(focus); 744 745 SetFocusTab(Selection(), focus); 746 } 747 748 749 void 750 BTabView::SetFocusTab(int32 tab, bool focus) 751 { 752 if (tab >= CountTabs()) 753 tab = 0; 754 755 if (tab < 0) 756 tab = CountTabs() - 1; 757 758 if (focus) { 759 if (tab == fFocus) 760 return; 761 762 if (fFocus != -1){ 763 if (TabAt (fFocus) != NULL) 764 TabAt(fFocus)->MakeFocus(false); 765 Invalidate(TabFrame(fFocus)); 766 } 767 if (TabAt(tab) != NULL){ 768 TabAt(tab)->MakeFocus(true); 769 Invalidate(TabFrame(tab)); 770 fFocus = tab; 771 } 772 } else if (fFocus != -1) { 773 TabAt(fFocus)->MakeFocus(false); 774 Invalidate(TabFrame(fFocus)); 775 fFocus = -1; 776 } 777 } 778 779 780 int32 781 BTabView::FocusTab() const 782 { 783 return fFocus; 784 } 785 786 787 void 788 BTabView::Draw(BRect updateRect) 789 { 790 DrawBox(TabFrame(fSelection)); 791 DrawTabs(); 792 793 if (IsFocus() && fFocus != -1) 794 TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus)); 795 } 796 797 798 BRect 799 BTabView::DrawTabs() 800 { 801 float left = 0; 802 803 for (int32 i = 0; i < CountTabs(); i++) { 804 BRect tabFrame = TabFrame(i); 805 TabAt(i)->DrawTab(this, tabFrame, 806 i == fSelection ? B_TAB_FRONT : (i == 0) ? B_TAB_FIRST : B_TAB_ANY, 807 i + 1 != fSelection); 808 left = tabFrame.right; 809 } 810 811 BRect frame(Bounds()); 812 if (fBorderStyle == B_PLAIN_BORDER) 813 frame.right += 1; 814 else if (fBorderStyle == B_NO_BORDER) 815 frame.right += 2; 816 if (left < frame.right) { 817 frame.left = left; 818 frame.bottom = fTabHeight; 819 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 820 uint32 borders = BControlLook::B_TOP_BORDER 821 | BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER; 822 if (left == 0) 823 borders |= BControlLook::B_LEFT_BORDER; 824 be_control_look->DrawInactiveTab(this, frame, frame, base, 0, 825 borders); 826 } 827 if (fBorderStyle == B_NO_BORDER) { 828 // Draw a small inactive area before first tab. 829 frame = Bounds(); 830 frame.right = 0.0f; 831 // one pixel wide 832 frame.bottom = fTabHeight; 833 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 834 uint32 borders = BControlLook::B_TOP_BORDER 835 | BControlLook::B_BOTTOM_BORDER; 836 be_control_look->DrawInactiveTab(this, frame, frame, base, 0, 837 borders); 838 } 839 840 if (fSelection < CountTabs()) 841 return TabFrame(fSelection); 842 843 return BRect(); 844 } 845 846 847 void 848 BTabView::DrawBox(BRect selTabRect) 849 { 850 BRect rect(Bounds()); 851 rect.top = selTabRect.bottom; 852 if (fBorderStyle != B_FANCY_BORDER) 853 rect.top += 1.0f; 854 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 855 856 if (fBorderStyle == B_FANCY_BORDER) 857 be_control_look->DrawGroupFrame(this, rect, rect, base); 858 else { 859 uint32 borders = BControlLook::B_TOP_BORDER; 860 if (fBorderStyle == B_PLAIN_BORDER) 861 borders = BControlLook::B_ALL_BORDERS; 862 be_control_look->DrawBorder(this, rect, rect, base, B_PLAIN_BORDER, 863 0, borders); 864 } 865 } 866 867 868 BRect 869 BTabView::TabFrame(int32 index) const 870 { 871 if (index >= CountTabs() || index < 0) 872 return BRect(); 873 874 float width = 100.0f; 875 float height = fTabHeight; 876 float borderOffset = 0.0f; 877 // Do not use 2.0f for B_NO_BORDER, that will look yet different 878 // again (handled in DrawTabs()). 879 if (fBorderStyle == B_PLAIN_BORDER) 880 borderOffset = 1.0f; 881 switch (fTabWidthSetting) { 882 case B_WIDTH_FROM_LABEL: 883 { 884 float x = 0.0f; 885 for (int32 i = 0; i < index; i++){ 886 x += StringWidth(TabAt(i)->Label()) + 20.0f; 887 } 888 889 return BRect(x - borderOffset, 0.0f, 890 x + StringWidth(TabAt(index)->Label()) + 20.0f 891 - borderOffset, 892 height); 893 } 894 895 case B_WIDTH_FROM_WIDEST: 896 width = 0.0; 897 for (int32 i = 0; i < CountTabs(); i++) { 898 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f; 899 if (tabWidth > width) 900 width = tabWidth; 901 } 902 // fall through 903 904 case B_WIDTH_AS_USUAL: 905 default: 906 return BRect(index * width - borderOffset, 0.0f, 907 index * width + width - borderOffset, height); 908 } 909 910 // TODO: fix to remove "offset" in DrawTab and DrawLabel ... 911 switch (fTabWidthSetting) { 912 case B_WIDTH_FROM_LABEL: 913 { 914 float x = 6.0f; 915 for (int32 i = 0; i < index; i++){ 916 x += StringWidth(TabAt(i)->Label()) + 20.0f; 917 } 918 919 return BRect(x - fTabOffset, 0.0f, 920 x - fTabOffset + StringWidth(TabAt(index)->Label()) + 20.0f, 921 fTabHeight); 922 } 923 924 case B_WIDTH_FROM_WIDEST: 925 { 926 float width = 0.0f; 927 928 for (int32 i = 0; i < CountTabs(); i++) { 929 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f; 930 if (tabWidth > width) 931 width = tabWidth; 932 } 933 return BRect((6.0f + index * width) - fTabOffset, 0.0f, 934 (6.0f + index * width + width) - fTabOffset, fTabHeight); 935 } 936 937 case B_WIDTH_AS_USUAL: 938 default: 939 return BRect((6.0f + index * 100.0f) - fTabOffset, 0.0f, 940 (6.0f + index * 100.0f + 100.0f) - fTabOffset, fTabHeight); 941 } 942 } 943 944 945 void 946 BTabView::SetFlags(uint32 flags) 947 { 948 BView::SetFlags(flags); 949 } 950 951 952 void 953 BTabView::SetResizingMode(uint32 mode) 954 { 955 BView::SetResizingMode(mode); 956 } 957 958 959 // #pragma mark - 960 961 962 void 963 BTabView::ResizeToPreferred() 964 { 965 BView::ResizeToPreferred(); 966 } 967 968 969 void 970 BTabView::GetPreferredSize(float* _width, float* _height) 971 { 972 BView::GetPreferredSize(_width, _height); 973 } 974 975 976 BSize 977 BTabView::MinSize() 978 { 979 BSize size; 980 if (GetLayout()) 981 size = GetLayout()->MinSize(); 982 else { 983 size = _TabsMinSize(); 984 BSize containerSize = fContainerView->MinSize(); 985 containerSize.width += 2 * _BorderWidth(); 986 containerSize.height += 2 * _BorderWidth(); 987 if (containerSize.width > size.width) 988 size.width = containerSize.width; 989 size.height += containerSize.height; 990 } 991 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 992 } 993 994 995 BSize 996 BTabView::MaxSize() 997 { 998 BSize size; 999 if (GetLayout()) 1000 size = GetLayout()->MaxSize(); 1001 else { 1002 size = _TabsMinSize(); 1003 BSize containerSize = fContainerView->MaxSize(); 1004 containerSize.width += 2 * _BorderWidth(); 1005 containerSize.height += 2 * _BorderWidth(); 1006 if (containerSize.width > size.width) 1007 size.width = containerSize.width; 1008 size.height += containerSize.height; 1009 } 1010 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 1011 } 1012 1013 1014 BSize 1015 BTabView::PreferredSize() 1016 { 1017 BSize size; 1018 if (GetLayout() != NULL) 1019 size = GetLayout()->PreferredSize(); 1020 else { 1021 size = _TabsMinSize(); 1022 BSize containerSize = fContainerView->PreferredSize(); 1023 containerSize.width += 2 * _BorderWidth(); 1024 containerSize.height += 2 * _BorderWidth(); 1025 if (containerSize.width > size.width) 1026 size.width = containerSize.width; 1027 size.height += containerSize.height; 1028 } 1029 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); 1030 } 1031 1032 1033 void 1034 BTabView::FrameMoved(BPoint newPosition) 1035 { 1036 BView::FrameMoved(newPosition); 1037 } 1038 1039 1040 void 1041 BTabView::FrameResized(float newWidth, float newHeight) 1042 { 1043 BView::FrameResized(newWidth, newHeight); 1044 } 1045 1046 1047 // #pragma mark - 1048 1049 1050 BHandler* 1051 BTabView::ResolveSpecifier(BMessage* message, int32 index, 1052 BMessage* specifier, int32 what, const char* property) 1053 { 1054 BPropertyInfo propInfo(sPropertyList); 1055 1056 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK) 1057 return this; 1058 1059 return BView::ResolveSpecifier(message, index, specifier, what, property); 1060 } 1061 1062 1063 status_t 1064 BTabView::GetSupportedSuites(BMessage* message) 1065 { 1066 message->AddString("suites", "suite/vnd.Be-tab-view"); 1067 1068 BPropertyInfo propInfo(sPropertyList); 1069 message->AddFlat("messages", &propInfo); 1070 1071 return BView::GetSupportedSuites(message); 1072 } 1073 1074 1075 // #pragma mark - 1076 1077 1078 void 1079 BTabView::AddTab(BView* target, BTab* tab) 1080 { 1081 if (tab == NULL) 1082 tab = new BTab(target); 1083 else 1084 tab->SetView(target); 1085 1086 if (fContainerView->GetLayout()) 1087 fContainerView->GetLayout()->AddView(CountTabs(), target); 1088 1089 fTabList->AddItem(tab); 1090 BTab::Private(tab).SetTabView(this); 1091 1092 // When we haven't had a any tabs before, but are already attached to the 1093 // window, select this one. 1094 if (CountTabs() == 1 && Window() != NULL) 1095 Select(0); 1096 } 1097 1098 1099 BTab* 1100 BTabView::RemoveTab(int32 index) 1101 { 1102 if (index < 0 || index >= CountTabs()) 1103 return NULL; 1104 1105 BTab* tab = (BTab*)fTabList->RemoveItem(index); 1106 if (tab == NULL) 1107 return NULL; 1108 1109 tab->Deselect(); 1110 BTab::Private(tab).SetTabView(NULL); 1111 1112 if (fContainerView->GetLayout()) 1113 fContainerView->GetLayout()->RemoveItem(index); 1114 1115 if (CountTabs() == 0) 1116 fFocus = -1; 1117 else if (index <= fSelection) 1118 Select(fSelection - 1); 1119 1120 if (fFocus == CountTabs() - 1 || CountTabs() == 0) 1121 SetFocusTab(fFocus, false); 1122 else 1123 SetFocusTab(fFocus, true); 1124 1125 return tab; 1126 } 1127 1128 1129 BTab* 1130 BTabView::TabAt(int32 index) const 1131 { 1132 return (BTab*)fTabList->ItemAt(index); 1133 } 1134 1135 1136 void 1137 BTabView::SetTabWidth(button_width width) 1138 { 1139 fTabWidthSetting = width; 1140 1141 Invalidate(); 1142 } 1143 1144 1145 button_width 1146 BTabView::TabWidth() const 1147 { 1148 return fTabWidthSetting; 1149 } 1150 1151 1152 void 1153 BTabView::SetTabHeight(float height) 1154 { 1155 if (fTabHeight == height) 1156 return; 1157 1158 fTabHeight = height; 1159 _LayoutContainerView(GetLayout() != NULL); 1160 1161 Invalidate(); 1162 } 1163 1164 1165 float 1166 BTabView::TabHeight() const 1167 { 1168 return fTabHeight; 1169 } 1170 1171 1172 void 1173 BTabView::SetBorder(border_style border) 1174 { 1175 if (fBorderStyle == border) 1176 return; 1177 1178 fBorderStyle = border; 1179 1180 _LayoutContainerView((Flags() & B_SUPPORTS_LAYOUT) != 0); 1181 } 1182 1183 1184 border_style 1185 BTabView::Border() const 1186 { 1187 return fBorderStyle; 1188 } 1189 1190 1191 BView* 1192 BTabView::ContainerView() const 1193 { 1194 return fContainerView; 1195 } 1196 1197 1198 int32 1199 BTabView::CountTabs() const 1200 { 1201 return fTabList->CountItems(); 1202 } 1203 1204 1205 BView* 1206 BTabView::ViewForTab(int32 tabIndex) const 1207 { 1208 BTab* tab = TabAt(tabIndex); 1209 if (tab != NULL) 1210 return tab->View(); 1211 1212 return NULL; 1213 } 1214 1215 1216 void 1217 BTabView::_InitObject(bool layouted, button_width width) 1218 { 1219 fTabList = new BList; 1220 1221 fTabWidthSetting = width; 1222 fSelection = -1; 1223 fFocus = -1; 1224 fTabOffset = 0.0f; 1225 fBorderStyle = B_FANCY_BORDER; 1226 1227 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1228 SetLowUIColor(B_PANEL_BACKGROUND_COLOR); 1229 1230 font_height fh; 1231 GetFontHeight(&fh); 1232 fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f; 1233 1234 fContainerView = NULL; 1235 _InitContainerView(layouted); 1236 } 1237 1238 1239 void 1240 BTabView::_InitContainerView(bool layouted) 1241 { 1242 bool needsLayout = false; 1243 bool createdContainer = false; 1244 if (layouted) { 1245 if (GetLayout() == NULL) { 1246 SetLayout(new(nothrow) BGroupLayout(B_HORIZONTAL)); 1247 needsLayout = true; 1248 } 1249 1250 if (fContainerView == NULL) { 1251 fContainerView = new BView("view container", B_WILL_DRAW); 1252 fContainerView->SetLayout(new(std::nothrow) BCardLayout()); 1253 createdContainer = true; 1254 } 1255 } else if (fContainerView == NULL) { 1256 fContainerView = new BView(Bounds(), "view container", B_FOLLOW_ALL, 1257 B_WILL_DRAW); 1258 createdContainer = true; 1259 } 1260 1261 if (needsLayout || createdContainer) 1262 _LayoutContainerView(layouted); 1263 1264 if (createdContainer) { 1265 fContainerView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1266 fContainerView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR); 1267 AddChild(fContainerView); 1268 } 1269 } 1270 1271 1272 BSize 1273 BTabView::_TabsMinSize() const 1274 { 1275 BSize size(0.0f, TabHeight()); 1276 int32 count = min_c(2, CountTabs()); 1277 for (int32 i = 0; i < count; i++) { 1278 BRect frame = TabFrame(i); 1279 size.width += frame.Width(); 1280 } 1281 1282 if (count < CountTabs()) { 1283 // TODO: Add size for yet to be implemented buttons that allow 1284 // "scrolling" the displayed tabs left/right. 1285 } 1286 1287 return size; 1288 } 1289 1290 1291 float 1292 BTabView::_BorderWidth() const 1293 { 1294 switch (fBorderStyle) { 1295 default: 1296 case B_FANCY_BORDER: 1297 return 3.0f; 1298 case B_PLAIN_BORDER: 1299 return 1.0f; 1300 case B_NO_BORDER: 1301 return 0.0f; 1302 } 1303 } 1304 1305 1306 void 1307 BTabView::_LayoutContainerView(bool layouted) 1308 { 1309 float borderWidth = _BorderWidth(); 1310 if (layouted) { 1311 float topBorderOffset; 1312 switch (fBorderStyle) { 1313 default: 1314 case B_FANCY_BORDER: 1315 topBorderOffset = 1.0f; 1316 break; 1317 case B_PLAIN_BORDER: 1318 topBorderOffset = 0.0f; 1319 break; 1320 case B_NO_BORDER: 1321 topBorderOffset = -1.0f; 1322 break; 1323 } 1324 BGroupLayout* layout = dynamic_cast<BGroupLayout*>(GetLayout()); 1325 if (layout != NULL) { 1326 layout->SetInsets(borderWidth, borderWidth + TabHeight() 1327 - topBorderOffset, borderWidth, borderWidth); 1328 } 1329 } else { 1330 BRect bounds = Bounds(); 1331 1332 bounds.top += TabHeight(); 1333 bounds.InsetBy(borderWidth, borderWidth); 1334 1335 fContainerView->MoveTo(bounds.left, bounds.top); 1336 fContainerView->ResizeTo(bounds.Width(), bounds.Height()); 1337 } 1338 } 1339 1340 1341 // #pragma mark - FBC and forbidden 1342 1343 1344 void BTabView::_ReservedTabView2() {} 1345 void BTabView::_ReservedTabView3() {} 1346 void BTabView::_ReservedTabView4() {} 1347 void BTabView::_ReservedTabView5() {} 1348 void BTabView::_ReservedTabView6() {} 1349 void BTabView::_ReservedTabView7() {} 1350 void BTabView::_ReservedTabView8() {} 1351 void BTabView::_ReservedTabView9() {} 1352 void BTabView::_ReservedTabView10() {} 1353 void BTabView::_ReservedTabView11() {} 1354 void BTabView::_ReservedTabView12() {} 1355 1356 1357 BTabView::BTabView(const BTabView& tabView) 1358 : BView(tabView) 1359 { 1360 // this is private and not functional, but exported 1361 } 1362 1363 1364 BTabView& 1365 BTabView::operator=(const BTabView&) 1366 { 1367 // this is private and not functional, but exported 1368 return *this; 1369 } 1370 1371 // #pragma mark - binary compatibility 1372 1373 1374 extern "C" void 1375 B_IF_GCC_2(_ReservedTabView1__8BTabView, _ZN8BTabView17_ReservedTabView1Ev)( 1376 BTabView* tabView, border_style border) 1377 { 1378 tabView->BTabView::SetBorder(border); 1379 } 1380