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