1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2005, Haiku 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: TabView.cpp 23 // Author: Marc Flerackers (mflerackers@androme.be) 24 // Description: BTab creates individual "tabs" that can be assigned 25 // to specific views. 26 // BTabView provides the framework for containing and 27 // managing groups of BTab objects. 28 //------------------------------------------------------------------------------ 29 30 #include <TabView.h> 31 #include <Message.h> 32 #include <List.h> 33 #include <Rect.h> 34 //#include <Errors.h> 35 36 37 BTab::BTab(BView *tabView) 38 : 39 fEnabled(true), 40 fSelected(false), 41 fFocus(false), 42 fView(tabView) 43 { 44 } 45 46 47 BTab::BTab(BMessage *archive) 48 : 49 fSelected(false), 50 fFocus(false), 51 fView(NULL) 52 { 53 bool disable; 54 55 if (archive->FindBool("_disable", &disable) != B_OK) 56 SetEnabled(true); 57 else 58 SetEnabled(!disable); 59 } 60 61 62 BTab::~BTab() 63 { 64 if (!fView) 65 return; 66 67 if (fSelected) 68 fView->RemoveSelf(); 69 70 delete fView; 71 } 72 73 74 BArchivable * 75 BTab::Instantiate(BMessage *archive) 76 { 77 if (validate_instantiation(archive, "BTab")) 78 return new BTab(archive); 79 80 return NULL; 81 } 82 83 84 status_t 85 BTab::Archive(BMessage *archive, bool deep) const 86 { 87 status_t err = BArchivable::Archive(archive, deep); 88 if (err != B_OK) 89 return err; 90 91 if (!fEnabled) 92 err = archive->AddBool("_disable", false); 93 94 return err; 95 } 96 97 98 status_t 99 BTab::Perform(uint32 d, void *arg) 100 { 101 return BArchivable::Perform(d, arg); 102 } 103 104 105 const char * 106 BTab::Label() const 107 { 108 if (fView) 109 return fView->Name(); 110 else 111 return NULL; 112 } 113 114 115 void 116 BTab::SetLabel(const char *label) 117 { 118 if (!label || !fView) 119 return; 120 121 fView->SetName(label); 122 } 123 124 125 bool 126 BTab::IsSelected() const 127 { 128 return fSelected; 129 } 130 131 132 void 133 BTab::Select(BView *owner) 134 { 135 if (!owner || !View() || !owner->Window()) 136 return; 137 138 owner->AddChild(fView); 139 //fView->Show(); 140 141 fSelected = true; 142 } 143 144 145 void 146 BTab::Deselect() 147 { 148 if (View()) 149 View()->RemoveSelf(); 150 151 fSelected = false; 152 } 153 154 155 void 156 BTab::SetEnabled(bool enabled) 157 { 158 fEnabled = enabled; 159 } 160 161 162 bool 163 BTab::IsEnabled() const 164 { 165 return fEnabled; 166 } 167 168 169 void 170 BTab::MakeFocus(bool inFocus) 171 { 172 fFocus = inFocus; 173 } 174 175 176 bool 177 BTab::IsFocus() const 178 { 179 return fFocus; 180 } 181 182 183 void 184 BTab::SetView(BView *view) 185 { 186 if (!view || fView == view) 187 return; 188 189 if (fView != NULL) { 190 fView->RemoveSelf(); 191 delete fView; 192 } 193 fView = view; 194 } 195 196 197 BView * 198 BTab::View() const 199 { 200 return fView; 201 } 202 203 204 void 205 BTab::DrawFocusMark(BView *owner, BRect frame) 206 { 207 float width = owner->StringWidth(Label()); 208 209 owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 210 owner->StrokeLine(BPoint(frame.left + frame.Width() * 0.5f - width * 0.5f, frame.bottom), 211 BPoint(frame.left + frame.Width() * 0.5f + width * 0.5f, frame.bottom)); 212 } 213 214 215 void 216 BTab::DrawLabel(BView *owner, BRect frame) 217 { 218 const char *label = Label(); 219 if (label == NULL) 220 return; 221 222 owner->SetHighColor(0, 0, 0); 223 owner->DrawString(label, BPoint(frame.left + frame.Width() * 0.5f - 224 owner->StringWidth(label) * 0.5f, frame.bottom - 4.0f - 2.0f)); 225 } 226 227 228 void 229 BTab::DrawTab(BView *owner, BRect frame, tab_position position, bool full) 230 { 231 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 232 rgb_color lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 233 rgb_color darken4 = tint_color(no_tint, B_DARKEN_4_TINT); 234 rgb_color darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 235 236 owner->SetHighColor(darkenmax); 237 owner->SetLowColor(no_tint); 238 DrawLabel(owner, frame); 239 240 owner->BeginLineArray(12); 241 242 if (position != B_TAB_ANY) { 243 owner->AddLine(BPoint(frame.left - 2.0f, frame.bottom), 244 BPoint(frame.left - 1.0f, frame.bottom - 1.0f), lightenmax); 245 owner->AddLine(BPoint(frame.left, frame.bottom - 2.0f), 246 BPoint(frame.left, frame.bottom - 3.0f), lightenmax); 247 } 248 249 owner->AddLine(BPoint(frame.left + 1.0f, frame.bottom - 4.0f), 250 BPoint(frame.left + 1.0f, frame.top + 5.0f), lightenmax); 251 owner->AddLine(BPoint(frame.left + 1.0f, frame.top + 4.0f), 252 BPoint(frame.left + 2.0f, frame.top + 2.0f), lightenmax); 253 owner->AddLine(BPoint(frame.left + 3.0f, frame.top + 1.0f), 254 BPoint(frame.left + 4.0f, frame.top + 1.0f), lightenmax); 255 owner->AddLine(BPoint(frame.left + 5.0f, frame.top), 256 BPoint(frame.right - 5.0f, frame.top), lightenmax); 257 258 owner->AddLine(BPoint(frame.right - 4.0f, frame.top + 1.0f), 259 BPoint(frame.right - 3.0f, frame.top + 1.0f), lightenmax); 260 261 owner->AddLine(BPoint(frame.right - 2.0f, frame.top + 2.0f), 262 BPoint(frame.right - 2.0f, frame.top + 3.0f), darken4); 263 owner->AddLine(BPoint(frame.right - 1.0f, frame.top + 4.0f), 264 BPoint(frame.right - 1.0f, frame.bottom - 4.0f), darken4); 265 266 if (full) { 267 owner->AddLine(BPoint(frame.right, frame.bottom - 3.0f), 268 BPoint(frame.right, frame.bottom - 2.0f), darken4); 269 owner->AddLine(BPoint(frame.right + 1.0f, frame.bottom - 1.0f), 270 BPoint(frame.right + 2.0f, frame.bottom), darken4); 271 } 272 273 owner->EndLineArray(); 274 } 275 276 277 void BTab::_ReservedTab1() {} 278 void BTab::_ReservedTab2() {} 279 void BTab::_ReservedTab3() {} 280 void BTab::_ReservedTab4() {} 281 void BTab::_ReservedTab5() {} 282 void BTab::_ReservedTab6() {} 283 void BTab::_ReservedTab7() {} 284 void BTab::_ReservedTab8() {} 285 void BTab::_ReservedTab9() {} 286 void BTab::_ReservedTab10() {} 287 void BTab::_ReservedTab11() {} 288 void BTab::_ReservedTab12() {} 289 290 BTab &BTab::operator=(const BTab &) 291 { 292 // this is private and not functional, but exported 293 return *this; 294 } 295 296 297 // #pragma mark - 298 299 300 BTabView::BTabView(BRect frame, const char *name, button_width width, 301 uint32 resizingMode, uint32 flags) 302 : BView(frame, name, resizingMode, flags) 303 { 304 _InitObject(); 305 306 fTabWidthSetting = width; 307 } 308 309 310 BTabView::~BTabView() 311 { 312 for (int32 i = 0; i < CountTabs(); i++) { 313 delete TabAt(i); 314 } 315 316 delete fTabList; 317 } 318 319 320 BTabView::BTabView(BMessage *archive) 321 : BView(archive), 322 fFocus(-1) 323 { 324 fContainerView = NULL; 325 fTabList = new BList; 326 327 int16 width; 328 329 if (archive->FindInt16("_but_width", &width) == B_OK) 330 fTabWidthSetting = (button_width)width; 331 else 332 fTabWidthSetting = B_WIDTH_AS_USUAL; 333 334 if (archive->FindFloat("_high", &fTabHeight) != B_OK) { 335 font_height fh; 336 GetFontHeight(&fh); 337 fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f; 338 } 339 340 fFocus = -1; 341 342 if (archive->FindInt32("_sel", &fSelection) != B_OK) 343 fSelection = 0; 344 345 if (fContainerView == NULL) 346 fContainerView = ChildAt(0); 347 348 int32 i = 0; 349 BMessage tabMsg; 350 351 while (archive->FindMessage("_l_items", i, &tabMsg) == B_OK) { 352 BArchivable *archivedTab = instantiate_object(&tabMsg); 353 354 if (archivedTab) { 355 BTab *tab = dynamic_cast<BTab *>(archivedTab); 356 357 BMessage viewMsg; 358 if (archive->FindMessage("_view_list", i, &viewMsg) == B_OK) { 359 BArchivable *archivedView = instantiate_object(&viewMsg); 360 if (archivedView) 361 AddTab(dynamic_cast<BView*>(archivedView), tab); 362 } 363 } 364 365 tabMsg.MakeEmpty(); 366 i++; 367 } 368 } 369 370 371 BArchivable * 372 BTabView::Instantiate(BMessage *archive) 373 { 374 if ( validate_instantiation(archive, "BTabView")) 375 return new BTabView(archive); 376 377 return NULL; 378 } 379 380 381 status_t 382 BTabView::Archive(BMessage *archive, bool deep) const 383 { 384 if (CountTabs() > 0) 385 TabAt(Selection())->View()->RemoveSelf(); 386 387 BView::Archive(archive, deep); 388 389 archive->AddInt16("_but_width", fTabWidthSetting); 390 archive->AddFloat("_high", fTabHeight); 391 archive->AddInt32("_sel", fSelection); 392 393 if (deep) { 394 for (int32 i = 0; i < CountTabs(); i++) { 395 BMessage tabArchive; 396 BTab *tab = TabAt(i); 397 398 if (!tab) 399 continue; 400 401 if (tab->Archive(&tabArchive, true) == B_OK) 402 archive->AddMessage("_l_items", &tabArchive); 403 404 if (!tab->View()) 405 continue; 406 407 BMessage viewArchive; 408 409 if (tab->View()->Archive(&viewArchive, true) == B_OK) 410 archive->AddMessage("_view_list", &viewArchive); 411 } 412 } 413 414 if (CountTabs() > 0) { 415 if (TabAt(Selection())->View() && ContainerView()) 416 TabAt(Selection())->Select(ContainerView()); 417 } 418 419 return B_OK; 420 } 421 422 423 status_t 424 BTabView::Perform(perform_code d, void *arg) 425 { 426 return BView::Perform(d, arg); 427 } 428 429 430 void 431 BTabView::WindowActivated(bool active) 432 { 433 BView::WindowActivated(active); 434 435 DrawTabs(); 436 } 437 438 439 void 440 BTabView::AttachedToWindow() 441 { 442 BView::AttachedToWindow(); 443 444 Select(fSelection); 445 } 446 447 448 void 449 BTabView::AllAttached() 450 { 451 BView::AllAttached(); 452 } 453 454 455 void 456 BTabView::AllDetached() 457 { 458 BView::AllDetached(); 459 } 460 461 462 void 463 BTabView::DetachedFromWindow() 464 { 465 BView::DetachedFromWindow(); 466 } 467 468 469 void 470 BTabView::MessageReceived(BMessage *message) 471 { 472 BView::MessageReceived(message); 473 } 474 475 476 void 477 BTabView::FrameMoved(BPoint newLocation) 478 { 479 BView::FrameMoved(newLocation); 480 } 481 482 483 void 484 BTabView::FrameResized(float width,float height) 485 { 486 BView::FrameResized(width, height); 487 } 488 489 490 void 491 BTabView::KeyDown(const char *bytes, int32 numBytes) 492 { 493 if (IsHidden()) 494 return; 495 496 switch (bytes[0]) { 497 case B_DOWN_ARROW: 498 case B_LEFT_ARROW: 499 SetFocusTab((fFocus - 1) % CountTabs(), true); 500 break; 501 502 case B_UP_ARROW: 503 case B_RIGHT_ARROW: 504 SetFocusTab((fFocus + 1) % CountTabs(), true); 505 break; 506 507 case B_RETURN: 508 case B_SPACE: 509 Select(FocusTab()); 510 break; 511 512 default: 513 BView::KeyDown(bytes, numBytes); 514 } 515 } 516 517 518 void 519 BTabView::MouseDown(BPoint point) 520 { 521 if (point.y > fTabHeight) 522 return; 523 524 for (int32 i = 0; i < CountTabs(); i++) { 525 if (TabFrame(i).Contains(point) 526 && i != Selection()) { 527 Select(i); 528 return; 529 } 530 } 531 532 BView::MouseDown(point); 533 } 534 535 536 void 537 BTabView::MouseUp(BPoint point) 538 { 539 BView::MouseUp(point); 540 } 541 542 543 void 544 BTabView::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 545 { 546 BView::MouseMoved(point, transit, message); 547 } 548 549 550 void 551 BTabView::Pulse() 552 { 553 BView::Pulse(); 554 } 555 556 557 void 558 BTabView::Select(int32 index) 559 { 560 if (index < 0 || index >= CountTabs()) 561 index = Selection(); 562 563 BTab *tab = TabAt(Selection()); 564 if (tab) 565 tab->Deselect(); 566 567 tab = TabAt(index); 568 if (tab && ContainerView()) { 569 tab->Select(ContainerView()); 570 fSelection = index; 571 } 572 573 //Draw(Bounds()); 574 Invalidate(); 575 } 576 577 578 int32 579 BTabView::Selection() const 580 { 581 return fSelection; 582 } 583 584 585 void 586 BTabView::MakeFocus(bool focused) 587 { 588 BView::MakeFocus(focused); 589 590 SetFocusTab(Selection(), focused); 591 } 592 593 594 void 595 BTabView::SetFocusTab(int32 tab, bool focused) 596 { 597 if (tab >= CountTabs()) 598 return; 599 600 if (focused) { 601 if (tab == fFocus) 602 return; 603 604 if (fFocus != -1) 605 TabAt (fFocus)->MakeFocus(false); 606 607 TabAt(tab)->MakeFocus(true); 608 fFocus = tab; 609 } else if (fFocus != -1) { 610 TabAt (fFocus)->MakeFocus(false); 611 fFocus = -1; 612 } 613 614 Invalidate(); 615 } 616 617 618 int32 619 BTabView::FocusTab() const 620 { 621 return fFocus; 622 } 623 624 625 void 626 BTabView::Draw(BRect updateRect) 627 { 628 DrawBox(DrawTabs()); 629 630 if (IsFocus() && fFocus != -1) 631 TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus)); 632 } 633 634 635 BRect 636 BTabView::DrawTabs() 637 { 638 for (int32 i = 0; i < CountTabs(); i++) 639 TabAt(i)->DrawTab(this, TabFrame(i), 640 i == fSelection ? B_TAB_FRONT : (i == 0) ? B_TAB_FIRST : B_TAB_ANY, 641 i + 1 != fSelection); 642 643 if (fSelection < CountTabs()) 644 return TabFrame(fSelection); 645 646 return BRect(); 647 } 648 649 650 void 651 BTabView::DrawBox(BRect selTabRect) 652 { 653 BRect rect = Bounds(); 654 655 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 656 B_LIGHTEN_MAX_TINT)); 657 658 StrokeLine(BPoint(0.0f, rect.bottom), BPoint(0.0f, selTabRect.bottom)); 659 StrokeLine(BPoint(selTabRect.left - 3.0f, selTabRect.bottom)); 660 StrokeLine(BPoint(selTabRect.right + 3.0f, selTabRect.bottom), 661 BPoint(rect.right - 1.0f, selTabRect.bottom)); 662 663 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 664 B_DARKEN_4_TINT)); 665 666 StrokeLine(BPoint(rect.right, selTabRect.bottom + 1.0f), 667 BPoint(rect.right, rect.bottom)); 668 StrokeLine(BPoint(rect.left + 1.0f, rect.bottom)); 669 } 670 671 672 BRect 673 BTabView::TabFrame(int32 tab_index) const 674 { 675 switch (fTabWidthSetting) { 676 case B_WIDTH_FROM_LABEL: 677 { 678 float x = 6.0f; 679 for (int32 i = 0; i < tab_index; i++) 680 x += StringWidth(TabAt(i)->Label()) + 20.0f; 681 682 return BRect(x, 0.0f, 683 x + StringWidth(TabAt(tab_index)->Label()) + 20.0f, fTabHeight); 684 } 685 686 case B_WIDTH_FROM_WIDEST: 687 { 688 float width = 0.0f; 689 690 for (int32 i = 0; i < CountTabs(); i++) { 691 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f; 692 693 if (tabWidth > width) 694 width = tabWidth; 695 } 696 697 return BRect(6.0f + tab_index * width, 0.0f, 698 6.0f + tab_index * width + width, fTabHeight); 699 } 700 701 case B_WIDTH_AS_USUAL: 702 default: 703 return BRect(6.0f + tab_index * 100.0f, 0.0f, 704 6.0f + tab_index * 100.0f + 100.0f, fTabHeight); 705 } 706 } 707 708 709 void 710 BTabView::SetFlags(uint32 flags) 711 { 712 BView::SetFlags(flags); 713 } 714 715 716 void 717 BTabView::SetResizingMode(uint32 mode) 718 { 719 BView::SetResizingMode(mode); 720 } 721 722 723 void 724 BTabView::GetPreferredSize(float *width, float *height) 725 { 726 BView::GetPreferredSize(width, height); 727 } 728 729 730 void 731 BTabView::ResizeToPreferred() 732 { 733 BView::ResizeToPreferred(); 734 } 735 736 737 BHandler * 738 BTabView::ResolveSpecifier(BMessage *message, int32 index, 739 BMessage *specifier, int32 what, const char *property) 740 { 741 return BView::ResolveSpecifier(message, index, specifier, what, property); 742 } 743 744 745 status_t 746 BTabView::GetSupportedSuites(BMessage *message) 747 { 748 return BView::GetSupportedSuites(message); 749 } 750 751 752 void 753 BTabView::AddTab(BView *target, BTab *tab) 754 { 755 if (tab == NULL) 756 tab = new BTab(target); 757 else 758 tab->SetView(target); 759 760 fTabList->AddItem(tab); 761 } 762 763 764 BTab * 765 BTabView::RemoveTab(int32 index) 766 { 767 if (index < 0 || index >= CountTabs()) 768 return NULL; 769 770 BTab *tab = (BTab *)fTabList->RemoveItem(index); 771 if (tab == NULL) 772 return NULL; 773 774 tab->Deselect(); 775 776 if (index <= fSelection && fSelection != 0) 777 fSelection--; 778 779 Select(fSelection); 780 781 if (fFocus == CountTabs() - 1) 782 SetFocusTab(fFocus, false); 783 else 784 SetFocusTab(fFocus, true); 785 786 return tab; 787 } 788 789 790 BTab * 791 BTabView::TabAt(int32 index) const 792 { 793 return (BTab *)fTabList->ItemAt(index); 794 } 795 796 797 void 798 BTabView::SetTabWidth(button_width width) 799 { 800 fTabWidthSetting = width; 801 802 Invalidate(); 803 } 804 805 806 button_width 807 BTabView::TabWidth() const 808 { 809 return fTabWidthSetting; 810 } 811 812 813 void 814 BTabView::SetTabHeight(float height) 815 { 816 if (fTabHeight == height) 817 return; 818 819 fContainerView->MoveBy(0.0f, height - fTabHeight); 820 fContainerView->ResizeBy(0.0f, height - fTabHeight); 821 822 fTabHeight = height; 823 824 Invalidate(); 825 } 826 827 828 float 829 BTabView::TabHeight() const 830 { 831 return fTabHeight; 832 } 833 834 835 BView * 836 BTabView::ContainerView() const 837 { 838 return fContainerView; 839 } 840 841 842 int32 843 BTabView::CountTabs() const 844 { 845 return fTabList->CountItems(); 846 } 847 848 849 BView * 850 BTabView::ViewForTab(int32 tabIndex) const 851 { 852 BTab *tab = TabAt(tabIndex); 853 if (tab) 854 return tab->View(); 855 856 return NULL; 857 } 858 859 860 void 861 BTabView::_InitObject() 862 { 863 fTabList = new BList; 864 865 fTabWidthSetting = B_WIDTH_AS_USUAL; 866 fSelection = 0; 867 fFocus = -1; 868 869 rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR); 870 871 SetViewColor(color); 872 SetLowColor(color); 873 874 font_height fh; 875 GetFontHeight(&fh); 876 fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f; 877 878 BRect bounds = Bounds(); 879 880 bounds.top += 1.0f + TabHeight(); 881 bounds.InsetBy(2.0f, 2.0f); 882 883 fContainerView = new BView(bounds, "view container", B_FOLLOW_ALL, 884 B_WILL_DRAW); 885 886 fContainerView->SetViewColor(color); 887 fContainerView->SetLowColor(color); 888 889 AddChild(fContainerView); 890 } 891 892 893 void BTabView::_ReservedTabView1() {} 894 void BTabView::_ReservedTabView2() {} 895 void BTabView::_ReservedTabView3() {} 896 void BTabView::_ReservedTabView4() {} 897 void BTabView::_ReservedTabView5() {} 898 void BTabView::_ReservedTabView6() {} 899 void BTabView::_ReservedTabView7() {} 900 void BTabView::_ReservedTabView8() {} 901 void BTabView::_ReservedTabView9() {} 902 void BTabView::_ReservedTabView10() {} 903 void BTabView::_ReservedTabView11() {} 904 void BTabView::_ReservedTabView12() {} 905 906 907 BTabView::BTabView(const BTabView &tabView) 908 : BView(tabView) 909 { 910 // this is private and not functional, but exported 911 } 912 913 914 BTabView &BTabView::operator=(const BTabView &) 915 { 916 // this is private and not functional, but exported 917 return *this; 918 } 919