headers/bsd: Add sys/queue.h.
[haiku.git] / src / kits / media / DefaultMediaTheme.cpp
blobfbb15aa2cf2efa483fa4f8545be746a1c830ff9e
1 /*
2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "DefaultMediaTheme.h"
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>
25 #include "debug.h"
28 using namespace BPrivate;
31 namespace BPrivate {
33 class DynamicScrollView : public BView {
34 public:
35 DynamicScrollView(const char *name, BView *target);
36 virtual ~DynamicScrollView();
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);
43 void SetContentBounds(BRect bounds);
44 BRect ContentBounds() const { return fContentBounds; }
46 private:
47 void UpdateBars();
49 BScrollBar *fHorizontalScrollBar, *fVerticalScrollBar;
50 BRect fContentBounds;
51 BView *fTarget;
52 bool fIsDocumentScroller;
55 class GroupView : public BView {
56 public:
57 GroupView(BRect frame, const char *name);
58 virtual ~GroupView();
60 virtual void AttachedToWindow();
61 virtual void AllAttached();
62 virtual void GetPreferredSize(float *_width, float *_height);
64 virtual BSize MinSize();
65 virtual BSize MaxSize();
66 virtual BSize PreferredSize();
68 void SetContentBounds(BRect bounds);
69 BRect ContentBounds() const { return fContentBounds; }
71 private:
72 BRect fContentBounds;
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);
81 virtual void FrameResized(float width, float height);
82 virtual void Select(int32 tab);
85 class SeparatorView : public BView {
86 public:
87 SeparatorView(BRect frame);
88 virtual ~SeparatorView();
90 virtual void Draw(BRect updateRect);
92 private:
93 bool fVertical;
96 class TitleView : public BView {
97 public:
98 TitleView(BRect frame, const char *title);
99 virtual ~TitleView();
101 virtual void Draw(BRect updateRect);
102 virtual void GetPreferredSize(float *width, float *height);
104 private:
105 const char *fTitle;
108 class CheckBox : public BCheckBox {
109 public:
110 CheckBox(BRect area, const char* name, const char* label,
111 BDiscreteParameter &parameter);
112 virtual ~CheckBox();
114 virtual void AttachedToWindow();
115 virtual void DetachedFromWindow();
116 private:
117 BDiscreteParameter &fParameter;
120 class OptionPopUp : public BOptionPopUp {
121 public:
122 OptionPopUp(BRect area, const char* name, const char* label,
123 BDiscreteParameter &parameter);
124 virtual ~OptionPopUp();
126 virtual void AttachedToWindow();
127 virtual void DetachedFromWindow();
128 private:
129 BDiscreteParameter &fParameter;
132 class Slider : public BSlider {
133 public:
134 Slider(BRect area, const char* name, const char*label, int32 minValue,
135 int32 maxValue, BContinuousParameter &parameter);
136 virtual ~Slider();
138 virtual void AttachedToWindow();
139 virtual void DetachedFromWindow();
140 private:
141 BContinuousParameter &fParameter;
144 class ChannelSlider : public BChannelSlider {
145 public:
146 ChannelSlider(BRect area, const char* name, const char* label,
147 orientation orientation, int32 channels,
148 BContinuousParameter &parameter);
149 virtual ~ChannelSlider();
151 virtual void AttachedToWindow();
152 virtual void DetachedFromWindow();
153 private:
154 BContinuousParameter &fParameter;
157 class MessageFilter : public BMessageFilter {
158 public:
159 static MessageFilter *FilterFor(BView *view, BParameter &parameter);
161 protected:
162 MessageFilter();
165 class ContinuousMessageFilter : public MessageFilter {
166 public:
167 ContinuousMessageFilter(BControl *control,
168 BContinuousParameter &parameter);
169 virtual ~ContinuousMessageFilter();
171 virtual filter_result Filter(BMessage *message, BHandler **target);
173 private:
174 void _UpdateControl();
176 BControl *fControl;
177 BContinuousParameter &fParameter;
180 class DiscreteMessageFilter : public MessageFilter {
181 public:
182 DiscreteMessageFilter(BControl *control, BDiscreteParameter &parameter);
183 virtual ~DiscreteMessageFilter();
185 virtual filter_result Filter(BMessage *message, BHandler **target);
187 private:
188 BDiscreteParameter &fParameter;
191 } // namespace BPrivate
194 const uint32 kMsgParameterChanged = '_mPC';
197 static bool
198 parameter_should_be_hidden(BParameter &parameter)
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;
207 for (int32 i = 0; i < parameter.CountOutputs(); i++) {
208 if (!strcmp(parameter.OutputAt(0)->Kind(), B_INPUT_MUX))
209 return true;
212 return false;
216 static void
217 start_watching_for_parameter_changes(BControl* control, BParameter &parameter)
219 BMediaRoster* roster = BMediaRoster::CurrentRoster();
220 if (roster != NULL) {
221 roster->StartWatching(control, parameter.Web()->Node(),
222 B_MEDIA_NEW_PARAMETER_VALUE);
227 static void
228 stop_watching_for_parameter_changes(BControl* control, BParameter &parameter)
230 BMediaRoster* roster = BMediaRoster::CurrentRoster();
231 if (roster != NULL) {
232 roster->StopWatching(control, parameter.Web()->Node(),
233 B_MEDIA_NEW_PARAMETER_VALUE);
237 // #pragma mark -
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)
247 fContentBounds.Set(-1, -1, -1, -1);
248 AdoptViewColors(fTarget);
249 target->MoveTo(B_ORIGIN);
250 AddChild(target);
254 DynamicScrollView::~DynamicScrollView()
259 void
260 DynamicScrollView::AttachedToWindow(void)
262 BRect frame = ConvertToScreen(Bounds());
263 BRect windowFrame = Window()->Frame();
265 fIsDocumentScroller = Parent() == NULL
266 && Window() != NULL
267 && Window()->Look() == B_DOCUMENT_WINDOW_LOOK
268 && frame.right == windowFrame.right
269 && frame.bottom == windowFrame.bottom;
271 UpdateBars();
275 void
276 DynamicScrollView::FrameResized(float width, float height)
278 UpdateBars();
282 void
283 DynamicScrollView::FrameMoved(BPoint newPosition)
285 UpdateBars();
289 void
290 DynamicScrollView::GetPreferredSize(float *_width, float *_height)
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;
305 void
306 DynamicScrollView::SetContentBounds(BRect bounds)
308 fContentBounds = bounds;
309 if (Window())
310 UpdateBars();
314 void
315 DynamicScrollView::UpdateBars()
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)
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();
328 BRect bounds = Bounds();
330 // do we have to remove a scroll bar?
332 bool horizontal = width > bounds.Width();
333 bool vertical = height > bounds.Height();
335 if (!horizontal && fHorizontalScrollBar != NULL) {
336 RemoveChild(fHorizontalScrollBar);
337 delete fHorizontalScrollBar;
338 fHorizontalScrollBar = NULL;
341 if (!vertical && fVerticalScrollBar != NULL) {
342 RemoveChild(fVerticalScrollBar);
343 delete fVerticalScrollBar;
344 fVerticalScrollBar = NULL;
347 // or do we have to add a scroll bar?
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;
355 fHorizontalScrollBar = new BScrollBar(rect, "horizontal", fTarget, 0,
356 width, B_HORIZONTAL);
357 AddChild(fHorizontalScrollBar);
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;
366 fVerticalScrollBar = new BScrollBar(rect, "vertical", fTarget, 0,
367 height, B_VERTICAL);
368 AddChild(fVerticalScrollBar);
371 // update the scroll bar range & proportions and layout views
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;
379 fTarget->MoveTo(bounds.LeftTop());
380 fTarget->ResizeTo(bounds.Width(), bounds.Height());
382 if (fHorizontalScrollBar != NULL) {
383 float delta = width - bounds.Width();
384 if (delta < 0)
385 delta = 0;
387 fHorizontalScrollBar->SetRange(0, delta);
388 fHorizontalScrollBar->SetSteps(1, bounds.Width());
389 fHorizontalScrollBar->SetProportion(bounds.Width() / width);
391 float barWidth = Bounds().Width();
392 if (vertical) {
393 // scrollbars overlap one pixel of the frame
394 barWidth += 1;
396 if (vertical || fIsDocumentScroller)
397 barWidth -= B_V_SCROLL_BAR_WIDTH + 1;
399 fHorizontalScrollBar->MoveTo(bounds.left, bounds.bottom + 1);
400 fHorizontalScrollBar->ResizeTo(barWidth, B_H_SCROLL_BAR_HEIGHT);
402 if (fVerticalScrollBar != NULL) {
403 float delta = height - bounds.Height();
404 if (delta < 0)
405 delta = 0;
407 fVerticalScrollBar->SetRange(0, delta);
408 fVerticalScrollBar->SetSteps(1, bounds.Height());
409 fVerticalScrollBar->SetProportion(bounds.Height() / height);
411 float barHeight = Bounds().Height();
412 if (horizontal) {
413 // scrollbars overlap one pixel of the frame
414 barHeight += 1;
416 if (horizontal || fIsDocumentScroller)
417 barHeight -= B_H_SCROLL_BAR_HEIGHT + 1;
419 fVerticalScrollBar->MoveTo(bounds.right + 1, bounds.top);
420 fVerticalScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, barHeight);
425 // #pragma mark -
428 GroupView::GroupView(BRect frame, const char *name)
429 : BView(frame, name, B_FOLLOW_NONE, B_WILL_DRAW)
431 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
435 GroupView::~GroupView()
440 void
441 GroupView::AttachedToWindow()
443 for (int32 i = CountChildren(); i-- > 0;) {
444 BControl *control = dynamic_cast<BControl *>(ChildAt(i));
445 if (control == NULL)
446 continue;
448 control->SetTarget(control);
453 void
454 GroupView::AllAttached()
459 void
460 GroupView::GetPreferredSize(float *_width, float *_height)
462 if (_width)
463 *_width = fContentBounds.Width();
465 if (_height)
466 *_height = fContentBounds.Height();
470 BSize
471 GroupView::MinSize()
473 return BSize(100, 100);
477 BSize
478 GroupView::PreferredSize()
480 return MinSize();
484 BSize
485 GroupView::MaxSize()
487 BSize max;
488 GetPreferredSize(&max.width, &max.height);
489 return max;
493 void
494 GroupView::SetContentBounds(BRect bounds)
496 fContentBounds = bounds;
500 // #pragma mark -
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).
510 TabView::TabView(BRect frame, const char *name, button_width width,
511 uint32 resizingMode, uint32 flags)
512 : BTabView(frame, name, width, resizingMode, flags)
517 void
518 TabView::FrameResized(float width, float height)
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()
525 ContainerView()->ResizeTo(rect.Width(), rect.Height());
529 void
530 TabView::Select(int32 tab)
532 BTabView::Select(tab);
534 BView *view = ViewForTab(Selection());
535 if (view != NULL) {
536 BRect rect = ContainerView()->Bounds();
537 view->ResizeTo(rect.Width(), rect.Height());
542 // #pragma mark -
545 SeparatorView::SeparatorView(BRect frame)
546 : BView(frame, "-", B_FOLLOW_NONE, B_WILL_DRAW)
548 fVertical = frame.Width() < frame.Height();
549 SetViewColor(B_TRANSPARENT_COLOR);
553 SeparatorView::~SeparatorView()
558 void
559 SeparatorView::Draw(BRect updateRect)
561 rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR);
562 BRect rect = updateRect & Bounds();
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));
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));
578 // #pragma mark -
581 TitleView::TitleView(BRect frame, const char *title)
582 : BView(frame, title, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW)
584 fTitle = strdup(title);
585 AdoptSystemColors();
589 TitleView::~TitleView()
591 free((char *)fTitle);
595 void
596 TitleView::Draw(BRect updateRect)
598 BRect rect(Bounds());
600 SetDrawingMode(B_OP_COPY);
601 SetHighColor(tint_color(ViewColor(), B_LIGHTEN_2_TINT));
602 DrawString(fTitle, BPoint(rect.left + 1, rect.bottom - 8));
604 SetDrawingMode(B_OP_OVER);
605 SetHighColor(80, 20, 20);
606 DrawString(fTitle, BPoint(rect.left, rect.bottom - 9));
610 void
611 TitleView::GetPreferredSize(float *_width, float *_height)
613 if (_width)
614 *_width = StringWidth(fTitle) + 2;
616 if (_height) {
617 font_height fontHeight;
618 GetFontHeight(&fontHeight);
620 *_height = fontHeight.ascent + fontHeight.descent + fontHeight.leading
621 + 8;
626 // #pragma mark -
629 CheckBox::CheckBox(BRect area, const char* name, const char* label,
630 BDiscreteParameter &parameter)
631 : BCheckBox(area, name, label, NULL),
632 fParameter(parameter)
637 CheckBox::~CheckBox()
642 void
643 CheckBox::AttachedToWindow()
645 start_watching_for_parameter_changes(this, fParameter);
649 void
650 CheckBox::DetachedFromWindow()
652 stop_watching_for_parameter_changes(this, fParameter);
656 OptionPopUp::OptionPopUp(BRect area, const char* name, const char* label,
657 BDiscreteParameter &parameter)
658 : BOptionPopUp(area, name, label, NULL),
659 fParameter(parameter)
664 OptionPopUp::~OptionPopUp()
669 void
670 OptionPopUp::AttachedToWindow()
672 start_watching_for_parameter_changes(this, fParameter);
676 void
677 OptionPopUp::DetachedFromWindow()
679 stop_watching_for_parameter_changes(this, fParameter);
683 Slider::Slider(BRect area, const char* name, const char* label, int32 minValue,
684 int32 maxValue, BContinuousParameter &parameter)
685 : BSlider(area, name, label, NULL, minValue, maxValue),
686 fParameter(parameter)
691 Slider::~Slider()
696 void
697 Slider::AttachedToWindow()
699 start_watching_for_parameter_changes(this, fParameter);
703 void
704 Slider::DetachedFromWindow()
706 stop_watching_for_parameter_changes(this, fParameter);
710 ChannelSlider::ChannelSlider(BRect area, const char* name, const char* label,
711 orientation orientation, int32 channels, BContinuousParameter &parameter)
712 : BChannelSlider(area, name, label, NULL, orientation, channels),
713 fParameter(parameter)
718 ChannelSlider::~ChannelSlider()
723 void
724 ChannelSlider::AttachedToWindow()
726 start_watching_for_parameter_changes(this, fParameter);
730 void
731 ChannelSlider::DetachedFromWindow()
733 stop_watching_for_parameter_changes(this, fParameter);
737 // #pragma mark -
740 MessageFilter::MessageFilter()
741 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
746 MessageFilter *
747 MessageFilter::FilterFor(BView *view, BParameter &parameter)
749 BControl *control = dynamic_cast<BControl *>(view);
750 if (control == NULL)
751 return NULL;
753 switch (parameter.Type()) {
754 case BParameter::B_CONTINUOUS_PARAMETER:
755 return new ContinuousMessageFilter(control,
756 static_cast<BContinuousParameter &>(parameter));
758 case BParameter::B_DISCRETE_PARAMETER:
759 return new DiscreteMessageFilter(control,
760 static_cast<BDiscreteParameter &>(parameter));
762 case BParameter::B_NULL_PARAMETER: /* fall through */
763 default:
764 return NULL;
769 // #pragma mark -
772 ContinuousMessageFilter::ContinuousMessageFilter(BControl *control,
773 BContinuousParameter &parameter)
774 : MessageFilter(),
775 fControl(control),
776 fParameter(parameter)
778 // initialize view for us
779 control->SetMessage(new BMessage(kMsgParameterChanged));
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");
788 // set initial value
789 _UpdateControl();
793 ContinuousMessageFilter::~ContinuousMessageFilter()
798 filter_result
799 ContinuousMessageFilter::Filter(BMessage *message, BHandler **target)
801 if (*target != fControl)
802 return B_DISPATCH_MESSAGE;
804 if (message->what == kMsgParameterChanged) {
805 // update parameter from control
806 // TODO: support for response!
808 float value[fParameter.CountChannels()];
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);
818 TRACE("ContinuousMessageFilter::Filter: update view %s, %" B_PRId32
819 " channels\n", fControl->Name(), fParameter.CountChannels());
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;
827 return B_SKIP_MESSAGE;
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", &parameterID) != 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;
841 _UpdateControl();
842 return B_SKIP_MESSAGE;
845 return B_DISPATCH_MESSAGE;
849 void
850 ContinuousMessageFilter::_UpdateControl()
852 // TODO: response support!
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;
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]));
875 // #pragma mark -
878 DiscreteMessageFilter::DiscreteMessageFilter(BControl *control,
879 BDiscreteParameter &parameter)
880 : MessageFilter(),
881 fParameter(parameter)
883 // initialize view for us
884 control->SetMessage(new BMessage(kMsgParameterChanged));
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", &parameter,
892 parameter.Name(), (int)(parameter.Web()->Node().node));
893 return;
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");
905 DiscreteMessageFilter::~DiscreteMessageFilter()
910 filter_result
911 DiscreteMessageFilter::Filter(BMessage *message, BHandler **target)
913 BControl *control;
915 if ((control = dynamic_cast<BControl *>(*target)) == NULL)
916 return B_DISPATCH_MESSAGE;
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", &parameterID) != 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;
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;
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);
944 return B_SKIP_MESSAGE;
947 if (message->what != kMsgParameterChanged)
948 return B_DISPATCH_MESSAGE;
950 // update view
952 int32 value = 0;
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);
960 TRACE("DiscreteMessageFilter::Filter: update view %s, value = %"
961 B_PRId32 "\n", control->Name(), value);
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;
968 return B_SKIP_MESSAGE;
972 // #pragma mark -
975 DefaultMediaTheme::DefaultMediaTheme()
976 : BMediaTheme("Haiku theme", "Haiku built-in theme version 0.1")
978 CALLED();
982 BControl *
983 DefaultMediaTheme::MakeControlFor(BParameter *parameter)
985 CALLED();
987 BRect rect(0, 0, 150, 100);
988 return MakeViewFor(parameter, &rect);
992 BView *
993 DefaultMediaTheme::MakeViewFor(BParameterWeb *web, const BRect *hintRect)
995 CALLED();
997 if (web == NULL)
998 return NULL;
1000 BRect rect;
1001 if (hintRect)
1002 rect = *hintRect;
1004 BRect bestRect;
1006 // do we have more than one attached parameter group?
1007 // if so, use a tabbed view with a tab for each group
1009 TabView *tabView = NULL;
1011 if (web->CountGroups() > 1)
1012 tabView = new TabView(rect, "web");
1014 rect.OffsetTo(B_ORIGIN);
1016 for (int32 i = 0; i < web->CountGroups(); i++) {
1017 BParameterGroup *group = web->GroupAt(i);
1018 if (group == NULL)
1019 continue;
1021 BView *groupView = MakeViewFor(*group, hintRect ? &rect : NULL);
1022 if (groupView == NULL)
1023 continue;
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);
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);
1044 return new DynamicScrollView(groupView->Name(), groupView);
1047 DynamicScrollView *scrollView = new DynamicScrollView(groupView->Name(), groupView);
1048 tabView->AddTab(scrollView);
1050 if (!hintRect) {
1051 bestRect = bestRect | scrollView->Bounds();
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);
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
1065 return tabView;
1069 BView *
1070 DefaultMediaTheme::MakeViewFor(BParameterGroup& group, const BRect* hintRect)
1072 CALLED();
1074 if (group.Flags() & B_HIDDEN_PARAMETER)
1075 return NULL;
1077 BRect rect;
1078 if (hintRect != NULL)
1079 rect = *hintRect;
1081 GroupView *view = new GroupView(rect, group.Name());
1083 // Create the parameter views - but don't add them yet
1085 rect.OffsetTo(B_ORIGIN);
1086 rect.InsetBySelf(5, 5);
1088 BList views;
1089 for (int32 i = 0; i < group.CountParameters(); i++) {
1090 BParameter *parameter = group.ParameterAt(i);
1091 if (parameter == NULL)
1092 continue;
1094 BView *parameterView = MakeSelfHostingViewFor(*parameter,
1095 hintRect ? &rect : NULL);
1096 if (parameterView == NULL)
1097 continue;
1099 parameterView->AdoptViewColors(view);
1100 // ToDo: dunno why this is needed, but the controls
1101 // sometimes (!) have a white background without it
1103 views.AddItem(parameterView);
1106 // Identify a title view, and add it at the top if present
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());
1114 // Add the sub-group views
1116 rect.right = rect.left + 20;
1117 rect.bottom = rect.top + 20;
1118 float lastHeight = 0;
1120 for (int32 i = 0; i < group.CountGroups(); i++) {
1121 BParameterGroup *subGroup = group.GroupAt(i);
1122 if (subGroup == NULL)
1123 continue;
1125 BView *groupView = MakeViewFor(*subGroup, &rect);
1126 if (groupView == NULL)
1127 continue;
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;
1137 view->AddChild(new SeparatorView(separatorRect));
1140 view->AddChild(groupView);
1142 rect.OffsetBy(groupView->Bounds().Width() + 5, 0);
1144 lastHeight = groupView->Bounds().Height();
1145 if (lastHeight > rect.Height())
1146 rect.bottom = rect.top + lastHeight - 1;
1149 view->ResizeTo(rect.left + 10, rect.bottom + 5);
1150 view->SetContentBounds(view->Bounds());
1152 if (group.CountParameters() == 0)
1153 return view;
1155 // add the parameter views part of the group
1157 if (group.CountGroups() > 0) {
1158 rect.top = rect.bottom + 10;
1159 rect.bottom = rect.top + 20;
1162 bool center = false;
1164 for (int32 i = 0; i < views.CountItems(); i++) {
1165 BView *parameterView = static_cast<BView *>(views.ItemAt(i));
1167 if (parameterView->Bounds().Width() + 5 > rect.Width())
1168 rect.right = parameterView->Bounds().Width() + rect.left + 5;
1170 // we don't need to add the title view again
1171 if (parameterView == titleView)
1172 continue;
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;
1179 parameterView->MoveTo(parameterView->Frame().left, rect.top);
1180 view->AddChild(parameterView);
1182 rect.OffsetBy(0, parameterView->Bounds().Height() + 5);
1185 if (views.CountItems() > (titleView != NULL ? 1 : 0))
1186 view->ResizeTo(rect.right + 5, rect.top + 5);
1188 // center the parameter views if needed, and tweak some views
1190 float width = view->Bounds().Width();
1192 for (int32 i = 0; i < views.CountItems(); i++) {
1193 BView *subView = static_cast<BView *>(views.ItemAt(i));
1194 BRect frame = subView->Frame();
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());
1205 view->SetContentBounds(view->Bounds());
1206 return view;
1210 /*! This creates a view that handles all incoming messages itself - that's
1211 what is meant with self-hosting.
1213 BView *
1214 DefaultMediaTheme::MakeSelfHostingViewFor(BParameter& parameter,
1215 const BRect* hintRect)
1217 if (parameter.Flags() & B_HIDDEN_PARAMETER
1218 || parameter_should_be_hidden(parameter))
1219 return NULL;
1221 BView *view = MakeViewFor(&parameter, 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) == &parameter) {
1229 // this is the first parameter in this group, so
1230 // let's use a nice title view
1232 TitleView *titleView = new TitleView(BRect(0, 0, 10, 10), parameter.Name());
1233 titleView->ResizeToPreferred();
1235 return titleView;
1237 BStringView *stringView = new BStringView(BRect(0, 0, 10, 10),
1238 parameter.Name(), parameter.Name());
1239 stringView->SetAlignment(B_ALIGN_CENTER);
1240 stringView->ResizeToPreferred();
1242 return stringView;
1245 return NULL;
1248 MessageFilter *filter = MessageFilter::FilterFor(view, parameter);
1249 if (filter != NULL)
1250 view->AddFilter(filter);
1252 return view;
1256 BControl *
1257 DefaultMediaTheme::MakeViewFor(BParameter *parameter, const BRect *hintRect)
1259 BRect rect;
1260 if (hintRect)
1261 rect = *hintRect;
1262 else
1263 rect.Set(0, 0, 50, 100);
1265 switch (parameter->Type()) {
1266 case BParameter::B_NULL_PARAMETER:
1267 // there is no default view for a null parameter
1268 return NULL;
1270 case BParameter::B_DISCRETE_PARAMETER:
1272 BDiscreteParameter &discrete = static_cast<BDiscreteParameter &>(*parameter);
1274 if (!strcmp(discrete.Kind(), B_ENABLE)
1275 || !strcmp(discrete.Kind(), B_MUTE)
1276 || discrete.CountItems() == 0) {
1277 // create a checkbox item
1279 BCheckBox *checkBox = new CheckBox(rect, discrete.Name(),
1280 discrete.Name(), discrete);
1281 checkBox->ResizeToPreferred();
1283 return checkBox;
1284 } else {
1285 // create a pop up menu field
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...
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;
1298 width += font.StringWidth(discrete.Name()) + 55;
1299 rect.right = rect.left + width;
1301 BOptionPopUp *popUp = new OptionPopUp(rect, discrete.Name(),
1302 discrete.Name(), discrete);
1304 for (int32 i = 0; i < discrete.CountItems(); i++) {
1305 popUp->AddOption(discrete.ItemNameAt(i), discrete.ItemValueAt(i));
1308 popUp->ResizeToPreferred();
1310 return popUp;
1314 case BParameter::B_CONTINUOUS_PARAMETER:
1316 BContinuousParameter &continuous = static_cast<BContinuousParameter &>(*parameter);
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);
1324 char minLabel[64], maxLabel[64];
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());
1335 slider->SetLimitLabels(minLabel, maxLabel);
1337 float width, height;
1338 slider->GetPreferredSize(&width, &height);
1339 slider->ResizeTo(width, 190);
1341 // ToDo: take BContinuousParameter::GetResponse() & ValueStep() into account!
1343 for (int32 i = 0; i < continuous.CountChannels(); i++) {
1344 slider->SetLimitsFor(i, int32(continuous.MinValue() * 1000),
1345 int32(continuous.MaxValue() * 1000));
1348 return slider;
1351 BSlider *slider = new Slider(rect, parameter->Name(),
1352 parameter->Name(), 0, 100, continuous);
1354 float width, height;
1355 slider->GetPreferredSize(&width, &height);
1356 slider->ResizeTo(100, height);
1358 return slider;
1361 default:
1362 ERROR("BMediaTheme: Don't know parameter type: 0x%x\n",
1363 parameter->Type());
1365 return NULL;