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