1 /* 2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "DefaultMediaTheme.h" 8 9 #include <Box.h> 10 #include <Button.h> 11 #include <ChannelSlider.h> 12 #include <CheckBox.h> 13 #include <MediaRoster.h> 14 #include <MenuField.h> 15 #include <MessageFilter.h> 16 #include <OptionPopUp.h> 17 #include <ParameterWeb.h> 18 #include <ScrollBar.h> 19 #include <Slider.h> 20 #include <StringView.h> 21 #include <TabView.h> 22 #include <TextControl.h> 23 #include <Window.h> 24 25 #include "debug.h" 26 27 28 using namespace BPrivate; 29 30 31 namespace BPrivate { 32 33 class DynamicScrollView : public BView { 34 public: 35 DynamicScrollView(const char *name, BView *target); 36 virtual ~DynamicScrollView(); 37 38 virtual void AttachedToWindow(void); 39 virtual void FrameResized(float width, float height); 40 virtual void FrameMoved(BPoint newPosition); 41 virtual void GetPreferredSize(float *_width, float *_height); 42 43 void SetContentBounds(BRect bounds); 44 BRect ContentBounds() const { return fContentBounds; } 45 46 private: 47 void UpdateBars(); 48 49 BScrollBar *fHorizontalScrollBar, *fVerticalScrollBar; 50 BRect fContentBounds; 51 BView *fTarget; 52 bool fIsDocumentScroller; 53 }; 54 55 class GroupView : public BView { 56 public: 57 GroupView(BRect frame, const char *name); 58 virtual ~GroupView(); 59 60 virtual void AttachedToWindow(); 61 virtual void AllAttached(); 62 virtual void GetPreferredSize(float *_width, float *_height); 63 64 virtual BSize MinSize(); 65 virtual BSize MaxSize(); 66 virtual BSize PreferredSize(); 67 68 void SetContentBounds(BRect bounds); 69 BRect ContentBounds() const { return fContentBounds; } 70 71 private: 72 BRect fContentBounds; 73 }; 74 75 class TabView : public BTabView { 76 public: 77 TabView(BRect frame, const char *name, button_width width = B_WIDTH_FROM_LABEL, 78 uint32 resizingMode = B_FOLLOW_ALL, uint32 flags = B_FULL_UPDATE_ON_RESIZE 79 | B_WILL_DRAW | B_NAVIGABLE_JUMP | B_FRAME_EVENTS | B_NAVIGABLE); 80 81 virtual void FrameResized(float width, float height); 82 virtual void Select(int32 tab); 83 }; 84 85 class SeparatorView : public BView { 86 public: 87 SeparatorView(BRect frame); 88 virtual ~SeparatorView(); 89 90 virtual void Draw(BRect updateRect); 91 92 private: 93 bool fVertical; 94 }; 95 96 class TitleView : public BView { 97 public: 98 TitleView(BRect frame, const char *title); 99 virtual ~TitleView(); 100 101 virtual void Draw(BRect updateRect); 102 virtual void GetPreferredSize(float *width, float *height); 103 104 private: 105 const char *fTitle; 106 }; 107 108 class MessageFilter : public BMessageFilter { 109 public: 110 static MessageFilter *FilterFor(BView *view, BParameter ¶meter); 111 112 protected: 113 MessageFilter(); 114 }; 115 116 class ContinuousMessageFilter : public MessageFilter { 117 public: 118 ContinuousMessageFilter(BControl *control, 119 BContinuousParameter ¶meter); 120 virtual ~ContinuousMessageFilter(); 121 122 virtual filter_result Filter(BMessage *message, BHandler **target); 123 124 private: 125 void _UpdateControl(); 126 127 BControl *fControl; 128 BContinuousParameter &fParameter; 129 bool fRegistered; 130 }; 131 132 class DiscreteMessageFilter : public MessageFilter { 133 public: 134 DiscreteMessageFilter(BControl *control, BDiscreteParameter ¶meter); 135 virtual ~DiscreteMessageFilter(); 136 137 virtual filter_result Filter(BMessage *message, BHandler **target); 138 139 private: 140 BDiscreteParameter &fParameter; 141 }; 142 143 } // namespace BPrivate 144 145 146 const uint32 kMsgParameterChanged = '_mPC'; 147 148 149 static bool 150 parameter_should_be_hidden(BParameter ¶meter) 151 { 152 // ToDo: note, this is probably completely stupid, but it's the only 153 // way I could safely remove the null parameters that are not shown 154 // by the R5 media theme 155 if (parameter.Type() != BParameter::B_NULL_PARAMETER 156 || strcmp(parameter.Kind(), B_WEB_PHYSICAL_INPUT)) 157 return false; 158 159 for (int32 i = 0; i < parameter.CountOutputs(); i++) { 160 if (!strcmp(parameter.OutputAt(0)->Kind(), B_INPUT_MUX)) 161 return true; 162 } 163 164 return false; 165 } 166 167 168 // #pragma mark - 169 170 171 DynamicScrollView::DynamicScrollView(const char *name, BView *target) 172 : BView(target->Frame(), name, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS), 173 fHorizontalScrollBar(NULL), 174 fVerticalScrollBar(NULL), 175 fTarget(target), 176 fIsDocumentScroller(false) 177 { 178 fContentBounds.Set(-1, -1, -1, -1); 179 SetViewColor(fTarget->ViewColor()); 180 target->MoveTo(B_ORIGIN); 181 AddChild(target); 182 } 183 184 185 DynamicScrollView::~DynamicScrollView() 186 { 187 } 188 189 190 void 191 DynamicScrollView::AttachedToWindow(void) 192 { 193 BRect frame = ConvertToScreen(Bounds()); 194 BRect windowFrame = Window()->Frame(); 195 196 fIsDocumentScroller = Parent() == NULL 197 && Window() != NULL 198 && Window()->Look() == B_DOCUMENT_WINDOW_LOOK 199 && frame.right == windowFrame.right 200 && frame.bottom == windowFrame.bottom; 201 202 UpdateBars(); 203 } 204 205 206 void 207 DynamicScrollView::FrameResized(float width, float height) 208 { 209 UpdateBars(); 210 } 211 212 213 void 214 DynamicScrollView::FrameMoved(BPoint newPosition) 215 { 216 UpdateBars(); 217 } 218 219 220 void 221 DynamicScrollView::GetPreferredSize(float *_width, float *_height) 222 { 223 float width = 50; 224 if (fVerticalScrollBar) 225 width += B_V_SCROLL_BAR_WIDTH; 226 float height = 50; 227 if (fHorizontalScrollBar) 228 height += B_H_SCROLL_BAR_HEIGHT; 229 if (_width) 230 *_width = width; 231 if (_height) 232 *_height = height; 233 } 234 235 236 void 237 DynamicScrollView::SetContentBounds(BRect bounds) 238 { 239 fContentBounds = bounds; 240 if (Window()) 241 UpdateBars(); 242 } 243 244 245 void 246 DynamicScrollView::UpdateBars() 247 { 248 // we need the size that the view wants to have, and the one 249 // it could have (without the space for the scroll bars) 250 251 float width, height; 252 if (fContentBounds == BRect(-1, -1, -1, -1)) 253 fTarget->GetPreferredSize(&width, &height); 254 else { 255 width = fContentBounds.Width(); 256 height = fContentBounds.Height(); 257 } 258 259 BRect bounds = Bounds(); 260 261 // do we have to remove a scroll bar? 262 263 bool horizontal = width > bounds.Width(); 264 bool vertical = height > bounds.Height(); 265 266 if (!horizontal && fHorizontalScrollBar != NULL) { 267 RemoveChild(fHorizontalScrollBar); 268 delete fHorizontalScrollBar; 269 fHorizontalScrollBar = NULL; 270 } 271 272 if (!vertical && fVerticalScrollBar != NULL) { 273 RemoveChild(fVerticalScrollBar); 274 delete fVerticalScrollBar; 275 fVerticalScrollBar = NULL; 276 } 277 278 // or do we have to add a scroll bar? 279 280 if (horizontal && fHorizontalScrollBar == NULL) { 281 BRect rect = Bounds(); 282 rect.top = rect.bottom + 1 - B_H_SCROLL_BAR_HEIGHT; 283 if (vertical || fIsDocumentScroller) 284 rect.right -= B_V_SCROLL_BAR_WIDTH; 285 286 fHorizontalScrollBar = new BScrollBar(rect, "horizontal", fTarget, 0, 287 width, B_HORIZONTAL); 288 AddChild(fHorizontalScrollBar); 289 } 290 291 if (vertical && fVerticalScrollBar == NULL) { 292 BRect rect = Bounds(); 293 rect.left = rect.right + 1 - B_V_SCROLL_BAR_WIDTH; 294 if (horizontal || fIsDocumentScroller) 295 rect.bottom -= B_H_SCROLL_BAR_HEIGHT; 296 297 fVerticalScrollBar = new BScrollBar(rect, "vertical", fTarget, 0, 298 height, B_VERTICAL); 299 AddChild(fVerticalScrollBar); 300 } 301 302 // update the scroll bar range & proportions and layout views 303 304 bounds = Bounds(); 305 if (fHorizontalScrollBar != NULL) 306 bounds.bottom -= B_H_SCROLL_BAR_HEIGHT + 1; 307 if (fVerticalScrollBar != NULL) 308 bounds.right -= B_V_SCROLL_BAR_WIDTH + 1; 309 310 fTarget->MoveTo(bounds.LeftTop()); 311 fTarget->ResizeTo(bounds.Width(), bounds.Height()); 312 313 if (fHorizontalScrollBar != NULL) { 314 float delta = width - bounds.Width(); 315 if (delta < 0) 316 delta = 0; 317 318 fHorizontalScrollBar->SetRange(0, delta); 319 fHorizontalScrollBar->SetSteps(1, bounds.Width()); 320 fHorizontalScrollBar->SetProportion(bounds.Width() / width); 321 322 float barWidth = Bounds().Width(); 323 if (vertical) { 324 // scrollbars overlap one pixel of the frame 325 barWidth += 1; 326 } 327 if (vertical || fIsDocumentScroller) 328 barWidth -= B_V_SCROLL_BAR_WIDTH + 1; 329 330 fHorizontalScrollBar->MoveTo(bounds.left, bounds.bottom + 1); 331 fHorizontalScrollBar->ResizeTo(barWidth, B_H_SCROLL_BAR_HEIGHT); 332 } 333 if (fVerticalScrollBar != NULL) { 334 float delta = height - bounds.Height(); 335 if (delta < 0) 336 delta = 0; 337 338 fVerticalScrollBar->SetRange(0, delta); 339 fVerticalScrollBar->SetSteps(1, bounds.Height()); 340 fVerticalScrollBar->SetProportion(bounds.Height() / height); 341 342 float barHeight = Bounds().Height(); 343 if (horizontal) { 344 // scrollbars overlap one pixel of the frame 345 barHeight += 1; 346 } 347 if (horizontal || fIsDocumentScroller) 348 barHeight -= B_H_SCROLL_BAR_HEIGHT + 1; 349 350 fVerticalScrollBar->MoveTo(bounds.right + 1, bounds.top); 351 fVerticalScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, barHeight); 352 } 353 } 354 355 356 // #pragma mark - 357 358 359 GroupView::GroupView(BRect frame, const char *name) 360 : BView(frame, name, B_FOLLOW_NONE, B_WILL_DRAW) 361 { 362 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 363 } 364 365 366 GroupView::~GroupView() 367 { 368 } 369 370 371 void 372 GroupView::AttachedToWindow() 373 { 374 for (int32 i = CountChildren(); i-- > 0;) { 375 BControl *control = dynamic_cast<BControl *>(ChildAt(i)); 376 if (control == NULL) 377 continue; 378 379 control->SetTarget(control); 380 } 381 } 382 383 384 void 385 GroupView::AllAttached() 386 { 387 } 388 389 390 void 391 GroupView::GetPreferredSize(float *_width, float *_height) 392 { 393 if (_width) 394 *_width = fContentBounds.Width(); 395 396 if (_height) 397 *_height = fContentBounds.Height(); 398 } 399 400 401 BSize 402 GroupView::MinSize() 403 { 404 return BSize(100, 100); 405 } 406 407 408 BSize 409 GroupView::PreferredSize() 410 { 411 return MinSize(); 412 } 413 414 415 BSize 416 GroupView::MaxSize() 417 { 418 BSize max; 419 GetPreferredSize(&max.width, &max.height); 420 return max; 421 } 422 423 424 void 425 GroupView::SetContentBounds(BRect bounds) 426 { 427 fContentBounds = bounds; 428 } 429 430 431 // #pragma mark - 432 433 434 /** BTabView is really stupid - it doesn't even resize its content 435 * view when it is resized itself. 436 * This derived class fixes this issue, and also resizes all tab 437 * content views to the size of the container view when they are 438 * selected (does not take their resize flags into account, though). 439 */ 440 441 TabView::TabView(BRect frame, const char *name, button_width width, 442 uint32 resizingMode, uint32 flags) 443 : BTabView(frame, name, width, resizingMode, flags) 444 { 445 } 446 447 448 void 449 TabView::FrameResized(float width, float height) 450 { 451 BRect rect = Bounds(); 452 rect.top += TabHeight(); 453 rect.InsetBy(3.0f, 3.0f); 454 //ContainerView is inseted by 3.0 in BTabView::_InitObject() 455 456 ContainerView()->ResizeTo(rect.Width(), rect.Height()); 457 } 458 459 460 void 461 TabView::Select(int32 tab) 462 { 463 BTabView::Select(tab); 464 465 BView *view = ViewForTab(Selection()); 466 if (view != NULL) { 467 BRect rect = ContainerView()->Bounds(); 468 view->ResizeTo(rect.Width(), rect.Height()); 469 } 470 } 471 472 473 // #pragma mark - 474 475 476 SeparatorView::SeparatorView(BRect frame) 477 : BView(frame, "-", B_FOLLOW_NONE, B_WILL_DRAW) 478 { 479 fVertical = frame.Width() < frame.Height(); 480 SetViewColor(B_TRANSPARENT_COLOR); 481 } 482 483 484 SeparatorView::~SeparatorView() 485 { 486 } 487 488 489 void 490 SeparatorView::Draw(BRect updateRect) 491 { 492 rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR); 493 BRect rect = updateRect & Bounds(); 494 495 SetHighColor(tint_color(color, B_DARKEN_1_TINT)); 496 if (fVertical) 497 StrokeLine(BPoint(0, rect.top), BPoint(0, rect.bottom)); 498 else 499 StrokeLine(BPoint(rect.left, 0), BPoint(rect.right, 0)); 500 501 SetHighColor(tint_color(color, B_LIGHTEN_1_TINT)); 502 if (fVertical) 503 StrokeLine(BPoint(1, rect.top), BPoint(1, rect.bottom)); 504 else 505 StrokeLine(BPoint(rect.left, 1), BPoint(rect.right, 1)); 506 } 507 508 509 // #pragma mark - 510 511 512 TitleView::TitleView(BRect frame, const char *title) 513 : BView(frame, title, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW) 514 { 515 fTitle = strdup(title); 516 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 517 SetLowColor(ViewColor()); 518 } 519 520 521 TitleView::~TitleView() 522 { 523 free((char *)fTitle); 524 } 525 526 527 void 528 TitleView::Draw(BRect updateRect) 529 { 530 BRect rect(Bounds()); 531 532 SetDrawingMode(B_OP_COPY); 533 SetHighColor(240, 240, 240); 534 DrawString(fTitle, BPoint(rect.left + 1, rect.bottom - 8)); 535 536 SetDrawingMode(B_OP_OVER); 537 SetHighColor(80, 20, 20); 538 DrawString(fTitle, BPoint(rect.left, rect.bottom - 9)); 539 } 540 541 542 void 543 TitleView::GetPreferredSize(float *_width, float *_height) 544 { 545 if (_width) 546 *_width = StringWidth(fTitle) + 2; 547 548 if (_height) { 549 font_height fontHeight; 550 GetFontHeight(&fontHeight); 551 552 *_height = fontHeight.ascent + fontHeight.descent + fontHeight.leading 553 + 8; 554 } 555 } 556 557 558 // #pragma mark - 559 560 561 MessageFilter::MessageFilter() 562 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE) 563 { 564 } 565 566 567 MessageFilter * 568 MessageFilter::FilterFor(BView *view, BParameter ¶meter) 569 { 570 BControl *control = dynamic_cast<BControl *>(view); 571 if (control == NULL) 572 return NULL; 573 574 switch (parameter.Type()) { 575 case BParameter::B_CONTINUOUS_PARAMETER: 576 return new ContinuousMessageFilter(control, 577 static_cast<BContinuousParameter &>(parameter)); 578 579 case BParameter::B_DISCRETE_PARAMETER: 580 return new DiscreteMessageFilter(control, 581 static_cast<BDiscreteParameter &>(parameter)); 582 583 case BParameter::B_NULL_PARAMETER: /* fall through */ 584 default: 585 return NULL; 586 } 587 } 588 589 590 // #pragma mark - 591 592 593 ContinuousMessageFilter::ContinuousMessageFilter(BControl *control, 594 BContinuousParameter ¶meter) 595 : MessageFilter(), 596 fControl(control), 597 fParameter(parameter), 598 fRegistered(false) 599 { 600 // initialize view for us 601 control->SetMessage(new BMessage(kMsgParameterChanged)); 602 603 if (BSlider *slider = dynamic_cast<BSlider *>(fControl)) 604 slider->SetModificationMessage(new BMessage(kMsgParameterChanged)); 605 else if (BChannelSlider *slider = dynamic_cast<BChannelSlider *>(fControl)) 606 slider->SetModificationMessage(new BMessage(kMsgParameterChanged)); 607 else 608 ERROR("ContinuousMessageFilter: unknown continuous parameter view\n"); 609 610 // set initial value 611 _UpdateControl(); 612 } 613 614 615 ContinuousMessageFilter::~ContinuousMessageFilter() 616 { 617 } 618 619 620 filter_result 621 ContinuousMessageFilter::Filter(BMessage *message, BHandler **target) 622 { 623 if (*target != fControl) 624 return B_DISPATCH_MESSAGE; 625 626 // TODO: remove this work-around! We can solve this by subclassing the 627 // slider classes, and start watching in their AttachedToWindow() method 628 if (!fRegistered) { 629 if (BMediaRoster* roster = BMediaRoster::CurrentRoster()) { 630 roster->StartWatching(fControl, fParameter.Web()->Node(), 631 B_MEDIA_NEW_PARAMETER_VALUE); 632 } 633 fRegistered = true; 634 } 635 636 if (message->what == kMsgParameterChanged) { 637 // update parameter from control 638 // TODO: support for response! 639 640 float value[fParameter.CountChannels()]; 641 642 if (BSlider *slider = dynamic_cast<BSlider *>(fControl)) { 643 value[0] = (float)(slider->Value() / 1000.0); 644 } else if (BChannelSlider *slider 645 = dynamic_cast<BChannelSlider *>(fControl)) { 646 for (int32 i = 0; i < fParameter.CountChannels(); i++) 647 value[i] = (float)(slider->ValueFor(i) / 1000.0); 648 } 649 650 TRACE("ContinuousMessageFilter::Filter: update view %s, %ld " 651 "channels\n", fControl->Name(), fParameter.CountChannels()); 652 653 if (fParameter.SetValue((void *)value, sizeof(value), 654 system_time()) < B_OK) { 655 ERROR("ContinuousMessageFilter::Filter: Could not set parameter " 656 "value for %p\n", &fParameter); 657 return B_DISPATCH_MESSAGE; 658 } 659 return B_SKIP_MESSAGE; 660 } 661 if (message->what == B_MEDIA_NEW_PARAMETER_VALUE) { 662 // update view from parameter -- if the message concerns us 663 const media_node* node; 664 int32 parameterID; 665 ssize_t size; 666 if (message->FindInt32("parameter", ¶meterID) != B_OK 667 || fParameter.ID() != parameterID 668 || message->FindData("node", B_RAW_TYPE, (const void**)&node, 669 &size) != B_OK 670 || fParameter.Web()->Node() != *node) 671 return B_DISPATCH_MESSAGE; 672 673 _UpdateControl(); 674 return B_SKIP_MESSAGE; 675 } 676 677 return B_DISPATCH_MESSAGE; 678 } 679 680 681 void 682 ContinuousMessageFilter::_UpdateControl() 683 { 684 // TODO: response support! 685 686 float value[fParameter.CountChannels()]; 687 size_t size = sizeof(value); 688 if (fParameter.GetValue((void *)&value, &size, NULL) < B_OK) { 689 ERROR("ContinuousMessageFilter: Could not get value for continuous " 690 "parameter %p (name '%s', node %d)\n", &fParameter, 691 fParameter.Name(), (int)fParameter.Web()->Node().node); 692 return; 693 } 694 695 if (BSlider *slider = dynamic_cast<BSlider *>(fControl)) { 696 slider->SetValue((int32) (1000 * value[0])); 697 slider->SetModificationMessage(new BMessage(kMsgParameterChanged)); 698 } else if (BChannelSlider *slider 699 = dynamic_cast<BChannelSlider *>(fControl)) { 700 for (int32 i = 0; i < fParameter.CountChannels(); i++) { 701 slider->SetValueFor(i, (int32) (1000 * value[i])); 702 } 703 } 704 } 705 706 707 // #pragma mark - 708 709 710 DiscreteMessageFilter::DiscreteMessageFilter(BControl *control, 711 BDiscreteParameter ¶meter) 712 : MessageFilter(), 713 fParameter(parameter) 714 { 715 // initialize view for us 716 control->SetMessage(new BMessage(kMsgParameterChanged)); 717 718 // set initial value 719 720 size_t size = sizeof(int32); 721 int32 value; 722 if (parameter.GetValue((void *)&value, &size, NULL) < B_OK) { 723 ERROR("DiscreteMessageFilter: Could not get value for discrete " 724 "parameter %p (name '%s', node %d)\n", ¶meter, 725 parameter.Name(), (int)(parameter.Web()->Node().node)); 726 return; 727 } 728 729 if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) { 730 checkBox->SetValue(value); 731 } else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) { 732 popUp->SelectOptionFor(value); 733 } else 734 ERROR("DiscreteMessageFilter: unknown discrete parameter view\n"); 735 } 736 737 738 DiscreteMessageFilter::~DiscreteMessageFilter() 739 { 740 } 741 742 743 filter_result 744 DiscreteMessageFilter::Filter(BMessage *message, BHandler **target) 745 { 746 BControl *control; 747 748 if (message->what != kMsgParameterChanged 749 || (control = dynamic_cast<BControl *>(*target)) == NULL) 750 return B_DISPATCH_MESSAGE; 751 752 // update view 753 754 int32 value = 0; 755 756 if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) { 757 value = checkBox->Value(); 758 } else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) { 759 popUp->SelectedOption(NULL, &value); 760 } 761 762 TRACE("DiscreteMessageFilter::Filter: update view %s, value = %ld\n", control->Name(), value); 763 764 if (fParameter.SetValue((void *)&value, sizeof(value), system_time()) < B_OK) { 765 ERROR("DiscreteMessageFilter::Filter: Could not set parameter value for %p\n", &fParameter); 766 return B_DISPATCH_MESSAGE; 767 } 768 769 return B_SKIP_MESSAGE; 770 } 771 772 773 // #pragma mark - 774 775 776 DefaultMediaTheme::DefaultMediaTheme() 777 : BMediaTheme("Haiku Theme", "Haiku built-in theme version 0.1") 778 { 779 CALLED(); 780 } 781 782 783 BControl * 784 DefaultMediaTheme::MakeControlFor(BParameter *parameter) 785 { 786 CALLED(); 787 788 BRect rect(0, 0, 150, 100); 789 return MakeViewFor(parameter, &rect); 790 } 791 792 793 BView * 794 DefaultMediaTheme::MakeViewFor(BParameterWeb *web, const BRect *hintRect) 795 { 796 CALLED(); 797 798 if (web == NULL) 799 return NULL; 800 801 BRect rect; 802 if (hintRect) 803 rect = *hintRect; 804 805 BRect bestRect; 806 807 // do we have more than one attached parameter group? 808 // if so, use a tabbed view with a tab for each group 809 810 TabView *tabView = NULL; 811 812 if (web->CountGroups() > 1) 813 tabView = new TabView(rect, "web"); 814 815 rect.OffsetTo(B_ORIGIN); 816 817 for (int32 i = 0; i < web->CountGroups(); i++) { 818 BParameterGroup *group = web->GroupAt(i); 819 if (group == NULL) 820 continue; 821 822 BView *groupView = MakeViewFor(*group, hintRect ? &rect : NULL); 823 if (groupView == NULL) 824 continue; 825 826 if (GroupView *view = dynamic_cast<GroupView *>(groupView)) { 827 // the top-level group views must not be larger than their hintRect, 828 // but unlike their children, they should follow all sides when 829 // their parent is resized 830 if (hintRect != NULL) 831 view->ResizeTo(rect.Width() - 10, rect.Height() - 10); 832 view->SetResizingMode(B_FOLLOW_ALL); 833 } 834 835 if (tabView == NULL) { 836 // if we don't need a container to put that view into, 837 // we're done here (but the groupView may span over the 838 // whole hintRect) 839 if (groupView->Frame().LeftTop() == BPoint(5, 5)) { 840 // remove insets, as they are not needed 841 groupView->MoveBy(-5, -5); 842 groupView->ResizeBy(10, 10); 843 } 844 845 return new DynamicScrollView(groupView->Name(), groupView); 846 } 847 848 DynamicScrollView *scrollView = new DynamicScrollView(groupView->Name(), groupView); 849 tabView->AddTab(scrollView); 850 851 if (!hintRect) { 852 bestRect = bestRect | scrollView->Bounds(); 853 } 854 } 855 856 if (tabView != NULL) { 857 // this adjustment must be kept in sync with TabView::FrameResized 858 bestRect.bottom += tabView->TabHeight(); 859 bestRect.InsetBy(-3.0,-3.0); 860 861 tabView->ResizeTo(bestRect.Width(), bestRect.Height()); 862 tabView->FrameResized(bestRect.Width(), bestRect.Height()); 863 //needed since we're not attached to a window yet 864 } 865 866 return tabView; 867 } 868 869 870 BView * 871 DefaultMediaTheme::MakeViewFor(BParameterGroup& group, const BRect* hintRect) 872 { 873 CALLED(); 874 875 if (group.Flags() & B_HIDDEN_PARAMETER) 876 return NULL; 877 878 BRect rect; 879 if (hintRect != NULL) 880 rect = *hintRect; 881 882 GroupView *view = new GroupView(rect, group.Name()); 883 884 // Create the parameter views - but don't add them yet 885 886 rect.OffsetTo(B_ORIGIN); 887 rect.InsetBySelf(5, 5); 888 889 BList views; 890 for (int32 i = 0; i < group.CountParameters(); i++) { 891 BParameter *parameter = group.ParameterAt(i); 892 if (parameter == NULL) 893 continue; 894 895 BView *parameterView = MakeSelfHostingViewFor(*parameter, 896 hintRect ? &rect : NULL); 897 if (parameterView == NULL) 898 continue; 899 900 parameterView->SetViewColor(view->ViewColor()); 901 // ToDo: dunno why this is needed, but the controls 902 // sometimes (!) have a white background without it 903 904 views.AddItem(parameterView); 905 } 906 907 // Identify a title view, and add it at the top if present 908 909 TitleView *titleView = dynamic_cast<TitleView *>((BView *)views.ItemAt(0)); 910 if (titleView != NULL) { 911 view->AddChild(titleView); 912 rect.OffsetBy(0, titleView->Bounds().Height()); 913 } 914 915 // Add the sub-group views 916 917 rect.right = rect.left + 20; 918 rect.bottom = rect.top + 20; 919 float lastHeight = 0; 920 921 for (int32 i = 0; i < group.CountGroups(); i++) { 922 BParameterGroup *subGroup = group.GroupAt(i); 923 if (subGroup == NULL) 924 continue; 925 926 BView *groupView = MakeViewFor(*subGroup, &rect); 927 if (groupView == NULL) 928 continue; 929 930 if (i > 0) { 931 // add separator view 932 BRect separatorRect(groupView->Frame()); 933 separatorRect.left -= 3; 934 separatorRect.right = separatorRect.left + 1; 935 if (lastHeight > separatorRect.Height()) 936 separatorRect.bottom = separatorRect.top + lastHeight; 937 938 view->AddChild(new SeparatorView(separatorRect)); 939 } 940 941 view->AddChild(groupView); 942 943 rect.OffsetBy(groupView->Bounds().Width() + 5, 0); 944 945 lastHeight = groupView->Bounds().Height(); 946 if (lastHeight > rect.Height()) 947 rect.bottom = rect.top + lastHeight - 1; 948 } 949 950 view->ResizeTo(rect.left + 10, rect.bottom + 5); 951 view->SetContentBounds(view->Bounds()); 952 953 if (group.CountParameters() == 0) 954 return view; 955 956 // add the parameter views part of the group 957 958 if (group.CountGroups() > 0) { 959 rect.top = rect.bottom + 10; 960 rect.bottom = rect.top + 20; 961 } 962 963 bool center = false; 964 965 for (int32 i = 0; i < views.CountItems(); i++) { 966 BView *parameterView = static_cast<BView *>(views.ItemAt(i)); 967 968 if (parameterView->Bounds().Width() + 5 > rect.Width()) 969 rect.right = parameterView->Bounds().Width() + rect.left + 5; 970 971 // we don't need to add the title view again 972 if (parameterView == titleView) 973 continue; 974 975 // if there is a BChannelSlider (ToDo: or any vertical slider?) 976 // the views will be centered 977 if (dynamic_cast<BChannelSlider *>(parameterView) != NULL) 978 center = true; 979 980 parameterView->MoveTo(parameterView->Frame().left, rect.top); 981 view->AddChild(parameterView); 982 983 rect.OffsetBy(0, parameterView->Bounds().Height() + 5); 984 } 985 986 if (views.CountItems() > (titleView != NULL ? 1 : 0)) 987 view->ResizeTo(rect.right + 5, rect.top + 5); 988 989 // center the parameter views if needed, and tweak some views 990 991 float width = view->Bounds().Width(); 992 993 for (int32 i = 0; i < views.CountItems(); i++) { 994 BView *subView = static_cast<BView *>(views.ItemAt(i)); 995 BRect frame = subView->Frame(); 996 997 if (center) 998 subView->MoveTo((width - frame.Width()) / 2, frame.top); 999 else { 1000 // tweak the PopUp views to look better 1001 if (dynamic_cast<BOptionPopUp *>(subView) != NULL) 1002 subView->ResizeTo(width, frame.Height()); 1003 } 1004 } 1005 1006 view->SetContentBounds(view->Bounds()); 1007 return view; 1008 } 1009 1010 1011 /*! This creates a view that handles all incoming messages itself - that's 1012 what is meant with self-hosting. 1013 */ 1014 BView * 1015 DefaultMediaTheme::MakeSelfHostingViewFor(BParameter& parameter, 1016 const BRect* hintRect) 1017 { 1018 if (parameter.Flags() & B_HIDDEN_PARAMETER 1019 || parameter_should_be_hidden(parameter)) 1020 return NULL; 1021 1022 BView *view = MakeViewFor(¶meter, hintRect); 1023 if (view == NULL) { 1024 // The MakeViewFor() method above returns a BControl - which we 1025 // don't need for a null parameter; that's why it returns NULL. 1026 // But we want to see something anyway, so we add a string view 1027 // here. 1028 if (parameter.Type() == BParameter::B_NULL_PARAMETER) { 1029 if (parameter.Group()->ParameterAt(0) == ¶meter) { 1030 // this is the first parameter in this group, so 1031 // let's use a nice title view 1032 1033 TitleView *titleView = new TitleView(BRect(0, 0, 10, 10), parameter.Name()); 1034 titleView->ResizeToPreferred(); 1035 1036 return titleView; 1037 } 1038 BStringView *stringView = new BStringView(BRect(0, 0, 10, 10), 1039 parameter.Name(), parameter.Name()); 1040 stringView->SetAlignment(B_ALIGN_CENTER); 1041 stringView->ResizeToPreferred(); 1042 1043 return stringView; 1044 } 1045 1046 return NULL; 1047 } 1048 1049 MessageFilter *filter = MessageFilter::FilterFor(view, parameter); 1050 if (filter != NULL) 1051 view->AddFilter(filter); 1052 1053 return view; 1054 } 1055 1056 1057 BControl * 1058 DefaultMediaTheme::MakeViewFor(BParameter *parameter, const BRect *hintRect) 1059 { 1060 BRect rect; 1061 if (hintRect) 1062 rect = *hintRect; 1063 else 1064 rect.Set(0, 0, 50, 100); 1065 1066 switch (parameter->Type()) { 1067 case BParameter::B_NULL_PARAMETER: 1068 // there is no default view for a null parameter 1069 return NULL; 1070 1071 case BParameter::B_DISCRETE_PARAMETER: 1072 { 1073 BDiscreteParameter &discrete = static_cast<BDiscreteParameter &>(*parameter); 1074 1075 if (!strcmp(discrete.Kind(), B_ENABLE) 1076 || !strcmp(discrete.Kind(), B_MUTE) 1077 || discrete.CountItems() == 0) { 1078 // create a checkbox item 1079 1080 BCheckBox *checkBox = new BCheckBox(rect, discrete.Name(), 1081 discrete.Name(), NULL); 1082 checkBox->ResizeToPreferred(); 1083 1084 return checkBox; 1085 } else { 1086 // create a pop up menu field 1087 1088 // ToDo: replace BOptionPopUp (or fix it in Haiku...) 1089 // this is a workaround for a bug in BOptionPopUp - you need to 1090 // know the actual width before creating the object - very nice... 1091 1092 BFont font; 1093 float width = 0; 1094 for (int32 i = 0; i < discrete.CountItems(); i++) { 1095 float labelWidth = font.StringWidth(discrete.ItemNameAt(i)); 1096 if (labelWidth > width) 1097 width = labelWidth; 1098 } 1099 width += font.StringWidth(discrete.Name()) + 55; 1100 rect.right = rect.left + width; 1101 1102 BOptionPopUp *popUp = new BOptionPopUp(rect, discrete.Name(), 1103 discrete.Name(), NULL); 1104 1105 for (int32 i = 0; i < discrete.CountItems(); i++) { 1106 popUp->AddOption(discrete.ItemNameAt(i), discrete.ItemValueAt(i)); 1107 } 1108 1109 popUp->ResizeToPreferred(); 1110 1111 return popUp; 1112 } 1113 } 1114 1115 case BParameter::B_CONTINUOUS_PARAMETER: 1116 { 1117 BContinuousParameter &continuous = static_cast<BContinuousParameter &>(*parameter); 1118 1119 if (!strcmp(continuous.Kind(), B_MASTER_GAIN) 1120 || !strcmp(continuous.Kind(), B_GAIN)) { 1121 BChannelSlider *slider = new BChannelSlider(rect, continuous.Name(), 1122 continuous.Name(), NULL, B_VERTICAL, continuous.CountChannels()); 1123 1124 char minLabel[64], maxLabel[64]; 1125 1126 const char *unit = continuous.Unit(); 1127 if (unit[0]) { 1128 // if we have a unit, print it next to the limit values 1129 sprintf(minLabel, "%g %s", continuous.MinValue(), continuous.Unit()); 1130 sprintf(maxLabel, "%g %s", continuous.MaxValue(), continuous.Unit()); 1131 } else { 1132 sprintf(minLabel, "%g", continuous.MinValue()); 1133 sprintf(maxLabel, "%g", continuous.MaxValue()); 1134 } 1135 slider->SetLimitLabels(minLabel, maxLabel); 1136 1137 float width, height; 1138 slider->GetPreferredSize(&width, &height); 1139 slider->ResizeTo(width, 190); 1140 1141 // ToDo: take BContinuousParameter::GetResponse() & ValueStep() into account! 1142 1143 for (int32 i = 0; i < continuous.CountChannels(); i++) { 1144 slider->SetLimitsFor(i, int32(continuous.MinValue() * 1000), 1145 int32(continuous.MaxValue() * 1000)); 1146 } 1147 1148 return slider; 1149 } 1150 1151 BSlider *slider = new BSlider(rect, parameter->Name(), parameter->Name(), 1152 NULL, 0, 100); 1153 1154 float width, height; 1155 slider->GetPreferredSize(&width, &height); 1156 slider->ResizeTo(100, height); 1157 1158 return slider; 1159 } 1160 1161 default: 1162 ERROR("BMediaTheme: Don't know parameter type: 0x%x\n", 1163 parameter->Type()); 1164 } 1165 return NULL; 1166 } 1167 1168