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.top += TabHeight(); 393 rect.InsetBy(3.0f, 3.0f); 394 //ContainerView is inseted by 3.0 in BTabView::_InitObject() 395 396 ContainerView()->ResizeTo(rect.Width(), rect.Height()); 397 } 398 399 400 void 401 TabView::Select(int32 tab) 402 { 403 BTabView::Select(tab); 404 405 BView *view = ViewForTab(Selection()); 406 if (view != NULL) { 407 BRect rect = ContainerView()->Bounds(); 408 view->ResizeTo(rect.Width(), rect.Height()); 409 } 410 } 411 412 413 // #pragma mark - 414 415 416 SeparatorView::SeparatorView(BRect frame) 417 : BView(frame, "-", B_FOLLOW_NONE, B_WILL_DRAW) 418 { 419 fVertical = frame.Width() < frame.Height(); 420 SetViewColor(B_TRANSPARENT_COLOR); 421 } 422 423 424 SeparatorView::~SeparatorView() 425 { 426 } 427 428 429 void 430 SeparatorView::Draw(BRect updateRect) 431 { 432 rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR); 433 BRect rect = updateRect & Bounds(); 434 435 SetHighColor(tint_color(color, B_DARKEN_1_TINT)); 436 if (fVertical) 437 StrokeLine(BPoint(0, rect.top), BPoint(0, rect.bottom)); 438 else 439 StrokeLine(BPoint(rect.left, 0), BPoint(rect.right, 0)); 440 441 SetHighColor(tint_color(color, B_LIGHTEN_1_TINT)); 442 if (fVertical) 443 StrokeLine(BPoint(1, rect.top), BPoint(1, rect.bottom)); 444 else 445 StrokeLine(BPoint(rect.left, 1), BPoint(rect.right, 1)); 446 } 447 448 449 // #pragma mark - 450 451 452 TitleView::TitleView(BRect frame, const char *title) 453 : BView(frame, title, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW) 454 { 455 fTitle = strdup(title); 456 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 457 SetLowColor(ViewColor()); 458 } 459 460 461 TitleView::~TitleView() 462 { 463 free((char *)fTitle); 464 } 465 466 467 void 468 TitleView::Draw(BRect updateRect) 469 { 470 BRect rect(Bounds()); 471 472 SetDrawingMode(B_OP_COPY); 473 SetHighColor(240, 240, 240); 474 DrawString(fTitle, BPoint(rect.left + 1, rect.bottom - 9)); 475 476 SetDrawingMode(B_OP_OVER); 477 SetHighColor(80, 20, 20); 478 DrawString(fTitle, BPoint(rect.left, rect.bottom - 8)); 479 } 480 481 482 void 483 TitleView::GetPreferredSize(float *_width, float *_height) 484 { 485 if (_width) 486 *_width = StringWidth(fTitle) + 2; 487 488 if (_height) { 489 font_height fontHeight; 490 GetFontHeight(&fontHeight); 491 492 *_height = fontHeight.ascent + fontHeight.descent + fontHeight.leading 493 + 8; 494 } 495 } 496 497 498 // #pragma mark - 499 500 501 MessageFilter::MessageFilter() 502 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE) 503 { 504 } 505 506 507 MessageFilter * 508 MessageFilter::FilterFor(BView *view, BParameter ¶meter) 509 { 510 BControl *control = dynamic_cast<BControl *>(view); 511 if (control == NULL) 512 return NULL; 513 514 switch (parameter.Type()) { 515 case BParameter::B_CONTINUOUS_PARAMETER: 516 return new ContinuousMessageFilter(control, 517 static_cast<BContinuousParameter &>(parameter)); 518 519 case BParameter::B_DISCRETE_PARAMETER: 520 return new DiscreteMessageFilter(control, 521 static_cast<BDiscreteParameter &>(parameter)); 522 523 case BParameter::B_NULL_PARAMETER: /* fall through */ 524 default: 525 return NULL; 526 } 527 } 528 529 530 // #pragma mark - 531 532 533 ContinuousMessageFilter::ContinuousMessageFilter(BControl *control, 534 BContinuousParameter ¶meter) 535 : MessageFilter(), 536 fParameter(parameter) 537 { 538 // initialize view for us 539 control->SetMessage(new BMessage(kMsgParameterChanged)); 540 541 // set initial value 542 // ToDo: response support! 543 544 float value[fParameter.CountChannels()]; 545 size_t size = sizeof(value); 546 if (parameter.GetValue((void *)&value, &size, NULL) < B_OK) { 547 ERROR("ContinuousMessageFilter: Could not get value for continuous parameter %p (name '%s', node %d)\n", ¶meter, parameter.Name(), parameter.Web()->Node().node); 548 return; 549 } 550 551 if (BSlider *slider = dynamic_cast<BSlider *>(control)) { 552 slider->SetValue((int32) (1000 * value[0])); 553 slider->SetModificationMessage(new BMessage(kMsgParameterChanged)); 554 } else if (BChannelSlider *slider = dynamic_cast<BChannelSlider *>(control)) { 555 for (int32 i = 0; i < fParameter.CountChannels(); i++) 556 slider->SetValueFor(i, (int32) (1000 * value[i])); 557 558 slider->SetModificationMessage(new BMessage(kMsgParameterChanged)); 559 } else 560 ERROR("ContinuousMessageFilter: unknown continuous parameter view\n"); 561 } 562 563 564 ContinuousMessageFilter::~ContinuousMessageFilter() 565 { 566 } 567 568 569 filter_result 570 ContinuousMessageFilter::Filter(BMessage *message, BHandler **target) 571 { 572 BControl *control; 573 574 if (message->what != kMsgParameterChanged 575 || (control = dynamic_cast<BControl *>(*target)) == NULL) 576 return B_DISPATCH_MESSAGE; 577 578 // update view 579 // ToDo: support for response! 580 581 float value[fParameter.CountChannels()]; 582 583 if (BSlider *slider = dynamic_cast<BSlider *>(control)) { 584 value[0] = (float)(slider->Value() / 1000.0); 585 } else if (BChannelSlider *slider = dynamic_cast<BChannelSlider *>(control)) { 586 for (int32 i = 0; i < fParameter.CountChannels(); i++) 587 value[i] = (float)(slider->ValueFor(i) / 1000.0); 588 } 589 590 TRACE("ContinuousMessageFilter::Filter: update view %s, %ld channels\n", control->Name(), fParameter.CountChannels()); 591 592 if (fParameter.SetValue((void *)value, sizeof(value), system_time()) < B_OK) { 593 ERROR("ContinuousMessageFilter::Filter: Could not set parameter value for %p\n", &fParameter); 594 return B_DISPATCH_MESSAGE; 595 } 596 597 return B_SKIP_MESSAGE; 598 } 599 600 601 // #pragma mark - 602 603 604 DiscreteMessageFilter::DiscreteMessageFilter(BControl *control, 605 BDiscreteParameter ¶meter) 606 : MessageFilter(), 607 fParameter(parameter) 608 { 609 // initialize view for us 610 control->SetMessage(new BMessage(kMsgParameterChanged)); 611 612 // set initial value 613 614 size_t size = sizeof(int32); 615 int32 value; 616 if (parameter.GetValue((void *)&value, &size, NULL) < B_OK) { 617 ERROR("DiscreteMessageFilter: Could not get value for discrete parameter %p (name '%s', node %d)\n", ¶meter, parameter.Name(), parameter.Web()->Node().node); 618 return; 619 } 620 621 if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) { 622 checkBox->SetValue(value); 623 } else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) { 624 popUp->SelectOptionFor(value); 625 } else 626 ERROR("DiscreteMessageFilter: unknown discrete parameter view\n"); 627 } 628 629 630 DiscreteMessageFilter::~DiscreteMessageFilter() 631 { 632 } 633 634 635 filter_result 636 DiscreteMessageFilter::Filter(BMessage *message, BHandler **target) 637 { 638 BControl *control; 639 640 if (message->what != kMsgParameterChanged 641 || (control = dynamic_cast<BControl *>(*target)) == NULL) 642 return B_DISPATCH_MESSAGE; 643 644 // update view 645 646 int32 value = 0; 647 648 if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) { 649 value = checkBox->Value(); 650 } else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) { 651 popUp->SelectedOption(NULL, &value); 652 } 653 654 TRACE("DiscreteMessageFilter::Filter: update view %s, value = %ld\n", control->Name(), value); 655 656 if (fParameter.SetValue((void *)&value, sizeof(value), system_time()) < B_OK) { 657 ERROR("DiscreteMessageFilter::Filter: Could not set parameter value for %p\n", &fParameter); 658 return B_DISPATCH_MESSAGE; 659 } 660 661 return B_SKIP_MESSAGE; 662 } 663 664 665 // #pragma mark - 666 667 668 DefaultMediaTheme::DefaultMediaTheme() 669 : BMediaTheme("Haiku Theme", "Haiku built-in theme version 0.1") 670 { 671 CALLED(); 672 } 673 674 675 BControl * 676 DefaultMediaTheme::MakeControlFor(BParameter *parameter) 677 { 678 CALLED(); 679 680 BRect rect(0, 0, 150, 100); 681 return MakeViewFor(parameter, &rect); 682 } 683 684 685 BView * 686 DefaultMediaTheme::MakeViewFor(BParameterWeb *web, const BRect *hintRect) 687 { 688 CALLED(); 689 690 if (web == NULL) 691 return NULL; 692 693 BRect rect; 694 if (hintRect) 695 rect = *hintRect; 696 else 697 rect.Set(0, 0, 80, 100); 698 699 // do we have more than one attached parameter group? 700 // if so, use a tabbed view with a tab for each group 701 702 TabView *tabView = NULL; 703 704 if (web->CountGroups() > 1) 705 tabView = new TabView(rect, "web"); 706 707 rect.OffsetTo(B_ORIGIN); 708 709 for (int32 i = 0; i < web->CountGroups(); i++) { 710 BParameterGroup *group = web->GroupAt(i); 711 if (group == NULL) 712 continue; 713 714 BView *groupView = MakeViewFor(*group, hintRect ? &rect : NULL); 715 if (groupView == NULL) 716 continue; 717 718 if (GroupView *view = dynamic_cast<GroupView *>(groupView)) { 719 // the top-level group views must not be larger than their hintRect, 720 // but unlike their children, they should follow all sides when 721 // their parent is resized 722 if (hintRect != NULL) 723 view->ResizeTo(rect.Width() - 10, rect.Height() - 10); 724 view->SetResizingMode(B_FOLLOW_ALL); 725 } 726 727 if (tabView == NULL) { 728 // if we don't need a container to put that view into, 729 // we're done here (but the groupView may span over the 730 // whole hintRect) 731 groupView->MoveBy(-5, -5); 732 groupView->ResizeBy(10, 10); 733 734 return new DynamicScrollView(groupView->Name(), groupView); 735 } 736 737 tabView->AddTab(new DynamicScrollView(groupView->Name(), groupView)); 738 } 739 740 return tabView; 741 } 742 743 744 BView * 745 DefaultMediaTheme::MakeViewFor(BParameterGroup& group, const BRect* hintRect) 746 { 747 CALLED(); 748 749 if (group.Flags() & B_HIDDEN_PARAMETER) 750 return NULL; 751 752 BRect rect; 753 if (hintRect != NULL) 754 rect = *hintRect; 755 756 GroupView *view = new GroupView(rect, group.Name()); 757 758 // Create the parameter views - but don't add them yet 759 760 rect.OffsetTo(B_ORIGIN); 761 rect.InsetBySelf(5, 5); 762 763 BList views; 764 for (int32 i = 0; i < group.CountParameters(); i++) { 765 BParameter *parameter = group.ParameterAt(i); 766 if (parameter == NULL) 767 continue; 768 769 BView *parameterView = MakeSelfHostingViewFor(*parameter, 770 hintRect ? &rect : NULL); 771 if (parameterView == NULL) 772 continue; 773 774 parameterView->SetViewColor(view->ViewColor()); 775 // ToDo: dunno why this is needed, but the controls 776 // sometimes (!) have a white background without it 777 778 views.AddItem(parameterView); 779 } 780 781 // Identify a title view, and add it at the top if present 782 783 TitleView *titleView = dynamic_cast<TitleView *>((BView *)views.ItemAt(0)); 784 if (titleView != NULL) { 785 view->AddChild(titleView); 786 rect.OffsetBy(0, titleView->Bounds().Height()); 787 } 788 789 // Add the sub-group views 790 791 rect.right = rect.left + 20; 792 rect.bottom = rect.top + 20; 793 float lastHeight = 0; 794 795 for (int32 i = 0; i < group.CountGroups(); i++) { 796 BParameterGroup *subGroup = group.GroupAt(i); 797 if (subGroup == NULL) 798 continue; 799 800 BView *groupView = MakeViewFor(*subGroup, &rect); 801 if (groupView == NULL) 802 continue; 803 804 if (i > 0) { 805 // add separator view 806 BRect separatorRect(groupView->Frame()); 807 separatorRect.left -= 3; 808 separatorRect.right = separatorRect.left + 1; 809 if (lastHeight > separatorRect.Height()) 810 separatorRect.bottom = separatorRect.top + lastHeight; 811 812 view->AddChild(new SeparatorView(separatorRect)); 813 } 814 815 view->AddChild(groupView); 816 817 rect.OffsetBy(groupView->Bounds().Width() + 5, 0); 818 819 lastHeight = groupView->Bounds().Height(); 820 if (lastHeight > rect.Height()) 821 rect.bottom = rect.top + lastHeight - 1; 822 } 823 824 view->ResizeTo(rect.left + 10, rect.bottom + 5); 825 view->SetContentBounds(view->Bounds()); 826 827 if (group.CountParameters() == 0) 828 return view; 829 830 // add the parameter views part of the group 831 832 if (group.CountGroups() > 0) { 833 rect.top = rect.bottom + 10; 834 rect.bottom = rect.top + 20; 835 } 836 837 bool center = false; 838 839 for (int32 i = 0; i < views.CountItems(); i++) { 840 BView *parameterView = static_cast<BView *>(views.ItemAt(i)); 841 842 if (parameterView->Bounds().Width() + 5 > rect.Width()) 843 rect.right = parameterView->Bounds().Width() + rect.left + 5; 844 845 // we don't need to add the title view again 846 if (parameterView == titleView) 847 continue; 848 849 // if there is a BChannelSlider (ToDo: or any vertical slider?) 850 // the views will be centered 851 if (dynamic_cast<BChannelSlider *>(parameterView) != NULL) 852 center = true; 853 854 parameterView->MoveTo(parameterView->Frame().left, rect.top); 855 view->AddChild(parameterView); 856 857 rect.OffsetBy(0, parameterView->Bounds().Height() + 5); 858 } 859 860 if (views.CountItems() > (titleView != NULL ? 1 : 0)) 861 view->ResizeTo(rect.right + 5, rect.top + 5); 862 863 // center the parameter views if needed, and tweak some views 864 865 float width = view->Bounds().Width(); 866 867 for (int32 i = 0; i < views.CountItems(); i++) { 868 BView *subView = static_cast<BView *>(views.ItemAt(i)); 869 BRect frame = subView->Frame(); 870 871 if (center) 872 subView->MoveTo((width - frame.Width()) / 2, frame.top); 873 else { 874 // tweak the PopUp views to look better 875 if (dynamic_cast<BOptionPopUp *>(subView) != NULL) 876 subView->ResizeTo(width, frame.Height()); 877 } 878 } 879 880 view->SetContentBounds(view->Bounds()); 881 return view; 882 } 883 884 885 /*! This creates a view that handles all incoming messages itself - that's 886 what is meant with self-hosting. 887 */ 888 BView * 889 DefaultMediaTheme::MakeSelfHostingViewFor(BParameter& parameter, 890 const BRect* hintRect) 891 { 892 if (parameter.Flags() & B_HIDDEN_PARAMETER 893 || parameter_should_be_hidden(parameter)) 894 return NULL; 895 896 BView *view = MakeViewFor(¶meter, hintRect); 897 if (view == NULL) { 898 // The MakeViewFor() method above returns a BControl - which we 899 // don't need for a null parameter; that's why it returns NULL. 900 // But we want to see something anyway, so we add a string view 901 // here. 902 if (parameter.Type() == BParameter::B_NULL_PARAMETER) { 903 if (parameter.Group()->ParameterAt(0) == ¶meter) { 904 // this is the first parameter in this group, so 905 // let's use a nice title view 906 907 TitleView *titleView = new TitleView(BRect(0, 0, 10, 10), parameter.Name()); 908 titleView->ResizeToPreferred(); 909 910 return titleView; 911 } 912 BStringView *stringView = new BStringView(BRect(0, 0, 10, 10), 913 parameter.Name(), parameter.Name()); 914 stringView->SetAlignment(B_ALIGN_CENTER); 915 stringView->ResizeToPreferred(); 916 917 return stringView; 918 } 919 920 return NULL; 921 } 922 923 MessageFilter *filter = MessageFilter::FilterFor(view, parameter); 924 if (filter != NULL) 925 view->AddFilter(filter); 926 927 return view; 928 } 929 930 931 BControl * 932 DefaultMediaTheme::MakeViewFor(BParameter *parameter, const BRect *hintRect) 933 { 934 BRect rect; 935 if (hintRect) 936 rect = *hintRect; 937 else 938 rect.Set(0, 0, 50, 100); 939 940 switch (parameter->Type()) { 941 case BParameter::B_NULL_PARAMETER: 942 // there is no default view for a null parameter 943 return NULL; 944 945 case BParameter::B_DISCRETE_PARAMETER: 946 { 947 BDiscreteParameter &discrete = static_cast<BDiscreteParameter &>(*parameter); 948 949 if (!strcmp(discrete.Kind(), B_ENABLE) 950 || !strcmp(discrete.Kind(), B_MUTE) 951 || discrete.CountItems() == 0) { 952 // create a checkbox item 953 954 BCheckBox *checkBox = new BCheckBox(rect, discrete.Name(), 955 discrete.Name(), NULL); 956 checkBox->ResizeToPreferred(); 957 958 return checkBox; 959 } else { 960 // create a pop up menu field 961 962 // ToDo: replace BOptionPopUp (or fix it in Haiku...) 963 // this is a workaround for a bug in BOptionPopUp - you need to 964 // know the actual width before creating the object - very nice... 965 966 BFont font; 967 float width = 0; 968 for (int32 i = 0; i < discrete.CountItems(); i++) { 969 float labelWidth = font.StringWidth(discrete.ItemNameAt(i)); 970 if (labelWidth > width) 971 width = labelWidth; 972 } 973 width += font.StringWidth(discrete.Name()) + 55; 974 rect.right = rect.left + width; 975 976 BOptionPopUp *popUp = new BOptionPopUp(rect, discrete.Name(), 977 discrete.Name(), NULL); 978 979 for (int32 i = 0; i < discrete.CountItems(); i++) { 980 popUp->AddOption(discrete.ItemNameAt(i), discrete.ItemValueAt(i)); 981 } 982 983 popUp->ResizeToPreferred(); 984 985 return popUp; 986 } 987 } 988 989 case BParameter::B_CONTINUOUS_PARAMETER: 990 { 991 BContinuousParameter &continuous = static_cast<BContinuousParameter &>(*parameter); 992 993 if (!strcmp(continuous.Kind(), B_MASTER_GAIN) 994 || !strcmp(continuous.Kind(), B_GAIN)) { 995 BChannelSlider *slider = new BChannelSlider(rect, continuous.Name(), 996 continuous.Name(), NULL, B_VERTICAL, continuous.CountChannels()); 997 998 char minLabel[64], maxLabel[64]; 999 1000 const char *unit = continuous.Unit(); 1001 if (unit[0]) { 1002 // if we have a unit, print it next to the limit values 1003 sprintf(minLabel, "%g %s", continuous.MinValue(), continuous.Unit()); 1004 sprintf(maxLabel, "%g %s", continuous.MaxValue(), continuous.Unit()); 1005 } else { 1006 sprintf(minLabel, "%g", continuous.MinValue()); 1007 sprintf(maxLabel, "%g", continuous.MaxValue()); 1008 } 1009 slider->SetLimitLabels(minLabel, maxLabel); 1010 1011 float width, height; 1012 slider->GetPreferredSize(&width, &height); 1013 slider->ResizeTo(width, 190); 1014 1015 // ToDo: take BContinuousParameter::GetResponse() & ValueStep() into account! 1016 1017 for (int32 i = 0; i < continuous.CountChannels(); i++) { 1018 slider->SetLimitsFor(i, int32(continuous.MinValue() * 1000), 1019 int32(continuous.MaxValue() * 1000)); 1020 } 1021 1022 return slider; 1023 } 1024 1025 BSlider *slider = new BSlider(rect, parameter->Name(), parameter->Name(), 1026 NULL, 0, 100); 1027 1028 float width, height; 1029 slider->GetPreferredSize(&width, &height); 1030 slider->ResizeTo(100, height); 1031 1032 return slider; 1033 } 1034 1035 default: 1036 ERROR("BMediaTheme: Don't know parameter type: 0x%lx\n", parameter->Type()); 1037 } 1038 return NULL; 1039 } 1040 1041