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