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