xref: /haiku/src/kits/media/DefaultMediaTheme.cpp (revision 4a32f48e70297d9a634646f01e08c2f451ecd6bd)
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 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 
123 class MessageFilter : public BMessageFilter {
124 	public:
125 		static MessageFilter *FilterFor(BView *view, BParameter &parameter);
126 
127 	protected:
128 		MessageFilter();
129 };
130 
131 class 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 
146 class 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 
157 class 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 
170 using namespace DefaultMediaControls;
171 
172 }	// namespace BPrivate
173 
174 
175 const uint32 kMsgParameterChanged = '_mPC';
176 
177 
178 static bool
179 parameter_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 
197 static void
198 start_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 
213 static void
214 stop_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 
228 SeparatorView::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 
242 SeparatorView::~SeparatorView()
243 {
244 }
245 
246 
247 void
248 SeparatorView::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 
270 TitleView::TitleView(const char *title)
271 	: BView(title, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
272 	fTitle(title)
273 {
274 	AdoptSystemColors();
275 }
276 
277 
278 TitleView::~TitleView()
279 {
280 }
281 
282 
283 void
284 TitleView::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 
299 void
300 TitleView::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 
318 CheckBox::CheckBox(const char* name, const char* label,
319 	BDiscreteParameter &parameter)
320 	: BCheckBox(name, label, NULL),
321 	fParameter(parameter)
322 {
323 }
324 
325 
326 CheckBox::~CheckBox()
327 {
328 }
329 
330 
331 void
332 CheckBox::AttachedToWindow()
333 {
334 	BCheckBox::AttachedToWindow();
335 
336 	SetTarget(this);
337 	start_watching_for_parameter_changes(this, fParameter);
338 }
339 
340 
341 void
342 CheckBox::DetachedFromWindow()
343 {
344 	stop_watching_for_parameter_changes(this, fParameter);
345 }
346 
347 
348 OptionPopUp::OptionPopUp(const char* name, const char* label,
349 	BDiscreteParameter &parameter)
350 	: BOptionPopUp(name, label, NULL),
351 	fParameter(parameter)
352 {
353 }
354 
355 
356 OptionPopUp::~OptionPopUp()
357 {
358 }
359 
360 
361 void
362 OptionPopUp::AttachedToWindow()
363 {
364 	BOptionPopUp::AttachedToWindow();
365 
366 	SetTarget(this);
367 	start_watching_for_parameter_changes(this, fParameter);
368 }
369 
370 
371 void
372 OptionPopUp::DetachedFromWindow()
373 {
374 	stop_watching_for_parameter_changes(this, fParameter);
375 }
376 
377 
378 Slider::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 
386 Slider::~Slider()
387 {
388 }
389 
390 
391 void
392 Slider::AttachedToWindow()
393 {
394 	BSlider::AttachedToWindow();
395 
396 	SetTarget(this);
397 	start_watching_for_parameter_changes(this, fParameter);
398 }
399 
400 
401 void
402 Slider::DetachedFromWindow()
403 {
404 	stop_watching_for_parameter_changes(this, fParameter);
405 }
406 
407 
408 ChannelSlider::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 
416 ChannelSlider::~ChannelSlider()
417 {
418 }
419 
420 
421 void
422 ChannelSlider::AttachedToWindow()
423 {
424 	BChannelSlider::AttachedToWindow();
425 
426 	SetTarget(this);
427 	start_watching_for_parameter_changes(this, fParameter);
428 }
429 
430 
431 void
432 ChannelSlider::DetachedFromWindow()
433 {
434 	stop_watching_for_parameter_changes(this, fParameter);
435 
436 	BChannelSlider::DetachedFromWindow();
437 }
438 
439 
440 TextControl::TextControl(const char* name, const char* label,
441 	BTextParameter &parameter)
442 	: BTextControl(name, label, "", NULL),
443 	fParameter(parameter)
444 {
445 }
446 
447 
448 TextControl::~TextControl()
449 {
450 }
451 
452 
453 void
454 TextControl::AttachedToWindow()
455 {
456 	BTextControl::AttachedToWindow();
457 
458 	SetTarget(this);
459 	start_watching_for_parameter_changes(this, fParameter);
460 }
461 
462 
463 void
464 TextControl::DetachedFromWindow()
465 {
466 	stop_watching_for_parameter_changes(this, fParameter);
467 }
468 
469 
470 //	#pragma mark -
471 
472 
473 MessageFilter::MessageFilter()
474 	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
475 {
476 }
477 
478 
479 MessageFilter *
480 MessageFilter::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 
509 ContinuousMessageFilter::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 
530 ContinuousMessageFilter::~ContinuousMessageFilter()
531 {
532 }
533 
534 
535 filter_result
536 ContinuousMessageFilter::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 
586 void
587 ContinuousMessageFilter::_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 
615 DiscreteMessageFilter::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 
642 DiscreteMessageFilter::~DiscreteMessageFilter()
643 {
644 }
645 
646 
647 filter_result
648 DiscreteMessageFilter::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 
712 TextMessageFilter::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 
740 TextMessageFilter::~TextMessageFilter()
741 {
742 }
743 
744 
745 filter_result
746 TextMessageFilter::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 
806 DefaultMediaTheme::DefaultMediaTheme()
807 	: BMediaTheme("Haiku theme", "Haiku built-in theme version 0.1")
808 {
809 	CALLED();
810 }
811 
812 
813 BControl *
814 DefaultMediaTheme::MakeControlFor(BParameter *parameter)
815 {
816 	CALLED();
817 
818 	return MakeViewFor(parameter);
819 }
820 
821 
822 BView *
823 DefaultMediaTheme::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 
878 BView *
879 DefaultMediaTheme::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 */
937 BView *
938 DefaultMediaTheme::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 
974 BControl *
975 DefaultMediaTheme::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