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