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