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