1 /* 2 * Copyright (c) 2001-2008, Haiku, Inc. 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 frame.left = left; 855 frame.bottom = fTabHeight; 856 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 857 uint32 borders = BControlLook::B_TOP_BORDER 858 | BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER; 859 if (left == 0) 860 borders |= BControlLook::B_LEFT_BORDER; 861 be_control_look->DrawInactiveTab(this, frame, frame, base, 0, borders); 862 } 863 864 if (fSelection < CountTabs()) 865 return TabFrame(fSelection); 866 867 return BRect(); 868 } 869 870 871 void 872 BTabView::DrawBox(BRect selTabRect) 873 { 874 if (be_control_look != NULL) { 875 BRect rect(Bounds()); 876 rect.top = selTabRect.bottom; 877 878 // BRegion clipping(Bounds()); 879 // selTabRect.left += 2; 880 // selTabRect.right -= 2; 881 // clipping.Exclude(selTabRect); 882 // ConstrainClippingRegion(&clipping); 883 884 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 885 be_control_look->DrawGroupFrame(this, rect, rect, base); 886 887 // ConstrainClippingRegion(NULL); 888 889 return; 890 } 891 892 BRect rect = Bounds(); 893 BRect lastTabRect = TabFrame(CountTabs() - 1); 894 895 rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR); 896 rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT); 897 rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT); 898 rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT); 899 rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT); 900 901 BeginLineArray(12); 902 903 int32 offset = (int32)ceilf(selTabRect.Height() / 2.0); 904 905 // outer lines 906 AddLine(BPoint(rect.left, rect.bottom - 1), 907 BPoint(rect.left, selTabRect.bottom), darken2); 908 if (selTabRect.left >= rect.left + 1) 909 AddLine(BPoint(rect.left + 1, selTabRect.bottom), 910 BPoint(selTabRect.left, selTabRect.bottom), darken2); 911 if (lastTabRect.right + offset + 1 <= rect.right - 1) 912 AddLine(BPoint(lastTabRect.right + offset + 1, selTabRect.bottom), 913 BPoint(rect.right - 1, selTabRect.bottom), darken2); 914 AddLine(BPoint(rect.right, selTabRect.bottom + 2), 915 BPoint(rect.right, rect.bottom), darken2); 916 AddLine(BPoint(rect.right - 1, rect.bottom), 917 BPoint(rect.left + 2, rect.bottom), darken2); 918 919 // inner lines 920 rect.InsetBy(1, 1); 921 selTabRect.bottom += 1; 922 923 AddLine(BPoint(rect.left, rect.bottom - 2), 924 BPoint(rect.left, selTabRect.bottom), lightenMax); 925 if (selTabRect.left >= rect.left + 1) 926 AddLine(BPoint(rect.left + 1, selTabRect.bottom), 927 BPoint(selTabRect.left, selTabRect.bottom), lightenMax); 928 if (selTabRect.right + offset + 1 <= rect.right - 2) 929 AddLine(BPoint(selTabRect.right + offset + 1, selTabRect.bottom), 930 BPoint(rect.right - 2, selTabRect.bottom), lightenMax); 931 AddLine(BPoint(rect.right, selTabRect.bottom), 932 BPoint(rect.right, rect.bottom), darken4); 933 AddLine(BPoint(rect.right - 1, rect.bottom), 934 BPoint(rect.left, rect.bottom), darken4); 935 936 // soft inner bevel at right/bottom 937 rect.right--; 938 rect.bottom--; 939 940 AddLine(BPoint(rect.right, selTabRect.bottom + 1), 941 BPoint(rect.right, rect.bottom), darken1); 942 AddLine(BPoint(rect.right - 1, rect.bottom), 943 BPoint(rect.left + 1, rect.bottom), darken1); 944 945 EndLineArray(); 946 } 947 948 #define X_OFFSET 0.0f 949 950 BRect 951 BTabView::TabFrame(int32 tab_index) const 952 { 953 if (be_control_look != NULL) { 954 float width = 100.0; 955 float height = fTabHeight;; 956 switch (fTabWidthSetting) { 957 case B_WIDTH_FROM_LABEL: 958 { 959 float x = 0.0; 960 for (int32 i = 0; i < tab_index; i++){ 961 x += StringWidth(TabAt(i)->Label()) + 20.0; 962 } 963 964 return BRect(x, 0.0, 965 x + StringWidth(TabAt(tab_index)->Label()) + 20.0, 966 height); 967 } 968 969 case B_WIDTH_FROM_WIDEST: 970 width = 0.0; 971 for (int32 i = 0; i < CountTabs(); i++) { 972 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0; 973 if (tabWidth > width) 974 width = tabWidth; 975 } 976 // fall through 977 978 case B_WIDTH_AS_USUAL: 979 default: 980 return BRect(tab_index * width, 0.0, 981 tab_index * width + width, height); 982 } 983 } 984 985 // TODO: fix to remove "offset" in DrawTab and DrawLabel ... 986 switch (fTabWidthSetting) { 987 case B_WIDTH_FROM_LABEL: 988 { 989 float x = 6.0f; 990 for (int32 i = 0; i < tab_index; i++){ 991 x += StringWidth(TabAt(i)->Label()) + 20.0f; 992 } 993 994 return BRect(x - fTabOffset, 0.0f, 995 x - fTabOffset + StringWidth(TabAt(tab_index)->Label()) + 20.0f , fTabHeight); 996 997 998 /*float x = X_OFFSET; 999 for (int32 i = 0; i < tab_index; i++) 1000 x += StringWidth(TabAt(i)->Label()) + 20.0f; 1001 1002 return BRect(x, 0.0f, 1003 x + StringWidth(TabAt(tab_index)->Label()) + 20.0f, fTabHeight);*/ 1004 } 1005 1006 case B_WIDTH_FROM_WIDEST: 1007 { 1008 float width = 0.0f; 1009 1010 for (int32 i = 0; i < CountTabs(); i++) { 1011 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f; 1012 if (tabWidth > width) 1013 width = tabWidth; 1014 } 1015 return BRect((6.0f + tab_index * width) - fTabOffset, 0.0f, 1016 (6.0f + tab_index * width + width) - fTabOffset, fTabHeight); 1017 /*float width = 0.0f; 1018 1019 for (int32 i = 0; i < CountTabs(); i++) { 1020 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f; 1021 1022 if (tabWidth > width) 1023 width = tabWidth; 1024 } 1025 1026 return BRect(X_OFFSET + tab_index * width, 0.0f, 1027 X_OFFSET + tab_index * width + width, fTabHeight);*/ 1028 } 1029 1030 case B_WIDTH_AS_USUAL: 1031 default: 1032 return BRect((6.0f + tab_index * 100.0f) - fTabOffset, 0.0f, 1033 (6.0f + tab_index * 100.0f + 100.0f) - fTabOffset, fTabHeight); 1034 /*return BRect(X_OFFSET + tab_index * 100.0f, 0.0f, 1035 X_OFFSET + tab_index * 100.0f + 100.0f, fTabHeight);*/ 1036 } 1037 } 1038 1039 1040 void 1041 BTabView::SetFlags(uint32 flags) 1042 { 1043 BView::SetFlags(flags); 1044 } 1045 1046 1047 void 1048 BTabView::SetResizingMode(uint32 mode) 1049 { 1050 BView::SetResizingMode(mode); 1051 } 1052 1053 1054 void 1055 BTabView::GetPreferredSize(float *width, float *height) 1056 { 1057 BView::GetPreferredSize(width, height); 1058 } 1059 1060 1061 BSize 1062 BTabView::MinSize() 1063 { 1064 BSize size = fContainerView->MinSize(); 1065 size.height += TabHeight() + 6.0f; 1066 size.width += 6.0f; 1067 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 1068 } 1069 1070 1071 BSize 1072 BTabView::MaxSize() 1073 { 1074 BSize size = fContainerView->MaxSize(); 1075 size.height += TabHeight() + 6.0f; 1076 size.width += 6.0f; 1077 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 1078 } 1079 1080 1081 BSize 1082 BTabView::PreferredSize() 1083 { 1084 BSize size = fContainerView->PreferredSize(); 1085 size.height += TabHeight() + 6.0f; 1086 size.width += 6.0f; 1087 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); 1088 } 1089 1090 1091 void 1092 BTabView::ResizeToPreferred() 1093 { 1094 BView::ResizeToPreferred(); 1095 } 1096 1097 1098 BHandler * 1099 BTabView::ResolveSpecifier(BMessage *message, int32 index, 1100 BMessage *specifier, int32 what, const char *property) 1101 { 1102 BPropertyInfo propInfo(sPropertyList); 1103 1104 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK) 1105 return this; 1106 1107 return BView::ResolveSpecifier(message, index, specifier, what, 1108 property); 1109 } 1110 1111 1112 status_t 1113 BTabView::GetSupportedSuites(BMessage *message) 1114 { 1115 message->AddString("suites", "suite/vnd.Be-tab-view"); 1116 1117 BPropertyInfo propInfo(sPropertyList); 1118 message->AddFlat("messages", &propInfo); 1119 1120 return BView::GetSupportedSuites(message); 1121 } 1122 1123 1124 void 1125 BTabView::AddTab(BView *target, BTab *tab) 1126 { 1127 if (tab == NULL) 1128 tab = new BTab(target); 1129 else 1130 tab->SetView(target); 1131 1132 if (fContainerView->GetLayout()) 1133 fContainerView->GetLayout()->AddView(CountTabs(), target); 1134 1135 fTabList->AddItem(tab); 1136 } 1137 1138 1139 BTab * 1140 BTabView::RemoveTab(int32 index) 1141 { 1142 if (index < 0 || index >= CountTabs()) 1143 return NULL; 1144 1145 BTab *tab = (BTab *)fTabList->RemoveItem(index); 1146 if (tab == NULL) 1147 return NULL; 1148 1149 tab->Deselect(); 1150 1151 if (index <= fSelection && fSelection != 0) 1152 fSelection--; 1153 1154 if (CountTabs() == 0) 1155 fFocus = -1; 1156 else 1157 Select(fSelection); 1158 1159 if (fFocus == CountTabs() - 1 || CountTabs() == 0) 1160 SetFocusTab(fFocus, false); 1161 else 1162 SetFocusTab(fFocus, true); 1163 1164 if (fContainerView->GetLayout()) 1165 fContainerView->GetLayout()->RemoveItem(index); 1166 1167 return tab; 1168 } 1169 1170 1171 BTab * 1172 BTabView::TabAt(int32 index) const 1173 { 1174 return (BTab *)fTabList->ItemAt(index); 1175 } 1176 1177 1178 void 1179 BTabView::SetTabWidth(button_width width) 1180 { 1181 fTabWidthSetting = width; 1182 1183 Invalidate(); 1184 } 1185 1186 1187 button_width 1188 BTabView::TabWidth() const 1189 { 1190 return fTabWidthSetting; 1191 } 1192 1193 1194 void 1195 BTabView::SetTabHeight(float height) 1196 { 1197 if (fTabHeight == height) 1198 return; 1199 1200 fContainerView->MoveBy(0.0f, height - fTabHeight); 1201 fContainerView->ResizeBy(0.0f, height - fTabHeight); 1202 1203 fTabHeight = height; 1204 1205 Invalidate(); 1206 } 1207 1208 1209 float 1210 BTabView::TabHeight() const 1211 { 1212 return fTabHeight; 1213 } 1214 1215 1216 BView * 1217 BTabView::ContainerView() const 1218 { 1219 return fContainerView; 1220 } 1221 1222 1223 int32 1224 BTabView::CountTabs() const 1225 { 1226 return fTabList->CountItems(); 1227 } 1228 1229 1230 BView * 1231 BTabView::ViewForTab(int32 tabIndex) const 1232 { 1233 BTab *tab = TabAt(tabIndex); 1234 if (tab) 1235 return tab->View(); 1236 1237 return NULL; 1238 } 1239 1240 1241 void 1242 BTabView::_InitObject(bool layouted, button_width width) 1243 { 1244 if (!be_control_look) 1245 SetFont(be_bold_font); 1246 1247 fTabList = new BList; 1248 1249 fTabWidthSetting = width; 1250 fSelection = 0; 1251 fFocus = -1; 1252 fTabOffset = 0.0f; 1253 1254 rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR); 1255 1256 SetViewColor(color); 1257 SetLowColor(color); 1258 1259 font_height fh; 1260 GetFontHeight(&fh); 1261 fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f; 1262 1263 if (layouted) { 1264 BGroupLayout* layout = new(std::nothrow) BGroupLayout(B_HORIZONTAL); 1265 if (layout) { 1266 layout->SetInsets(3.0, 3.0 + TabHeight() - 1, 3.0, 3.0); 1267 SetLayout(layout); 1268 } 1269 1270 fContainerView = new BView("view container", B_WILL_DRAW); 1271 fContainerView->SetLayout(new(std::nothrow) BCardLayout()); 1272 } else { 1273 BRect bounds = Bounds(); 1274 1275 bounds.top += TabHeight(); 1276 bounds.InsetBy(3.0f, 3.0f); 1277 1278 fContainerView = new BView(bounds, "view container", B_FOLLOW_ALL, 1279 B_WILL_DRAW); 1280 } 1281 1282 fContainerView->SetViewColor(color); 1283 fContainerView->SetLowColor(color); 1284 1285 AddChild(fContainerView); 1286 } 1287 1288 1289 void BTabView::_ReservedTabView1() {} 1290 void BTabView::_ReservedTabView2() {} 1291 void BTabView::_ReservedTabView3() {} 1292 void BTabView::_ReservedTabView4() {} 1293 void BTabView::_ReservedTabView5() {} 1294 void BTabView::_ReservedTabView6() {} 1295 void BTabView::_ReservedTabView7() {} 1296 void BTabView::_ReservedTabView8() {} 1297 void BTabView::_ReservedTabView9() {} 1298 void BTabView::_ReservedTabView10() {} 1299 void BTabView::_ReservedTabView11() {} 1300 void BTabView::_ReservedTabView12() {} 1301 1302 1303 BTabView::BTabView(const BTabView &tabView) 1304 : BView(tabView) 1305 { 1306 // this is private and not functional, but exported 1307 } 1308 1309 1310 BTabView &BTabView::operator=(const BTabView &) 1311 { 1312 // this is private and not functional, but exported 1313 return *this; 1314 } 1315