2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
7 #include "DefaultMediaTheme.h"
11 #include <ChannelSlider.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>
20 #include <StringView.h>
22 #include <TextControl.h>
28 using namespace BPrivate
;
33 class DynamicScrollView
: public BView
{
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
; }
49 BScrollBar
*fHorizontalScrollBar
, *fVerticalScrollBar
;
52 bool fIsDocumentScroller
;
55 class GroupView
: public BView
{
57 GroupView(BRect frame
, const char *name
);
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
; }
75 class TabView
: public BTabView
{
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
{
87 SeparatorView(BRect frame
);
88 virtual ~SeparatorView();
90 virtual void Draw(BRect updateRect
);
96 class TitleView
: public BView
{
98 TitleView(BRect frame
, const char *title
);
101 virtual void Draw(BRect updateRect
);
102 virtual void GetPreferredSize(float *width
, float *height
);
108 class MessageFilter
: public BMessageFilter
{
110 static MessageFilter
*FilterFor(BView
*view
, BParameter
¶meter
);
116 class ContinuousMessageFilter
: public MessageFilter
{
118 ContinuousMessageFilter(BControl
*control
,
119 BContinuousParameter
¶meter
);
120 virtual ~ContinuousMessageFilter();
122 virtual filter_result
Filter(BMessage
*message
, BHandler
**target
);
125 void _UpdateControl();
128 BContinuousParameter
&fParameter
;
132 class DiscreteMessageFilter
: public MessageFilter
{
134 DiscreteMessageFilter(BControl
*control
, BDiscreteParameter
¶meter
);
135 virtual ~DiscreteMessageFilter();
137 virtual filter_result
Filter(BMessage
*message
, BHandler
**target
);
140 BDiscreteParameter
&fParameter
;
143 } // namespace BPrivate
146 const uint32 kMsgParameterChanged
= '_mPC';
150 parameter_should_be_hidden(BParameter
¶meter
)
152 // ToDo: note, this is probably completely stupid, but it's the only
153 // way I could safely remove the null parameters that are not shown
154 // by the R5 media theme
155 if (parameter
.Type() != BParameter::B_NULL_PARAMETER
156 || strcmp(parameter
.Kind(), B_WEB_PHYSICAL_INPUT
))
159 for (int32 i
= 0; i
< parameter
.CountOutputs(); i
++) {
160 if (!strcmp(parameter
.OutputAt(0)->Kind(), B_INPUT_MUX
))
171 DynamicScrollView::DynamicScrollView(const char *name
, BView
*target
)
172 : BView(target
->Frame(), name
, B_FOLLOW_ALL
, B_WILL_DRAW
| B_FRAME_EVENTS
),
173 fHorizontalScrollBar(NULL
),
174 fVerticalScrollBar(NULL
),
176 fIsDocumentScroller(false)
178 fContentBounds
.Set(-1, -1, -1, -1);
179 SetViewColor(fTarget
->ViewColor());
180 target
->MoveTo(B_ORIGIN
);
185 DynamicScrollView::~DynamicScrollView()
191 DynamicScrollView::AttachedToWindow(void)
193 BRect frame
= ConvertToScreen(Bounds());
194 BRect windowFrame
= Window()->Frame();
196 fIsDocumentScroller
= Parent() == NULL
198 && Window()->Look() == B_DOCUMENT_WINDOW_LOOK
199 && frame
.right
== windowFrame
.right
200 && frame
.bottom
== windowFrame
.bottom
;
207 DynamicScrollView::FrameResized(float width
, float height
)
214 DynamicScrollView::FrameMoved(BPoint newPosition
)
221 DynamicScrollView::GetPreferredSize(float *_width
, float *_height
)
224 if (fVerticalScrollBar
)
225 width
+= B_V_SCROLL_BAR_WIDTH
;
227 if (fHorizontalScrollBar
)
228 height
+= B_H_SCROLL_BAR_HEIGHT
;
237 DynamicScrollView::SetContentBounds(BRect bounds
)
239 fContentBounds
= bounds
;
246 DynamicScrollView::UpdateBars()
248 // we need the size that the view wants to have, and the one
249 // it could have (without the space for the scroll bars)
252 if (fContentBounds
== BRect(-1, -1, -1, -1))
253 fTarget
->GetPreferredSize(&width
, &height
);
255 width
= fContentBounds
.Width();
256 height
= fContentBounds
.Height();
259 BRect bounds
= Bounds();
261 // do we have to remove a scroll bar?
263 bool horizontal
= width
> bounds
.Width();
264 bool vertical
= height
> bounds
.Height();
266 if (!horizontal
&& fHorizontalScrollBar
!= NULL
) {
267 RemoveChild(fHorizontalScrollBar
);
268 delete fHorizontalScrollBar
;
269 fHorizontalScrollBar
= NULL
;
272 if (!vertical
&& fVerticalScrollBar
!= NULL
) {
273 RemoveChild(fVerticalScrollBar
);
274 delete fVerticalScrollBar
;
275 fVerticalScrollBar
= NULL
;
278 // or do we have to add a scroll bar?
280 if (horizontal
&& fHorizontalScrollBar
== NULL
) {
281 BRect rect
= Bounds();
282 rect
.top
= rect
.bottom
+ 1 - B_H_SCROLL_BAR_HEIGHT
;
283 if (vertical
|| fIsDocumentScroller
)
284 rect
.right
-= B_V_SCROLL_BAR_WIDTH
;
286 fHorizontalScrollBar
= new BScrollBar(rect
, "horizontal", fTarget
, 0,
287 width
, B_HORIZONTAL
);
288 AddChild(fHorizontalScrollBar
);
291 if (vertical
&& fVerticalScrollBar
== NULL
) {
292 BRect rect
= Bounds();
293 rect
.left
= rect
.right
+ 1 - B_V_SCROLL_BAR_WIDTH
;
294 if (horizontal
|| fIsDocumentScroller
)
295 rect
.bottom
-= B_H_SCROLL_BAR_HEIGHT
;
297 fVerticalScrollBar
= new BScrollBar(rect
, "vertical", fTarget
, 0,
299 AddChild(fVerticalScrollBar
);
302 // update the scroll bar range & proportions and layout views
305 if (fHorizontalScrollBar
!= NULL
)
306 bounds
.bottom
-= B_H_SCROLL_BAR_HEIGHT
+ 1;
307 if (fVerticalScrollBar
!= NULL
)
308 bounds
.right
-= B_V_SCROLL_BAR_WIDTH
+ 1;
310 fTarget
->MoveTo(bounds
.LeftTop());
311 fTarget
->ResizeTo(bounds
.Width(), bounds
.Height());
313 if (fHorizontalScrollBar
!= NULL
) {
314 float delta
= width
- bounds
.Width();
318 fHorizontalScrollBar
->SetRange(0, delta
);
319 fHorizontalScrollBar
->SetSteps(1, bounds
.Width());
320 fHorizontalScrollBar
->SetProportion(bounds
.Width() / width
);
322 float barWidth
= Bounds().Width();
324 // scrollbars overlap one pixel of the frame
327 if (vertical
|| fIsDocumentScroller
)
328 barWidth
-= B_V_SCROLL_BAR_WIDTH
+ 1;
330 fHorizontalScrollBar
->MoveTo(bounds
.left
, bounds
.bottom
+ 1);
331 fHorizontalScrollBar
->ResizeTo(barWidth
, B_H_SCROLL_BAR_HEIGHT
);
333 if (fVerticalScrollBar
!= NULL
) {
334 float delta
= height
- bounds
.Height();
338 fVerticalScrollBar
->SetRange(0, delta
);
339 fVerticalScrollBar
->SetSteps(1, bounds
.Height());
340 fVerticalScrollBar
->SetProportion(bounds
.Height() / height
);
342 float barHeight
= Bounds().Height();
344 // scrollbars overlap one pixel of the frame
347 if (horizontal
|| fIsDocumentScroller
)
348 barHeight
-= B_H_SCROLL_BAR_HEIGHT
+ 1;
350 fVerticalScrollBar
->MoveTo(bounds
.right
+ 1, bounds
.top
);
351 fVerticalScrollBar
->ResizeTo(B_V_SCROLL_BAR_WIDTH
, barHeight
);
359 GroupView::GroupView(BRect frame
, const char *name
)
360 : BView(frame
, name
, B_FOLLOW_NONE
, B_WILL_DRAW
)
362 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
366 GroupView::~GroupView()
372 GroupView::AttachedToWindow()
374 for (int32 i
= CountChildren(); i
-- > 0;) {
375 BControl
*control
= dynamic_cast<BControl
*>(ChildAt(i
));
379 control
->SetTarget(control
);
385 GroupView::AllAttached()
391 GroupView::GetPreferredSize(float *_width
, float *_height
)
394 *_width
= fContentBounds
.Width();
397 *_height
= fContentBounds
.Height();
404 return BSize(100, 100);
409 GroupView::PreferredSize()
419 GetPreferredSize(&max
.width
, &max
.height
);
425 GroupView::SetContentBounds(BRect bounds
)
427 fContentBounds
= bounds
;
434 /** BTabView is really stupid - it doesn't even resize its content
435 * view when it is resized itself.
436 * This derived class fixes this issue, and also resizes all tab
437 * content views to the size of the container view when they are
438 * selected (does not take their resize flags into account, though).
441 TabView::TabView(BRect frame
, const char *name
, button_width width
,
442 uint32 resizingMode
, uint32 flags
)
443 : BTabView(frame
, name
, width
, resizingMode
, flags
)
449 TabView::FrameResized(float width
, float height
)
451 BRect rect
= Bounds();
452 rect
.top
+= TabHeight();
453 rect
.InsetBy(3.0f
, 3.0f
);
454 //ContainerView is inseted by 3.0 in BTabView::_InitObject()
456 ContainerView()->ResizeTo(rect
.Width(), rect
.Height());
461 TabView::Select(int32 tab
)
463 BTabView::Select(tab
);
465 BView
*view
= ViewForTab(Selection());
467 BRect rect
= ContainerView()->Bounds();
468 view
->ResizeTo(rect
.Width(), rect
.Height());
476 SeparatorView::SeparatorView(BRect frame
)
477 : BView(frame
, "-", B_FOLLOW_NONE
, B_WILL_DRAW
)
479 fVertical
= frame
.Width() < frame
.Height();
480 SetViewColor(B_TRANSPARENT_COLOR
);
484 SeparatorView::~SeparatorView()
490 SeparatorView::Draw(BRect updateRect
)
492 rgb_color color
= ui_color(B_PANEL_BACKGROUND_COLOR
);
493 BRect rect
= updateRect
& Bounds();
495 SetHighColor(tint_color(color
, B_DARKEN_1_TINT
));
497 StrokeLine(BPoint(0, rect
.top
), BPoint(0, rect
.bottom
));
499 StrokeLine(BPoint(rect
.left
, 0), BPoint(rect
.right
, 0));
501 SetHighColor(tint_color(color
, B_LIGHTEN_1_TINT
));
503 StrokeLine(BPoint(1, rect
.top
), BPoint(1, rect
.bottom
));
505 StrokeLine(BPoint(rect
.left
, 1), BPoint(rect
.right
, 1));
512 TitleView::TitleView(BRect frame
, const char *title
)
513 : BView(frame
, title
, B_FOLLOW_LEFT_RIGHT
, B_WILL_DRAW
)
515 fTitle
= strdup(title
);
516 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
517 SetLowColor(ViewColor());
521 TitleView::~TitleView()
523 free((char *)fTitle
);
528 TitleView::Draw(BRect updateRect
)
530 BRect
rect(Bounds());
532 SetDrawingMode(B_OP_COPY
);
533 SetHighColor(240, 240, 240);
534 DrawString(fTitle
, BPoint(rect
.left
+ 1, rect
.bottom
- 8));
536 SetDrawingMode(B_OP_OVER
);
537 SetHighColor(80, 20, 20);
538 DrawString(fTitle
, BPoint(rect
.left
, rect
.bottom
- 9));
543 TitleView::GetPreferredSize(float *_width
, float *_height
)
546 *_width
= StringWidth(fTitle
) + 2;
549 font_height fontHeight
;
550 GetFontHeight(&fontHeight
);
552 *_height
= fontHeight
.ascent
+ fontHeight
.descent
+ fontHeight
.leading
561 MessageFilter::MessageFilter()
562 : BMessageFilter(B_ANY_DELIVERY
, B_ANY_SOURCE
)
568 MessageFilter::FilterFor(BView
*view
, BParameter
¶meter
)
570 BControl
*control
= dynamic_cast<BControl
*>(view
);
574 switch (parameter
.Type()) {
575 case BParameter::B_CONTINUOUS_PARAMETER
:
576 return new ContinuousMessageFilter(control
,
577 static_cast<BContinuousParameter
&>(parameter
));
579 case BParameter::B_DISCRETE_PARAMETER
:
580 return new DiscreteMessageFilter(control
,
581 static_cast<BDiscreteParameter
&>(parameter
));
583 case BParameter::B_NULL_PARAMETER
: /* fall through */
593 ContinuousMessageFilter::ContinuousMessageFilter(BControl
*control
,
594 BContinuousParameter
¶meter
)
597 fParameter(parameter
),
600 // initialize view for us
601 control
->SetMessage(new BMessage(kMsgParameterChanged
));
603 if (BSlider
*slider
= dynamic_cast<BSlider
*>(fControl
))
604 slider
->SetModificationMessage(new BMessage(kMsgParameterChanged
));
605 else if (BChannelSlider
*slider
= dynamic_cast<BChannelSlider
*>(fControl
))
606 slider
->SetModificationMessage(new BMessage(kMsgParameterChanged
));
608 ERROR("ContinuousMessageFilter: unknown continuous parameter view\n");
615 ContinuousMessageFilter::~ContinuousMessageFilter()
621 ContinuousMessageFilter::Filter(BMessage
*message
, BHandler
**target
)
623 if (*target
!= fControl
)
624 return B_DISPATCH_MESSAGE
;
626 // TODO: remove this work-around! We can solve this by subclassing the
627 // slider classes, and start watching in their AttachedToWindow() method
629 if (BMediaRoster
* roster
= BMediaRoster::CurrentRoster()) {
630 roster
->StartWatching(fControl
, fParameter
.Web()->Node(),
631 B_MEDIA_NEW_PARAMETER_VALUE
);
636 if (message
->what
== kMsgParameterChanged
) {
637 // update parameter from control
638 // TODO: support for response!
640 float value
[fParameter
.CountChannels()];
642 if (BSlider
*slider
= dynamic_cast<BSlider
*>(fControl
)) {
643 value
[0] = (float)(slider
->Value() / 1000.0);
644 } else if (BChannelSlider
*slider
645 = dynamic_cast<BChannelSlider
*>(fControl
)) {
646 for (int32 i
= 0; i
< fParameter
.CountChannels(); i
++)
647 value
[i
] = (float)(slider
->ValueFor(i
) / 1000.0);
650 TRACE("ContinuousMessageFilter::Filter: update view %s, %ld "
651 "channels\n", fControl
->Name(), fParameter
.CountChannels());
653 if (fParameter
.SetValue((void *)value
, sizeof(value
),
654 system_time()) < B_OK
) {
655 ERROR("ContinuousMessageFilter::Filter: Could not set parameter "
656 "value for %p\n", &fParameter
);
657 return B_DISPATCH_MESSAGE
;
659 return B_SKIP_MESSAGE
;
661 if (message
->what
== B_MEDIA_NEW_PARAMETER_VALUE
) {
662 // update view from parameter -- if the message concerns us
663 const media_node
* node
;
666 if (message
->FindInt32("parameter", ¶meterID
) != B_OK
667 || fParameter
.ID() != parameterID
668 || message
->FindData("node", B_RAW_TYPE
, (const void**)&node
,
670 || fParameter
.Web()->Node() != *node
)
671 return B_DISPATCH_MESSAGE
;
674 return B_SKIP_MESSAGE
;
677 return B_DISPATCH_MESSAGE
;
682 ContinuousMessageFilter::_UpdateControl()
684 // TODO: response support!
686 float value
[fParameter
.CountChannels()];
687 size_t size
= sizeof(value
);
688 if (fParameter
.GetValue((void *)&value
, &size
, NULL
) < B_OK
) {
689 ERROR("ContinuousMessageFilter: Could not get value for continuous "
690 "parameter %p (name '%s', node %d)\n", &fParameter
,
691 fParameter
.Name(), (int)fParameter
.Web()->Node().node
);
695 if (BSlider
*slider
= dynamic_cast<BSlider
*>(fControl
)) {
696 slider
->SetValue((int32
) (1000 * value
[0]));
697 slider
->SetModificationMessage(new BMessage(kMsgParameterChanged
));
698 } else if (BChannelSlider
*slider
699 = dynamic_cast<BChannelSlider
*>(fControl
)) {
700 for (int32 i
= 0; i
< fParameter
.CountChannels(); i
++) {
701 slider
->SetValueFor(i
, (int32
) (1000 * value
[i
]));
710 DiscreteMessageFilter::DiscreteMessageFilter(BControl
*control
,
711 BDiscreteParameter
¶meter
)
713 fParameter(parameter
)
715 // initialize view for us
716 control
->SetMessage(new BMessage(kMsgParameterChanged
));
720 size_t size
= sizeof(int32
);
722 if (parameter
.GetValue((void *)&value
, &size
, NULL
) < B_OK
) {
723 ERROR("DiscreteMessageFilter: Could not get value for discrete "
724 "parameter %p (name '%s', node %d)\n", ¶meter
,
725 parameter
.Name(), (int)(parameter
.Web()->Node().node
));
729 if (BCheckBox
*checkBox
= dynamic_cast<BCheckBox
*>(control
)) {
730 checkBox
->SetValue(value
);
731 } else if (BOptionPopUp
*popUp
= dynamic_cast<BOptionPopUp
*>(control
)) {
732 popUp
->SelectOptionFor(value
);
734 ERROR("DiscreteMessageFilter: unknown discrete parameter view\n");
738 DiscreteMessageFilter::~DiscreteMessageFilter()
744 DiscreteMessageFilter::Filter(BMessage
*message
, BHandler
**target
)
748 if (message
->what
!= kMsgParameterChanged
749 || (control
= dynamic_cast<BControl
*>(*target
)) == NULL
)
750 return B_DISPATCH_MESSAGE
;
756 if (BCheckBox
*checkBox
= dynamic_cast<BCheckBox
*>(control
)) {
757 value
= checkBox
->Value();
758 } else if (BOptionPopUp
*popUp
= dynamic_cast<BOptionPopUp
*>(control
)) {
759 popUp
->SelectedOption(NULL
, &value
);
762 TRACE("DiscreteMessageFilter::Filter: update view %s, value = %ld\n", control
->Name(), value
);
764 if (fParameter
.SetValue((void *)&value
, sizeof(value
), system_time()) < B_OK
) {
765 ERROR("DiscreteMessageFilter::Filter: Could not set parameter value for %p\n", &fParameter
);
766 return B_DISPATCH_MESSAGE
;
769 return B_SKIP_MESSAGE
;
776 DefaultMediaTheme::DefaultMediaTheme()
777 : BMediaTheme("Haiku theme", "Haiku built-in theme version 0.1")
784 DefaultMediaTheme::MakeControlFor(BParameter
*parameter
)
788 BRect
rect(0, 0, 150, 100);
789 return MakeViewFor(parameter
, &rect
);
794 DefaultMediaTheme::MakeViewFor(BParameterWeb
*web
, const BRect
*hintRect
)
807 // do we have more than one attached parameter group?
808 // if so, use a tabbed view with a tab for each group
810 TabView
*tabView
= NULL
;
812 if (web
->CountGroups() > 1)
813 tabView
= new TabView(rect
, "web");
815 rect
.OffsetTo(B_ORIGIN
);
817 for (int32 i
= 0; i
< web
->CountGroups(); i
++) {
818 BParameterGroup
*group
= web
->GroupAt(i
);
822 BView
*groupView
= MakeViewFor(*group
, hintRect
? &rect
: NULL
);
823 if (groupView
== NULL
)
826 if (GroupView
*view
= dynamic_cast<GroupView
*>(groupView
)) {
827 // the top-level group views must not be larger than their hintRect,
828 // but unlike their children, they should follow all sides when
829 // their parent is resized
830 if (hintRect
!= NULL
)
831 view
->ResizeTo(rect
.Width() - 10, rect
.Height() - 10);
832 view
->SetResizingMode(B_FOLLOW_ALL
);
835 if (tabView
== NULL
) {
836 // if we don't need a container to put that view into,
837 // we're done here (but the groupView may span over the
839 if (groupView
->Frame().LeftTop() == BPoint(5, 5)) {
840 // remove insets, as they are not needed
841 groupView
->MoveBy(-5, -5);
842 groupView
->ResizeBy(10, 10);
845 return new DynamicScrollView(groupView
->Name(), groupView
);
848 DynamicScrollView
*scrollView
= new DynamicScrollView(groupView
->Name(), groupView
);
849 tabView
->AddTab(scrollView
);
852 bestRect
= bestRect
| scrollView
->Bounds();
856 if (tabView
!= NULL
) {
857 // this adjustment must be kept in sync with TabView::FrameResized
858 bestRect
.bottom
+= tabView
->TabHeight();
859 bestRect
.InsetBy(-3.0,-3.0);
861 tabView
->ResizeTo(bestRect
.Width(), bestRect
.Height());
862 tabView
->FrameResized(bestRect
.Width(), bestRect
.Height());
863 //needed since we're not attached to a window yet
871 DefaultMediaTheme::MakeViewFor(BParameterGroup
& group
, const BRect
* hintRect
)
875 if (group
.Flags() & B_HIDDEN_PARAMETER
)
879 if (hintRect
!= NULL
)
882 GroupView
*view
= new GroupView(rect
, group
.Name());
884 // Create the parameter views - but don't add them yet
886 rect
.OffsetTo(B_ORIGIN
);
887 rect
.InsetBySelf(5, 5);
890 for (int32 i
= 0; i
< group
.CountParameters(); i
++) {
891 BParameter
*parameter
= group
.ParameterAt(i
);
892 if (parameter
== NULL
)
895 BView
*parameterView
= MakeSelfHostingViewFor(*parameter
,
896 hintRect
? &rect
: NULL
);
897 if (parameterView
== NULL
)
900 parameterView
->SetViewColor(view
->ViewColor());
901 // ToDo: dunno why this is needed, but the controls
902 // sometimes (!) have a white background without it
904 views
.AddItem(parameterView
);
907 // Identify a title view, and add it at the top if present
909 TitleView
*titleView
= dynamic_cast<TitleView
*>((BView
*)views
.ItemAt(0));
910 if (titleView
!= NULL
) {
911 view
->AddChild(titleView
);
912 rect
.OffsetBy(0, titleView
->Bounds().Height());
915 // Add the sub-group views
917 rect
.right
= rect
.left
+ 20;
918 rect
.bottom
= rect
.top
+ 20;
919 float lastHeight
= 0;
921 for (int32 i
= 0; i
< group
.CountGroups(); i
++) {
922 BParameterGroup
*subGroup
= group
.GroupAt(i
);
923 if (subGroup
== NULL
)
926 BView
*groupView
= MakeViewFor(*subGroup
, &rect
);
927 if (groupView
== NULL
)
931 // add separator view
932 BRect
separatorRect(groupView
->Frame());
933 separatorRect
.left
-= 3;
934 separatorRect
.right
= separatorRect
.left
+ 1;
935 if (lastHeight
> separatorRect
.Height())
936 separatorRect
.bottom
= separatorRect
.top
+ lastHeight
;
938 view
->AddChild(new SeparatorView(separatorRect
));
941 view
->AddChild(groupView
);
943 rect
.OffsetBy(groupView
->Bounds().Width() + 5, 0);
945 lastHeight
= groupView
->Bounds().Height();
946 if (lastHeight
> rect
.Height())
947 rect
.bottom
= rect
.top
+ lastHeight
- 1;
950 view
->ResizeTo(rect
.left
+ 10, rect
.bottom
+ 5);
951 view
->SetContentBounds(view
->Bounds());
953 if (group
.CountParameters() == 0)
956 // add the parameter views part of the group
958 if (group
.CountGroups() > 0) {
959 rect
.top
= rect
.bottom
+ 10;
960 rect
.bottom
= rect
.top
+ 20;
965 for (int32 i
= 0; i
< views
.CountItems(); i
++) {
966 BView
*parameterView
= static_cast<BView
*>(views
.ItemAt(i
));
968 if (parameterView
->Bounds().Width() + 5 > rect
.Width())
969 rect
.right
= parameterView
->Bounds().Width() + rect
.left
+ 5;
971 // we don't need to add the title view again
972 if (parameterView
== titleView
)
975 // if there is a BChannelSlider (ToDo: or any vertical slider?)
976 // the views will be centered
977 if (dynamic_cast<BChannelSlider
*>(parameterView
) != NULL
)
980 parameterView
->MoveTo(parameterView
->Frame().left
, rect
.top
);
981 view
->AddChild(parameterView
);
983 rect
.OffsetBy(0, parameterView
->Bounds().Height() + 5);
986 if (views
.CountItems() > (titleView
!= NULL
? 1 : 0))
987 view
->ResizeTo(rect
.right
+ 5, rect
.top
+ 5);
989 // center the parameter views if needed, and tweak some views
991 float width
= view
->Bounds().Width();
993 for (int32 i
= 0; i
< views
.CountItems(); i
++) {
994 BView
*subView
= static_cast<BView
*>(views
.ItemAt(i
));
995 BRect frame
= subView
->Frame();
998 subView
->MoveTo((width
- frame
.Width()) / 2, frame
.top
);
1000 // tweak the PopUp views to look better
1001 if (dynamic_cast<BOptionPopUp
*>(subView
) != NULL
)
1002 subView
->ResizeTo(width
, frame
.Height());
1006 view
->SetContentBounds(view
->Bounds());
1011 /*! This creates a view that handles all incoming messages itself - that's
1012 what is meant with self-hosting.
1015 DefaultMediaTheme::MakeSelfHostingViewFor(BParameter
& parameter
,
1016 const BRect
* hintRect
)
1018 if (parameter
.Flags() & B_HIDDEN_PARAMETER
1019 || parameter_should_be_hidden(parameter
))
1022 BView
*view
= MakeViewFor(¶meter
, hintRect
);
1024 // The MakeViewFor() method above returns a BControl - which we
1025 // don't need for a null parameter; that's why it returns NULL.
1026 // But we want to see something anyway, so we add a string view
1028 if (parameter
.Type() == BParameter::B_NULL_PARAMETER
) {
1029 if (parameter
.Group()->ParameterAt(0) == ¶meter
) {
1030 // this is the first parameter in this group, so
1031 // let's use a nice title view
1033 TitleView
*titleView
= new TitleView(BRect(0, 0, 10, 10), parameter
.Name());
1034 titleView
->ResizeToPreferred();
1038 BStringView
*stringView
= new BStringView(BRect(0, 0, 10, 10),
1039 parameter
.Name(), parameter
.Name());
1040 stringView
->SetAlignment(B_ALIGN_CENTER
);
1041 stringView
->ResizeToPreferred();
1049 MessageFilter
*filter
= MessageFilter::FilterFor(view
, parameter
);
1051 view
->AddFilter(filter
);
1058 DefaultMediaTheme::MakeViewFor(BParameter
*parameter
, const BRect
*hintRect
)
1064 rect
.Set(0, 0, 50, 100);
1066 switch (parameter
->Type()) {
1067 case BParameter::B_NULL_PARAMETER
:
1068 // there is no default view for a null parameter
1071 case BParameter::B_DISCRETE_PARAMETER
:
1073 BDiscreteParameter
&discrete
= static_cast<BDiscreteParameter
&>(*parameter
);
1075 if (!strcmp(discrete
.Kind(), B_ENABLE
)
1076 || !strcmp(discrete
.Kind(), B_MUTE
)
1077 || discrete
.CountItems() == 0) {
1078 // create a checkbox item
1080 BCheckBox
*checkBox
= new BCheckBox(rect
, discrete
.Name(),
1081 discrete
.Name(), NULL
);
1082 checkBox
->ResizeToPreferred();
1086 // create a pop up menu field
1088 // ToDo: replace BOptionPopUp (or fix it in Haiku...)
1089 // this is a workaround for a bug in BOptionPopUp - you need to
1090 // know the actual width before creating the object - very nice...
1094 for (int32 i
= 0; i
< discrete
.CountItems(); i
++) {
1095 float labelWidth
= font
.StringWidth(discrete
.ItemNameAt(i
));
1096 if (labelWidth
> width
)
1099 width
+= font
.StringWidth(discrete
.Name()) + 55;
1100 rect
.right
= rect
.left
+ width
;
1102 BOptionPopUp
*popUp
= new BOptionPopUp(rect
, discrete
.Name(),
1103 discrete
.Name(), NULL
);
1105 for (int32 i
= 0; i
< discrete
.CountItems(); i
++) {
1106 popUp
->AddOption(discrete
.ItemNameAt(i
), discrete
.ItemValueAt(i
));
1109 popUp
->ResizeToPreferred();
1115 case BParameter::B_CONTINUOUS_PARAMETER
:
1117 BContinuousParameter
&continuous
= static_cast<BContinuousParameter
&>(*parameter
);
1119 if (!strcmp(continuous
.Kind(), B_MASTER_GAIN
)
1120 || !strcmp(continuous
.Kind(), B_GAIN
)) {
1121 BChannelSlider
*slider
= new BChannelSlider(rect
, continuous
.Name(),
1122 continuous
.Name(), NULL
, B_VERTICAL
, continuous
.CountChannels());
1124 char minLabel
[64], maxLabel
[64];
1126 const char *unit
= continuous
.Unit();
1128 // if we have a unit, print it next to the limit values
1129 sprintf(minLabel
, "%g %s", continuous
.MinValue(), continuous
.Unit());
1130 sprintf(maxLabel
, "%g %s", continuous
.MaxValue(), continuous
.Unit());
1132 sprintf(minLabel
, "%g", continuous
.MinValue());
1133 sprintf(maxLabel
, "%g", continuous
.MaxValue());
1135 slider
->SetLimitLabels(minLabel
, maxLabel
);
1137 float width
, height
;
1138 slider
->GetPreferredSize(&width
, &height
);
1139 slider
->ResizeTo(width
, 190);
1141 // ToDo: take BContinuousParameter::GetResponse() & ValueStep() into account!
1143 for (int32 i
= 0; i
< continuous
.CountChannels(); i
++) {
1144 slider
->SetLimitsFor(i
, int32(continuous
.MinValue() * 1000),
1145 int32(continuous
.MaxValue() * 1000));
1151 BSlider
*slider
= new BSlider(rect
, parameter
->Name(), parameter
->Name(),
1154 float width
, height
;
1155 slider
->GetPreferredSize(&width
, &height
);
1156 slider
->ResizeTo(100, height
);
1162 ERROR("BMediaTheme: Don't know parameter type: 0x%x\n",