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