xref: /haiku/src/kits/media/DefaultMediaTheme.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2019, Haiku, Inc. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "DefaultMediaTheme.h"
9 
10 #include <Box.h>
11 #include <Button.h>
12 #include <ChannelSlider.h>
13 #include <CheckBox.h>
14 #include <GroupView.h>
15 #include <MediaRoster.h>
16 #include <MenuField.h>
17 #include <MessageFilter.h>
18 #include <OptionPopUp.h>
19 #include <ParameterWeb.h>
20 #include <ScrollBar.h>
21 #include <ScrollView.h>
22 #include <Slider.h>
23 #include <SpaceLayoutItem.h>
24 #include <StringView.h>
25 #include <TabView.h>
26 #include <TextControl.h>
27 #include <Window.h>
28 
29 #include "MediaDebug.h"
30 
31 
32 using namespace BPrivate;
33 
34 
35 namespace BPrivate {
36 
37 class SeparatorView : public BView {
38 	public:
39 		SeparatorView(orientation orientation);
40 		virtual ~SeparatorView();
41 
42 		virtual void Draw(BRect updateRect);
43 
44 	private:
45 		bool	fVertical;
46 };
47 
48 class TitleView : public BView {
49 	public:
50 		TitleView(const char *title);
51 		virtual ~TitleView();
52 
53 		virtual void Draw(BRect updateRect);
54 		virtual void GetPreferredSize(float *width, float *height);
55 
56 	private:
57 		BString fTitle;
58 };
59 
60 class CheckBox : public BCheckBox {
61 	public:
62 		CheckBox(const char* name, const char* label,
63 			BDiscreteParameter &parameter);
64 		virtual ~CheckBox();
65 
66 		virtual void AttachedToWindow();
67 		virtual void DetachedFromWindow();
68 	private:
69 		BDiscreteParameter &fParameter;
70 };
71 
72 class OptionPopUp : public BOptionPopUp {
73 	public:
74 		OptionPopUp(const char* name, const char* label,
75 			BDiscreteParameter &parameter);
76 		virtual ~OptionPopUp();
77 
78 		virtual void AttachedToWindow();
79 		virtual void DetachedFromWindow();
80 	private:
81 		BDiscreteParameter &fParameter;
82 };
83 
84 class Slider : public BSlider {
85 	public:
86 		Slider(const char* name, const char*label, int32 minValue,
87 			int32 maxValue, BContinuousParameter &parameter);
88 		virtual ~Slider();
89 
90 		virtual void AttachedToWindow();
91 		virtual void DetachedFromWindow();
92 	private:
93 		BContinuousParameter &fParameter;
94 };
95 
96 class ChannelSlider : public BChannelSlider {
97 	public:
98 		ChannelSlider(const char* name, const char* label,
99 			orientation orientation, int32 channels,
100 			BContinuousParameter &parameter);
101 		virtual ~ChannelSlider();
102 
103 		virtual void AttachedToWindow();
104 		virtual void DetachedFromWindow();
105 	private:
106 		BContinuousParameter &fParameter;
107 };
108 
109 class MessageFilter : public BMessageFilter {
110 	public:
111 		static MessageFilter *FilterFor(BView *view, BParameter &parameter);
112 
113 	protected:
114 		MessageFilter();
115 };
116 
117 class ContinuousMessageFilter : public MessageFilter {
118 	public:
119 		ContinuousMessageFilter(BControl *control,
120 			BContinuousParameter &parameter);
121 		virtual ~ContinuousMessageFilter();
122 
123 		virtual filter_result Filter(BMessage *message, BHandler **target);
124 
125 	private:
126 		void _UpdateControl();
127 
128 		BControl				*fControl;
129 		BContinuousParameter	&fParameter;
130 };
131 
132 class DiscreteMessageFilter : public MessageFilter {
133 	public:
134 		DiscreteMessageFilter(BControl *control, BDiscreteParameter &parameter);
135 		virtual ~DiscreteMessageFilter();
136 
137 		virtual filter_result Filter(BMessage *message, BHandler **target);
138 
139 	private:
140 		BDiscreteParameter	&fParameter;
141 };
142 
143 }	// namespace BPrivate
144 
145 
146 const uint32 kMsgParameterChanged = '_mPC';
147 
148 
149 static bool
150 parameter_should_be_hidden(BParameter &parameter)
151 {
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;
158 
159 	for (int32 i = 0; i < parameter.CountOutputs(); i++) {
160 		if (!strcmp(parameter.OutputAt(0)->Kind(), B_INPUT_MUX))
161 			return true;
162 	}
163 
164 	return false;
165 }
166 
167 
168 static void
169 start_watching_for_parameter_changes(BControl* control, BParameter &parameter)
170 {
171 	BMediaRoster* roster = BMediaRoster::CurrentRoster();
172 	if (roster == NULL)
173 		return;
174 
175 	if (roster->StartWatching(control, parameter.Web()->Node(),
176 			B_MEDIA_NEW_PARAMETER_VALUE) != B_OK) {
177 		fprintf(stderr, "DefaultMediaTheme: Failed to start watching parameter"
178 			"\"%s\"\n", parameter.Name());
179 		return;
180 	}
181 }
182 
183 
184 static void
185 stop_watching_for_parameter_changes(BControl* control, BParameter &parameter)
186 {
187 	BMediaRoster* roster = BMediaRoster::CurrentRoster();
188 	if (roster == NULL)
189 		return;
190 
191 	roster->StopWatching(control, parameter.Web()->Node(),
192 		B_MEDIA_NEW_PARAMETER_VALUE);
193 }
194 
195 
196 //	#pragma mark -
197 
198 
199 SeparatorView::SeparatorView(orientation orientation)
200 	: BView("-", B_WILL_DRAW),
201 	fVertical(orientation == B_VERTICAL)
202 {
203 	if (fVertical) {
204 		SetExplicitMinSize(BSize(5, 0));
205 		SetExplicitMaxSize(BSize(5, MaxSize().height));
206 	} else {
207 		SetExplicitMinSize(BSize(0, 5));
208 		SetExplicitMaxSize(BSize(MaxSize().width, 5));
209 	}
210 }
211 
212 
213 SeparatorView::~SeparatorView()
214 {
215 }
216 
217 
218 void
219 SeparatorView::Draw(BRect updateRect)
220 {
221 	rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR);
222 	BRect rect = updateRect & Bounds();
223 
224 	SetHighColor(tint_color(color, B_DARKEN_1_TINT));
225 	if (fVertical)
226 		StrokeLine(BPoint(0, rect.top), BPoint(0, rect.bottom));
227 	else
228 		StrokeLine(BPoint(rect.left, 0), BPoint(rect.right, 0));
229 
230 	SetHighColor(tint_color(color, B_LIGHTEN_1_TINT));
231 	if (fVertical)
232 		StrokeLine(BPoint(1, rect.top), BPoint(1, rect.bottom));
233 	else
234 		StrokeLine(BPoint(rect.left, 1), BPoint(rect.right, 1));
235 }
236 
237 
238 //	#pragma mark -
239 
240 
241 TitleView::TitleView(const char *title)
242 	: BView(title, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
243 	fTitle(title)
244 {
245 	AdoptSystemColors();
246 }
247 
248 
249 TitleView::~TitleView()
250 {
251 }
252 
253 
254 void
255 TitleView::Draw(BRect updateRect)
256 {
257 	BRect rect(Bounds());
258 	rect.left = (rect.Width() - StringWidth(fTitle)) / 2;
259 
260 	SetDrawingMode(B_OP_COPY);
261 	SetHighColor(tint_color(ViewColor(), B_LIGHTEN_2_TINT));
262 	DrawString(fTitle, BPoint(rect.left + 1, rect.bottom - 8));
263 
264 	SetDrawingMode(B_OP_OVER);
265 	SetHighColor(80, 20, 20);
266 	DrawString(fTitle, BPoint(rect.left, rect.bottom - 9));
267 }
268 
269 
270 void
271 TitleView::GetPreferredSize(float *_width, float *_height)
272 {
273 	if (_width)
274 		*_width = StringWidth(fTitle) + 2;
275 
276 	if (_height) {
277 		font_height fontHeight;
278 		GetFontHeight(&fontHeight);
279 
280 		*_height = fontHeight.ascent + fontHeight.descent + fontHeight.leading
281 			+ 8;
282 	}
283 }
284 
285 
286 //	#pragma mark -
287 
288 
289 CheckBox::CheckBox(const char* name, const char* label,
290 	BDiscreteParameter &parameter)
291 	: BCheckBox(name, label, NULL),
292 	fParameter(parameter)
293 {
294 }
295 
296 
297 CheckBox::~CheckBox()
298 {
299 }
300 
301 
302 void
303 CheckBox::AttachedToWindow()
304 {
305 	BCheckBox::AttachedToWindow();
306 
307 	SetTarget(this);
308 	start_watching_for_parameter_changes(this, fParameter);
309 }
310 
311 
312 void
313 CheckBox::DetachedFromWindow()
314 {
315 	stop_watching_for_parameter_changes(this, fParameter);
316 }
317 
318 
319 OptionPopUp::OptionPopUp(const char* name, const char* label,
320 	BDiscreteParameter &parameter)
321 	: BOptionPopUp(name, label, NULL),
322 	fParameter(parameter)
323 {
324 }
325 
326 
327 OptionPopUp::~OptionPopUp()
328 {
329 }
330 
331 
332 void
333 OptionPopUp::AttachedToWindow()
334 {
335 	BOptionPopUp::AttachedToWindow();
336 
337 	SetTarget(this);
338 	start_watching_for_parameter_changes(this, fParameter);
339 }
340 
341 
342 void
343 OptionPopUp::DetachedFromWindow()
344 {
345 	stop_watching_for_parameter_changes(this, fParameter);
346 }
347 
348 
349 Slider::Slider(const char* name, const char* label, int32 minValue,
350 	int32 maxValue, BContinuousParameter &parameter)
351 	: BSlider(name, label, NULL, minValue, maxValue, B_HORIZONTAL),
352 	fParameter(parameter)
353 {
354 }
355 
356 
357 Slider::~Slider()
358 {
359 }
360 
361 
362 void
363 Slider::AttachedToWindow()
364 {
365 	BSlider::AttachedToWindow();
366 
367 	SetTarget(this);
368 	start_watching_for_parameter_changes(this, fParameter);
369 }
370 
371 
372 void
373 Slider::DetachedFromWindow()
374 {
375 	stop_watching_for_parameter_changes(this, fParameter);
376 }
377 
378 
379 ChannelSlider::ChannelSlider(const char* name, const char* label,
380 	orientation orientation, int32 channels, BContinuousParameter &parameter)
381 	: BChannelSlider(name, label, NULL, orientation, channels),
382 	fParameter(parameter)
383 {
384 }
385 
386 
387 ChannelSlider::~ChannelSlider()
388 {
389 }
390 
391 
392 void
393 ChannelSlider::AttachedToWindow()
394 {
395 	BChannelSlider::AttachedToWindow();
396 
397 	SetTarget(this);
398 	start_watching_for_parameter_changes(this, fParameter);
399 }
400 
401 
402 void
403 ChannelSlider::DetachedFromWindow()
404 {
405 	stop_watching_for_parameter_changes(this, fParameter);
406 
407 	BChannelSlider::DetachedFromWindow();
408 }
409 
410 
411 //	#pragma mark -
412 
413 
414 MessageFilter::MessageFilter()
415 	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
416 {
417 }
418 
419 
420 MessageFilter *
421 MessageFilter::FilterFor(BView *view, BParameter &parameter)
422 {
423 	BControl *control = dynamic_cast<BControl *>(view);
424 	if (control == NULL)
425 		return NULL;
426 
427 	switch (parameter.Type()) {
428 		case BParameter::B_CONTINUOUS_PARAMETER:
429 			return new ContinuousMessageFilter(control,
430 				static_cast<BContinuousParameter &>(parameter));
431 
432 		case BParameter::B_DISCRETE_PARAMETER:
433 			return new DiscreteMessageFilter(control,
434 				static_cast<BDiscreteParameter &>(parameter));
435 
436 		case BParameter::B_NULL_PARAMETER: /* fall through */
437 		default:
438 			return NULL;
439 	}
440 }
441 
442 
443 //	#pragma mark -
444 
445 
446 ContinuousMessageFilter::ContinuousMessageFilter(BControl *control,
447 		BContinuousParameter &parameter)
448 	: MessageFilter(),
449 	fControl(control),
450 	fParameter(parameter)
451 {
452 	// initialize view for us
453 	control->SetMessage(new BMessage(kMsgParameterChanged));
454 
455 	if (BSlider *slider = dynamic_cast<BSlider *>(fControl))
456 		slider->SetModificationMessage(new BMessage(kMsgParameterChanged));
457 	else if (BChannelSlider *slider = dynamic_cast<BChannelSlider *>(fControl))
458 		slider->SetModificationMessage(new BMessage(kMsgParameterChanged));
459 	else
460 		ERROR("ContinuousMessageFilter: unknown continuous parameter view\n");
461 
462 	// set initial value
463 	_UpdateControl();
464 }
465 
466 
467 ContinuousMessageFilter::~ContinuousMessageFilter()
468 {
469 }
470 
471 
472 filter_result
473 ContinuousMessageFilter::Filter(BMessage *message, BHandler **target)
474 {
475 	if (*target != fControl)
476 		return B_DISPATCH_MESSAGE;
477 
478 	if (message->what == kMsgParameterChanged) {
479 		// update parameter from control
480 		// TODO: support for response!
481 
482 		float value[fParameter.CountChannels()];
483 
484 		if (BSlider *slider = dynamic_cast<BSlider *>(fControl)) {
485 			value[0] = (float)(slider->Value() / 1000.0);
486 		} else if (BChannelSlider *slider
487 				= dynamic_cast<BChannelSlider *>(fControl)) {
488 			for (int32 i = 0; i < fParameter.CountChannels(); i++)
489 				value[i] = (float)(slider->ValueFor(i) / 1000.0);
490 		}
491 
492 		TRACE("ContinuousMessageFilter::Filter: update view %s, %" B_PRId32
493 			" channels\n", fControl->Name(), fParameter.CountChannels());
494 
495 		if (fParameter.SetValue((void *)value, sizeof(value),
496 				-1) < B_OK) {
497 			ERROR("ContinuousMessageFilter::Filter: Could not set parameter "
498 				"value for %p\n", &fParameter);
499 			return B_DISPATCH_MESSAGE;
500 		}
501 		return B_SKIP_MESSAGE;
502 	}
503 	if (message->what == B_MEDIA_NEW_PARAMETER_VALUE) {
504 		// update view from parameter -- if the message concerns us
505 		const media_node* node;
506 		int32 parameterID;
507 		ssize_t size;
508 		if (message->FindInt32("parameter", &parameterID) != B_OK
509 			|| fParameter.ID() != parameterID
510 			|| message->FindData("node", B_RAW_TYPE, (const void**)&node,
511 					&size) != B_OK
512 			|| fParameter.Web()->Node() != *node)
513 			return B_DISPATCH_MESSAGE;
514 
515 		_UpdateControl();
516 		return B_SKIP_MESSAGE;
517 	}
518 
519 	return B_DISPATCH_MESSAGE;
520 }
521 
522 
523 void
524 ContinuousMessageFilter::_UpdateControl()
525 {
526 	// TODO: response support!
527 
528 	float value[fParameter.CountChannels()];
529 	size_t size = sizeof(value);
530 	if (fParameter.GetValue((void *)&value, &size, NULL) < B_OK) {
531 		ERROR("ContinuousMessageFilter: Could not get value for continuous "
532 			"parameter %p (name '%s', node %d)\n", &fParameter,
533 			fParameter.Name(), (int)fParameter.Web()->Node().node);
534 		return;
535 	}
536 
537 	if (BSlider *slider = dynamic_cast<BSlider *>(fControl)) {
538 		slider->SetValue((int32) (1000 * value[0]));
539 		slider->SetModificationMessage(new BMessage(kMsgParameterChanged));
540 	} else if (BChannelSlider *slider
541 			= dynamic_cast<BChannelSlider *>(fControl)) {
542 		for (int32 i = 0; i < fParameter.CountChannels(); i++) {
543 			slider->SetValueFor(i, (int32) (1000 * value[i]));
544 		}
545 	}
546 }
547 
548 
549 //	#pragma mark -
550 
551 
552 DiscreteMessageFilter::DiscreteMessageFilter(BControl *control,
553 		BDiscreteParameter &parameter)
554 	: MessageFilter(),
555 	fParameter(parameter)
556 {
557 	// initialize view for us
558 	control->SetMessage(new BMessage(kMsgParameterChanged));
559 
560 	// set initial value
561 	size_t size = sizeof(int32);
562 	int32 value;
563 	if (parameter.GetValue((void *)&value, &size, NULL) < B_OK) {
564 		ERROR("DiscreteMessageFilter: Could not get value for discrete "
565 			"parameter %p (name '%s', node %d)\n", &parameter,
566 			parameter.Name(), (int)(parameter.Web()->Node().node));
567 		return;
568 	}
569 
570 	if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) {
571 		checkBox->SetValue(value);
572 	} else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) {
573 		popUp->SelectOptionFor(value);
574 	} else
575 		ERROR("DiscreteMessageFilter: unknown discrete parameter view\n");
576 }
577 
578 
579 DiscreteMessageFilter::~DiscreteMessageFilter()
580 {
581 }
582 
583 
584 filter_result
585 DiscreteMessageFilter::Filter(BMessage *message, BHandler **target)
586 {
587 	BControl *control;
588 
589 	if ((control = dynamic_cast<BControl *>(*target)) == NULL)
590 		return B_DISPATCH_MESSAGE;
591 
592 	if (message->what == B_MEDIA_NEW_PARAMETER_VALUE) {
593 		TRACE("DiscreteMessageFilter::Filter: Got a new parameter value\n");
594 		const media_node* node;
595 		int32 parameterID;
596 		ssize_t size;
597 		if (message->FindInt32("parameter", &parameterID) != B_OK
598 			|| fParameter.ID() != parameterID
599 			|| message->FindData("node", B_RAW_TYPE, (const void**)&node,
600 					&size) != B_OK
601 			|| fParameter.Web()->Node() != *node)
602 			return B_DISPATCH_MESSAGE;
603 
604 		int32 value = 0;
605 		size_t valueSize = sizeof(int32);
606 		if (fParameter.GetValue((void*)&value, &valueSize, NULL) < B_OK) {
607 			ERROR("DiscreteMessageFilter: Could not get value for continuous "
608 			"parameter %p (name '%s', node %d)\n", &fParameter,
609 			fParameter.Name(), (int)fParameter.Web()->Node().node);
610 			return B_SKIP_MESSAGE;
611 		}
612 		if (BCheckBox* checkBox = dynamic_cast<BCheckBox*>(control)) {
613 			checkBox->SetValue(value);
614 		} else if (BOptionPopUp* popUp = dynamic_cast<BOptionPopUp*>(control)) {
615 			popUp->SetValue(value);
616 		}
617 
618 		return B_SKIP_MESSAGE;
619 	}
620 
621 	if (message->what != kMsgParameterChanged)
622 		return B_DISPATCH_MESSAGE;
623 
624 	// update view
625 
626 	int32 value = 0;
627 
628 	if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) {
629 		value = checkBox->Value();
630 	} else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) {
631 		popUp->SelectedOption(NULL, &value);
632 	}
633 
634 	TRACE("DiscreteMessageFilter::Filter: update view %s, value = %"
635 		B_PRId32 "\n", control->Name(), value);
636 
637 	if (fParameter.SetValue((void *)&value, sizeof(value), -1) < B_OK) {
638 		ERROR("DiscreteMessageFilter::Filter: Could not set parameter value for %p\n", &fParameter);
639 		return B_DISPATCH_MESSAGE;
640 	}
641 
642 	return B_SKIP_MESSAGE;
643 }
644 
645 
646 //	#pragma mark -
647 
648 
649 DefaultMediaTheme::DefaultMediaTheme()
650 	: BMediaTheme("Haiku theme", "Haiku built-in theme version 0.1")
651 {
652 	CALLED();
653 }
654 
655 
656 BControl *
657 DefaultMediaTheme::MakeControlFor(BParameter *parameter)
658 {
659 	CALLED();
660 
661 	return MakeViewFor(parameter);
662 }
663 
664 
665 BView *
666 DefaultMediaTheme::MakeViewFor(BParameterWeb *web, const BRect *hintRect)
667 {
668 	CALLED();
669 
670 	if (web == NULL)
671 		return NULL;
672 
673 	// do we have more than one attached parameter group?
674 	// if so, use a tabbed view with a tab for each group
675 
676 	BTabView *tabView = NULL;
677 	if (web->CountGroups() > 1)
678 		tabView = new BTabView("web");
679 
680 	for (int32 i = 0; i < web->CountGroups(); i++) {
681 		BParameterGroup *group = web->GroupAt(i);
682 		if (group == NULL)
683 			continue;
684 
685 		BView *groupView = MakeViewFor(*group);
686 		if (groupView == NULL)
687 			continue;
688 
689 		BScrollView *scrollView = new BScrollView(groupView->Name(), groupView, 0,
690 			true, true, B_NO_BORDER);
691 		scrollView->SetExplicitMinSize(BSize(B_V_SCROLL_BAR_WIDTH,
692 			B_H_SCROLL_BAR_HEIGHT));
693 		if (tabView == NULL) {
694 			if (hintRect != NULL) {
695 				scrollView->MoveTo(hintRect->LeftTop());
696 				scrollView->ResizeTo(hintRect->Size());
697 			} else {
698 				scrollView->ResizeTo(600, 400);
699 					// See comment below.
700 			}
701 			return scrollView;
702 		}
703 		tabView->AddTab(scrollView);
704 	}
705 
706 	if (hintRect != NULL) {
707 		tabView->MoveTo(hintRect->LeftTop());
708 		tabView->ResizeTo(hintRect->Size());
709 	} else {
710 		// Apps not using layouted views may expect PreferredSize() to return
711 		// a sane value right away, and use this to set the maximum size of
712 		// things. Layouted views return their current size until the view has
713 		// been attached to the window, so in order to prevent breakage, we set
714 		// a default view size here.
715 		tabView->ResizeTo(600, 400);
716 	}
717 	return tabView;
718 }
719 
720 
721 BView *
722 DefaultMediaTheme::MakeViewFor(BParameterGroup& group)
723 {
724 	CALLED();
725 
726 	if (group.Flags() & B_HIDDEN_PARAMETER)
727 		return NULL;
728 
729 	BGroupView *view = new BGroupView(group.Name(), B_HORIZONTAL,
730 		B_USE_HALF_ITEM_SPACING);
731 	BGroupLayout *layout = view->GroupLayout();
732 	layout->SetInsets(B_USE_HALF_ITEM_INSETS);
733 
734 	// Create and add the parameter views
735 	if (group.CountParameters() > 0) {
736 		BGroupView *paramView = new BGroupView(group.Name(), B_VERTICAL,
737 			B_USE_HALF_ITEM_SPACING);
738 		BGroupLayout *paramLayout = paramView->GroupLayout();
739 		paramLayout->SetInsets(0);
740 
741 		for (int32 i = 0; i < group.CountParameters(); i++) {
742 			BParameter *parameter = group.ParameterAt(i);
743 			if (parameter == NULL)
744 				continue;
745 
746 			BView *parameterView = MakeSelfHostingViewFor(*parameter);
747 			if (parameterView == NULL)
748 				continue;
749 
750 			paramLayout->AddView(parameterView);
751 		}
752 		paramLayout->AddItem(BSpaceLayoutItem::CreateHorizontalStrut(10));
753 		layout->AddView(paramView);
754 	}
755 
756 	// Add the sub-group views
757 	for (int32 i = 0; i < group.CountGroups(); i++) {
758 		BParameterGroup *subGroup = group.GroupAt(i);
759 		if (subGroup == NULL)
760 			continue;
761 
762 		BView *groupView = MakeViewFor(*subGroup);
763 		if (groupView == NULL)
764 			continue;
765 
766 		if (i > 0)
767 			layout->AddView(new SeparatorView(B_VERTICAL));
768 
769 		layout->AddView(groupView);
770 	}
771 
772 	layout->AddItem(BSpaceLayoutItem::CreateGlue());
773 	return view;
774 }
775 
776 
777 /*!	This creates a view that handles all incoming messages itself - that's
778 	what is meant with self-hosting.
779 */
780 BView *
781 DefaultMediaTheme::MakeSelfHostingViewFor(BParameter& parameter)
782 {
783 	if (parameter.Flags() & B_HIDDEN_PARAMETER
784 		|| parameter_should_be_hidden(parameter))
785 		return NULL;
786 
787 	BView *view = MakeViewFor(&parameter);
788 	if (view == NULL) {
789 		// The MakeViewFor() method above returns a BControl - which we
790 		// don't need for a null parameter; that's why it returns NULL.
791 		// But we want to see something anyway, so we add a string view
792 		// here.
793 		if (parameter.Type() == BParameter::B_NULL_PARAMETER) {
794 			if (parameter.Group()->ParameterAt(0) == &parameter) {
795 				// this is the first parameter in this group, so
796 				// let's use a nice title view
797 				return new TitleView(parameter.Name());
798 			}
799 			BStringView *stringView = new BStringView(parameter.Name(),
800 				parameter.Name());
801 			stringView->SetAlignment(B_ALIGN_CENTER);
802 
803 			return stringView;
804 		}
805 
806 		return NULL;
807 	}
808 
809 	MessageFilter *filter = MessageFilter::FilterFor(view, parameter);
810 	if (filter != NULL)
811 		view->AddFilter(filter);
812 
813 	return view;
814 }
815 
816 
817 BControl *
818 DefaultMediaTheme::MakeViewFor(BParameter *parameter)
819 {
820 	switch (parameter->Type()) {
821 		case BParameter::B_NULL_PARAMETER:
822 			// there is no default view for a null parameter
823 			return NULL;
824 
825 		case BParameter::B_DISCRETE_PARAMETER:
826 		{
827 			BDiscreteParameter &discrete
828 				= static_cast<BDiscreteParameter &>(*parameter);
829 
830 			if (!strcmp(discrete.Kind(), B_ENABLE)
831 				|| !strcmp(discrete.Kind(), B_MUTE)
832 				|| discrete.CountItems() == 0) {
833 				return new CheckBox(discrete.Name(), discrete.Name(), discrete);
834 			} else {
835 				BOptionPopUp *popUp = new OptionPopUp(discrete.Name(),
836 					discrete.Name(), discrete);
837 
838 				for (int32 i = 0; i < discrete.CountItems(); i++) {
839 					popUp->AddOption(discrete.ItemNameAt(i),
840 						discrete.ItemValueAt(i));
841 				}
842 
843 				return popUp;
844 			}
845 		}
846 
847 		case BParameter::B_CONTINUOUS_PARAMETER:
848 		{
849 			BContinuousParameter &continuous
850 				= static_cast<BContinuousParameter &>(*parameter);
851 
852 			if (!strcmp(continuous.Kind(), B_MASTER_GAIN)
853 				|| !strcmp(continuous.Kind(), B_GAIN)) {
854 				BChannelSlider *slider = new ChannelSlider(
855 					continuous.Name(), continuous.Name(), B_VERTICAL,
856 					continuous.CountChannels(), continuous);
857 
858 				BString minLabel, maxLabel;
859 				const char *unit = continuous.Unit();
860 				if (unit[0]) {
861 					// if we have a unit, print it next to the limit values
862 					minLabel.SetToFormat("%g %s", continuous.MinValue(), continuous.Unit());
863 					maxLabel.SetToFormat("%g %s", continuous.MaxValue(), continuous.Unit());
864 				} else {
865 					minLabel.SetToFormat("%g", continuous.MinValue());
866 					maxLabel.SetToFormat("%g", continuous.MaxValue());
867 				}
868 				slider->SetLimitLabels(minLabel, maxLabel);
869 
870 				// ToDo: take BContinuousParameter::GetResponse() & ValueStep() into account!
871 
872 				for (int32 i = 0; i < continuous.CountChannels(); i++) {
873 					slider->SetLimitsFor(i, int32(continuous.MinValue() * 1000),
874 						int32(continuous.MaxValue() * 1000));
875 				}
876 
877 				return slider;
878 			}
879 
880 			BSlider *slider = new Slider(parameter->Name(),
881 				parameter->Name(), 0, 100, continuous);
882 
883 			return slider;
884 		}
885 
886 		default:
887 			ERROR("BMediaTheme: Don't know parameter type: 0x%x\n",
888 				parameter->Type());
889 	}
890 	return NULL;
891 }
892 
893