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