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