tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / media / DefaultMediaTheme.cpp
blob031e05aa727a098be8f878c7faee08ee6e496fbf
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 MessageFilter : public BMessageFilter {
109 public:
110 static MessageFilter *FilterFor(BView *view, BParameter &parameter);
112 protected:
113 MessageFilter();
116 class ContinuousMessageFilter : public MessageFilter {
117 public:
118 ContinuousMessageFilter(BControl *control,
119 BContinuousParameter &parameter);
120 virtual ~ContinuousMessageFilter();
122 virtual filter_result Filter(BMessage *message, BHandler **target);
124 private:
125 void _UpdateControl();
127 BControl *fControl;
128 BContinuousParameter &fParameter;
129 bool fRegistered;
132 class DiscreteMessageFilter : public MessageFilter {
133 public:
134 DiscreteMessageFilter(BControl *control, BDiscreteParameter &parameter);
135 virtual ~DiscreteMessageFilter();
137 virtual filter_result Filter(BMessage *message, BHandler **target);
139 private:
140 BDiscreteParameter &fParameter;
143 } // namespace BPrivate
146 const uint32 kMsgParameterChanged = '_mPC';
149 static bool
150 parameter_should_be_hidden(BParameter &parameter)
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))
157 return false;
159 for (int32 i = 0; i < parameter.CountOutputs(); i++) {
160 if (!strcmp(parameter.OutputAt(0)->Kind(), B_INPUT_MUX))
161 return true;
164 return false;
168 // #pragma mark -
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),
175 fTarget(target),
176 fIsDocumentScroller(false)
178 fContentBounds.Set(-1, -1, -1, -1);
179 SetViewColor(fTarget->ViewColor());
180 target->MoveTo(B_ORIGIN);
181 AddChild(target);
185 DynamicScrollView::~DynamicScrollView()
190 void
191 DynamicScrollView::AttachedToWindow(void)
193 BRect frame = ConvertToScreen(Bounds());
194 BRect windowFrame = Window()->Frame();
196 fIsDocumentScroller = Parent() == NULL
197 && Window() != NULL
198 && Window()->Look() == B_DOCUMENT_WINDOW_LOOK
199 && frame.right == windowFrame.right
200 && frame.bottom == windowFrame.bottom;
202 UpdateBars();
206 void
207 DynamicScrollView::FrameResized(float width, float height)
209 UpdateBars();
213 void
214 DynamicScrollView::FrameMoved(BPoint newPosition)
216 UpdateBars();
220 void
221 DynamicScrollView::GetPreferredSize(float *_width, float *_height)
223 float width = 50;
224 if (fVerticalScrollBar)
225 width += B_V_SCROLL_BAR_WIDTH;
226 float height = 50;
227 if (fHorizontalScrollBar)
228 height += B_H_SCROLL_BAR_HEIGHT;
229 if (_width)
230 *_width = width;
231 if (_height)
232 *_height = height;
236 void
237 DynamicScrollView::SetContentBounds(BRect bounds)
239 fContentBounds = bounds;
240 if (Window())
241 UpdateBars();
245 void
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)
251 float width, height;
252 if (fContentBounds == BRect(-1, -1, -1, -1))
253 fTarget->GetPreferredSize(&width, &height);
254 else {
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,
298 height, B_VERTICAL);
299 AddChild(fVerticalScrollBar);
302 // update the scroll bar range & proportions and layout views
304 bounds = Bounds();
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();
315 if (delta < 0)
316 delta = 0;
318 fHorizontalScrollBar->SetRange(0, delta);
319 fHorizontalScrollBar->SetSteps(1, bounds.Width());
320 fHorizontalScrollBar->SetProportion(bounds.Width() / width);
322 float barWidth = Bounds().Width();
323 if (vertical) {
324 // scrollbars overlap one pixel of the frame
325 barWidth += 1;
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();
335 if (delta < 0)
336 delta = 0;
338 fVerticalScrollBar->SetRange(0, delta);
339 fVerticalScrollBar->SetSteps(1, bounds.Height());
340 fVerticalScrollBar->SetProportion(bounds.Height() / height);
342 float barHeight = Bounds().Height();
343 if (horizontal) {
344 // scrollbars overlap one pixel of the frame
345 barHeight += 1;
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);
356 // #pragma mark -
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()
371 void
372 GroupView::AttachedToWindow()
374 for (int32 i = CountChildren(); i-- > 0;) {
375 BControl *control = dynamic_cast<BControl *>(ChildAt(i));
376 if (control == NULL)
377 continue;
379 control->SetTarget(control);
384 void
385 GroupView::AllAttached()
390 void
391 GroupView::GetPreferredSize(float *_width, float *_height)
393 if (_width)
394 *_width = fContentBounds.Width();
396 if (_height)
397 *_height = fContentBounds.Height();
401 BSize
402 GroupView::MinSize()
404 return BSize(100, 100);
408 BSize
409 GroupView::PreferredSize()
411 return MinSize();
415 BSize
416 GroupView::MaxSize()
418 BSize max;
419 GetPreferredSize(&max.width, &max.height);
420 return max;
424 void
425 GroupView::SetContentBounds(BRect bounds)
427 fContentBounds = bounds;
431 // #pragma mark -
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)
448 void
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());
460 void
461 TabView::Select(int32 tab)
463 BTabView::Select(tab);
465 BView *view = ViewForTab(Selection());
466 if (view != NULL) {
467 BRect rect = ContainerView()->Bounds();
468 view->ResizeTo(rect.Width(), rect.Height());
473 // #pragma mark -
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()
489 void
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));
496 if (fVertical)
497 StrokeLine(BPoint(0, rect.top), BPoint(0, rect.bottom));
498 else
499 StrokeLine(BPoint(rect.left, 0), BPoint(rect.right, 0));
501 SetHighColor(tint_color(color, B_LIGHTEN_1_TINT));
502 if (fVertical)
503 StrokeLine(BPoint(1, rect.top), BPoint(1, rect.bottom));
504 else
505 StrokeLine(BPoint(rect.left, 1), BPoint(rect.right, 1));
509 // #pragma mark -
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);
527 void
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));
542 void
543 TitleView::GetPreferredSize(float *_width, float *_height)
545 if (_width)
546 *_width = StringWidth(fTitle) + 2;
548 if (_height) {
549 font_height fontHeight;
550 GetFontHeight(&fontHeight);
552 *_height = fontHeight.ascent + fontHeight.descent + fontHeight.leading
553 + 8;
558 // #pragma mark -
561 MessageFilter::MessageFilter()
562 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
567 MessageFilter *
568 MessageFilter::FilterFor(BView *view, BParameter &parameter)
570 BControl *control = dynamic_cast<BControl *>(view);
571 if (control == NULL)
572 return NULL;
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 */
584 default:
585 return NULL;
590 // #pragma mark -
593 ContinuousMessageFilter::ContinuousMessageFilter(BControl *control,
594 BContinuousParameter &parameter)
595 : MessageFilter(),
596 fControl(control),
597 fParameter(parameter),
598 fRegistered(false)
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));
607 else
608 ERROR("ContinuousMessageFilter: unknown continuous parameter view\n");
610 // set initial value
611 _UpdateControl();
615 ContinuousMessageFilter::~ContinuousMessageFilter()
620 filter_result
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
628 if (!fRegistered) {
629 if (BMediaRoster* roster = BMediaRoster::CurrentRoster()) {
630 roster->StartWatching(fControl, fParameter.Web()->Node(),
631 B_MEDIA_NEW_PARAMETER_VALUE);
633 fRegistered = true;
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;
664 int32 parameterID;
665 ssize_t size;
666 if (message->FindInt32("parameter", &parameterID) != B_OK
667 || fParameter.ID() != parameterID
668 || message->FindData("node", B_RAW_TYPE, (const void**)&node,
669 &size) != B_OK
670 || fParameter.Web()->Node() != *node)
671 return B_DISPATCH_MESSAGE;
673 _UpdateControl();
674 return B_SKIP_MESSAGE;
677 return B_DISPATCH_MESSAGE;
681 void
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);
692 return;
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]));
707 // #pragma mark -
710 DiscreteMessageFilter::DiscreteMessageFilter(BControl *control,
711 BDiscreteParameter &parameter)
712 : MessageFilter(),
713 fParameter(parameter)
715 // initialize view for us
716 control->SetMessage(new BMessage(kMsgParameterChanged));
718 // set initial value
720 size_t size = sizeof(int32);
721 int32 value;
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", &parameter,
725 parameter.Name(), (int)(parameter.Web()->Node().node));
726 return;
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);
733 } else
734 ERROR("DiscreteMessageFilter: unknown discrete parameter view\n");
738 DiscreteMessageFilter::~DiscreteMessageFilter()
743 filter_result
744 DiscreteMessageFilter::Filter(BMessage *message, BHandler **target)
746 BControl *control;
748 if (message->what != kMsgParameterChanged
749 || (control = dynamic_cast<BControl *>(*target)) == NULL)
750 return B_DISPATCH_MESSAGE;
752 // update view
754 int32 value = 0;
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;
773 // #pragma mark -
776 DefaultMediaTheme::DefaultMediaTheme()
777 : BMediaTheme("Haiku theme", "Haiku built-in theme version 0.1")
779 CALLED();
783 BControl *
784 DefaultMediaTheme::MakeControlFor(BParameter *parameter)
786 CALLED();
788 BRect rect(0, 0, 150, 100);
789 return MakeViewFor(parameter, &rect);
793 BView *
794 DefaultMediaTheme::MakeViewFor(BParameterWeb *web, const BRect *hintRect)
796 CALLED();
798 if (web == NULL)
799 return NULL;
801 BRect rect;
802 if (hintRect)
803 rect = *hintRect;
805 BRect bestRect;
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);
819 if (group == NULL)
820 continue;
822 BView *groupView = MakeViewFor(*group, hintRect ? &rect : NULL);
823 if (groupView == NULL)
824 continue;
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
838 // whole hintRect)
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);
851 if (!hintRect) {
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
866 return tabView;
870 BView *
871 DefaultMediaTheme::MakeViewFor(BParameterGroup& group, const BRect* hintRect)
873 CALLED();
875 if (group.Flags() & B_HIDDEN_PARAMETER)
876 return NULL;
878 BRect rect;
879 if (hintRect != NULL)
880 rect = *hintRect;
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);
889 BList views;
890 for (int32 i = 0; i < group.CountParameters(); i++) {
891 BParameter *parameter = group.ParameterAt(i);
892 if (parameter == NULL)
893 continue;
895 BView *parameterView = MakeSelfHostingViewFor(*parameter,
896 hintRect ? &rect : NULL);
897 if (parameterView == NULL)
898 continue;
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)
924 continue;
926 BView *groupView = MakeViewFor(*subGroup, &rect);
927 if (groupView == NULL)
928 continue;
930 if (i > 0) {
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)
954 return view;
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;
963 bool center = false;
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)
973 continue;
975 // if there is a BChannelSlider (ToDo: or any vertical slider?)
976 // the views will be centered
977 if (dynamic_cast<BChannelSlider *>(parameterView) != NULL)
978 center = true;
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();
997 if (center)
998 subView->MoveTo((width - frame.Width()) / 2, frame.top);
999 else {
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());
1007 return view;
1011 /*! This creates a view that handles all incoming messages itself - that's
1012 what is meant with self-hosting.
1014 BView *
1015 DefaultMediaTheme::MakeSelfHostingViewFor(BParameter& parameter,
1016 const BRect* hintRect)
1018 if (parameter.Flags() & B_HIDDEN_PARAMETER
1019 || parameter_should_be_hidden(parameter))
1020 return NULL;
1022 BView *view = MakeViewFor(&parameter, hintRect);
1023 if (view == NULL) {
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
1027 // here.
1028 if (parameter.Type() == BParameter::B_NULL_PARAMETER) {
1029 if (parameter.Group()->ParameterAt(0) == &parameter) {
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();
1036 return titleView;
1038 BStringView *stringView = new BStringView(BRect(0, 0, 10, 10),
1039 parameter.Name(), parameter.Name());
1040 stringView->SetAlignment(B_ALIGN_CENTER);
1041 stringView->ResizeToPreferred();
1043 return stringView;
1046 return NULL;
1049 MessageFilter *filter = MessageFilter::FilterFor(view, parameter);
1050 if (filter != NULL)
1051 view->AddFilter(filter);
1053 return view;
1057 BControl *
1058 DefaultMediaTheme::MakeViewFor(BParameter *parameter, const BRect *hintRect)
1060 BRect rect;
1061 if (hintRect)
1062 rect = *hintRect;
1063 else
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
1069 return NULL;
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();
1084 return checkBox;
1085 } else {
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...
1092 BFont font;
1093 float width = 0;
1094 for (int32 i = 0; i < discrete.CountItems(); i++) {
1095 float labelWidth = font.StringWidth(discrete.ItemNameAt(i));
1096 if (labelWidth > width)
1097 width = labelWidth;
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();
1111 return popUp;
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();
1127 if (unit[0]) {
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());
1131 } else {
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));
1148 return slider;
1151 BSlider *slider = new BSlider(rect, parameter->Name(), parameter->Name(),
1152 NULL, 0, 100);
1154 float width, height;
1155 slider->GetPreferredSize(&width, &height);
1156 slider->ResizeTo(100, height);
1158 return slider;
1161 default:
1162 ERROR("BMediaTheme: Don't know parameter type: 0x%x\n",
1163 parameter->Type());
1165 return NULL;