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