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