xref: /haiku/src/kits/media/DefaultMediaTheme.cpp (revision d5cd5d63ff0ad395989db6cf4841a64d5b545d1d)
1 /*
2 ** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 ** Distributed under the terms of the OpenBeOS License.
4 */
5 
6 
7 #include "DefaultMediaTheme.h"
8 #include "debug.h"
9 
10 #include <ParameterWeb.h>
11 
12 #include <Slider.h>
13 #include <StringView.h>
14 #include <Button.h>
15 #include <TextControl.h>
16 #include <OptionPopUp.h>
17 #include <ChannelSlider.h>
18 #include <Box.h>
19 #include <CheckBox.h>
20 #include <TabView.h>
21 #include <MenuField.h>
22 #include <MessageFilter.h>
23 
24 
25 using namespace BPrivate;
26 
27 
28 namespace BPrivate {
29 
30 class GroupView : public BView {
31 	public:
32 		GroupView(BRect frame, const char *name);
33 		virtual ~GroupView();
34 
35 		virtual void AttachedToWindow();
36 
37 	private:
38 };
39 
40 class SeparatorView : public BView {
41 	public:
42 		SeparatorView(BRect frame);
43 		virtual ~SeparatorView();
44 
45 		virtual void Draw(BRect updateRect);
46 
47 	private:
48 		bool	fVertical;
49 };
50 
51 class TitleView : public BView {
52 	public:
53 		TitleView(BRect frame, const char *title);
54 		virtual ~TitleView();
55 
56 		virtual void Draw(BRect updateRect);
57 		virtual void GetPreferredSize(float *width, float *height);
58 
59 	private:
60 		const char *fTitle;
61 };
62 
63 class MessageFilter : public BMessageFilter {
64 	public:
65 		static MessageFilter *FilterFor(BView *view, BParameter &parameter);
66 
67 	protected:
68 		MessageFilter();
69 };
70 
71 class ContinuousMessageFilter : public MessageFilter {
72 	public:
73 		ContinuousMessageFilter(BControl *control, BContinuousParameter &parameter);
74 		virtual ~ContinuousMessageFilter();
75 
76 		virtual filter_result Filter(BMessage *message, BHandler **target);
77 
78 	private:
79 		BContinuousParameter	&fParameter;
80 };
81 
82 class DiscreteMessageFilter : public MessageFilter {
83 	public:
84 		DiscreteMessageFilter(BControl *control, BDiscreteParameter &parameter);
85 		virtual ~DiscreteMessageFilter();
86 
87 		virtual filter_result Filter(BMessage *message, BHandler **target);
88 
89 	private:
90 		BDiscreteParameter	&fParameter;
91 };
92 
93 }	// namespace BPrivate
94 
95 
96 const uint32 kMsgParameterChanged = '_mPC';
97 
98 
99 static bool
100 parameter_should_be_hidden(BParameter &parameter)
101 {
102 	// ToDo: note, this is probably completely stupid, but it's the only
103 	// way I could safely remove the null parameters that are not shown
104 	// by the R5 media theme
105 	if (parameter.Type() != BParameter::B_NULL_PARAMETER
106 		|| strcmp(parameter.Kind(), B_WEB_PHYSICAL_INPUT))
107 		return false;
108 
109 	for (int32 i = 0; i < parameter.CountOutputs(); i++) {
110 		if (!strcmp(parameter.OutputAt(0)->Kind(), B_INPUT_MUX))
111 			return true;
112 	}
113 
114 	return false;
115 }
116 
117 
118 //	#pragma mark -
119 
120 
121 GroupView::GroupView(BRect frame, const char *name)
122 	: BView(frame, name, B_FOLLOW_NONE, B_WILL_DRAW)
123 {
124 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
125 
126 	// ToDo: show scroll bars if necessary!
127 }
128 
129 
130 GroupView::~GroupView()
131 {
132 }
133 
134 
135 void
136 GroupView::AttachedToWindow()
137 {
138 	for (int32 i = CountChildren(); i-- > 0;) {
139 		BControl *control = dynamic_cast<BControl *>(ChildAt(i));
140 		if (control == NULL)
141 			continue;
142 
143 		control->SetTarget(control);
144 	}
145 }
146 
147 
148 //	#pragma mark -
149 
150 
151 SeparatorView::SeparatorView(BRect frame)
152 	: BView(frame, "-", B_FOLLOW_NONE, B_WILL_DRAW)
153 {
154 	fVertical = frame.Width() < frame.Height();
155 	SetViewColor(B_TRANSPARENT_COLOR);
156 }
157 
158 
159 SeparatorView::~SeparatorView()
160 {
161 }
162 
163 
164 void
165 SeparatorView::Draw(BRect updateRect)
166 {
167 	rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR);
168 	BRect rect = updateRect & Bounds();
169 
170 	SetHighColor(tint_color(color, B_DARKEN_1_TINT));
171 	if (fVertical)
172 		StrokeLine(BPoint(0, rect.top), BPoint(0, rect.bottom));
173 	else
174 		StrokeLine(BPoint(rect.left, 0), BPoint(rect.right, 0));
175 
176 	SetHighColor(tint_color(color, B_LIGHTEN_1_TINT));
177 	if (fVertical)
178 		StrokeLine(BPoint(1, rect.top), BPoint(1, rect.bottom));
179 	else
180 		StrokeLine(BPoint(rect.left, 1), BPoint(rect.right, 1));
181 }
182 
183 
184 //	#pragma mark -
185 
186 
187 TitleView::TitleView(BRect frame, const char *title)
188 	: BView(frame, title, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW)
189 {
190 	fTitle = strdup(title);
191 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
192 	SetLowColor(ViewColor());
193 }
194 
195 
196 TitleView::~TitleView()
197 {
198 	free((char *)fTitle);
199 }
200 
201 
202 void
203 TitleView::Draw(BRect updateRect)
204 {
205 	BRect rect(Bounds());
206 
207 	SetDrawingMode(B_OP_COPY);
208 	SetHighColor(240, 240, 240);
209 	DrawString(fTitle, BPoint(rect.left + 1, rect.bottom - 9));
210 
211 	SetDrawingMode(B_OP_OVER);
212 	SetHighColor(80, 20, 20);
213 	DrawString(fTitle, BPoint(rect.left, rect.bottom - 8));
214 }
215 
216 
217 void
218 TitleView::GetPreferredSize(float *_width, float *_height)
219 {
220 	if (_width)
221 		*_width = StringWidth(fTitle) + 2;
222 
223 	if (_height) {
224 		font_height fontHeight;
225 		GetFontHeight(&fontHeight);
226 
227 		*_height = fontHeight.ascent + fontHeight.descent + fontHeight.leading + 8;
228 	}
229 }
230 
231 
232 //	#pragma mark -
233 
234 
235 MessageFilter::MessageFilter()
236 	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
237 {
238 }
239 
240 
241 MessageFilter *
242 MessageFilter::FilterFor(BView *view, BParameter &parameter)
243 {
244 	BControl *control = dynamic_cast<BControl *>(view);
245 	if (control == NULL)
246 		return NULL;
247 
248 	switch (parameter.Type()) {
249 		case BParameter::B_CONTINUOUS_PARAMETER:
250 			return new ContinuousMessageFilter(control, static_cast<BContinuousParameter &>(parameter));
251 
252 		case BParameter::B_DISCRETE_PARAMETER:
253 			return new DiscreteMessageFilter(control, static_cast<BDiscreteParameter &>(parameter));
254 
255 		case BParameter::B_NULL_PARAMETER: /* fall through */
256 		default:
257 			return NULL;
258 	}
259 }
260 
261 
262 //	#pragma mark -
263 
264 
265 ContinuousMessageFilter::ContinuousMessageFilter(BControl *control, BContinuousParameter &parameter)
266 	: MessageFilter(),
267 	fParameter(parameter)
268 {
269 	// initialize view for us
270 	control->SetMessage(new BMessage(kMsgParameterChanged));
271 
272 	// set initial value
273 	// ToDo: response support!
274 
275 	float value[fParameter.CountChannels()];
276 	size_t size = sizeof(value);
277 	if (parameter.GetValue((void *)&value, &size, NULL) < B_OK) {
278 		ERROR("Could not get parameter value for %p\n", &parameter);
279 		return;
280 	}
281 
282 	if (BSlider *slider = dynamic_cast<BSlider *>(control)) {
283 		slider->SetValue((int32) (1000 * value[0]));
284 		slider->SetModificationMessage(new BMessage(kMsgParameterChanged));
285 	} else if (BChannelSlider *slider = dynamic_cast<BChannelSlider *>(control)) {
286 		for (int32 i = 0; i < fParameter.CountChannels(); i++)
287 			slider->SetValueFor(i, (int32) (1000 * value[i]));
288 
289 		slider->SetModificationMessage(new BMessage(kMsgParameterChanged));
290 	} else
291 		printf("unknown discrete parameter view\n");
292 }
293 
294 
295 ContinuousMessageFilter::~ContinuousMessageFilter()
296 {
297 }
298 
299 
300 filter_result
301 ContinuousMessageFilter::Filter(BMessage *message, BHandler **target)
302 {
303 	BControl *control;
304 
305 	if (message->what != kMsgParameterChanged
306 		|| (control = dynamic_cast<BControl *>(*target)) == NULL)
307 		return B_DISPATCH_MESSAGE;
308 
309 	// update view
310 	// ToDo: support for response!
311 
312 	float value[fParameter.CountChannels()];
313 
314 	if (BSlider *slider = dynamic_cast<BSlider *>(control)) {
315 		value[0] = (float)(slider->Value() / 1000.0);
316 	} else if (BChannelSlider *slider = dynamic_cast<BChannelSlider *>(control)) {
317 		for (int32 i = 0; i < fParameter.CountChannels(); i++)
318 			value[i] = (float)(slider->ValueFor(i) / 1000.0);
319 	}
320 
321 	printf("update view %s, %ld channels\n", control->Name(), fParameter.CountChannels());
322 
323 	if (fParameter.SetValue((void *)value, sizeof(value), system_time()) < B_OK) {
324 		ERROR("Could not set parameter value for %p\n", &fParameter);
325 		return B_DISPATCH_MESSAGE;
326 	}
327 
328 	return B_SKIP_MESSAGE;
329 }
330 
331 
332 //	#pragma mark -
333 
334 
335 DiscreteMessageFilter::DiscreteMessageFilter(BControl *control, BDiscreteParameter &parameter)
336 	: MessageFilter(),
337 	fParameter(parameter)
338 {
339 	// initialize view for us
340 	control->SetMessage(new BMessage(kMsgParameterChanged));
341 
342 	// set initial value
343 
344 	size_t size = sizeof(int32);
345 	int32 value;
346 	if (parameter.GetValue((void *)&value, &size, NULL) < B_OK) {
347 		ERROR("Could not get parameter value for %p\n", &parameter);
348 		return;
349 	}
350 
351 	if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) {
352 		checkBox->SetValue(value);
353 	} else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) {
354 		popUp->SelectOptionFor(value);
355 	} else
356 		printf("unknown discrete parameter view\n");
357 }
358 
359 
360 DiscreteMessageFilter::~DiscreteMessageFilter()
361 {
362 }
363 
364 
365 filter_result
366 DiscreteMessageFilter::Filter(BMessage *message, BHandler **target)
367 {
368 	BControl *control;
369 
370 	if (message->what != kMsgParameterChanged
371 		|| (control = dynamic_cast<BControl *>(*target)) == NULL)
372 		return B_DISPATCH_MESSAGE;
373 
374 	// update view
375 
376 	int32 value = 0;
377 
378 	if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) {
379 		value = checkBox->Value();
380 	} else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) {
381 		popUp->SelectedOption(NULL, &value);
382 	}
383 
384 	printf("update view %s, value = %ld\n", control->Name(), value);
385 
386 	if (fParameter.SetValue((void *)&value, sizeof(value), system_time()) < B_OK) {
387 		ERROR("Could not set parameter value for %p\n", &fParameter);
388 		return B_DISPATCH_MESSAGE;
389 	}
390 
391 	return B_SKIP_MESSAGE;
392 }
393 
394 
395 //	#pragma mark -
396 
397 
398 DefaultMediaTheme::DefaultMediaTheme()
399 	: BMediaTheme("BeOS Theme", "BeOS built-in theme version 0.1")
400 {
401 	CALLED();
402 }
403 
404 
405 BControl *
406 DefaultMediaTheme::MakeControlFor(BParameter *parameter)
407 {
408 	CALLED();
409 
410 	BRect rect(0, 0, 150, 100);
411 	return MakeViewFor(parameter, &rect);
412 }
413 
414 
415 BView *
416 DefaultMediaTheme::MakeViewFor(BParameterWeb *web, const BRect *hintRect)
417 {
418 	CALLED();
419 
420 	if (web == NULL)
421 		return NULL;
422 
423 	BRect rect;
424 	if (hintRect)
425 		rect = *hintRect;
426 	else
427 		rect.Set(0, 0, 80, 100);
428 
429 	// do we have more than one attached parameter group?
430 	// if so, use a tabbed view with a tab for each group
431 
432 	BTabView *tabView = NULL;
433 
434 	if (web->CountGroups() > 1)
435 		tabView = new BTabView(rect, "web");
436 
437 	rect.OffsetTo(B_ORIGIN);
438 
439 	for (int32 i = 0; i < web->CountGroups(); i++) {
440 		BParameterGroup *group = web->GroupAt(i);
441 		if (group == NULL)
442 			continue;
443 
444 		BView *groupView = MakeViewFor(*group, rect);
445 		if (groupView == NULL)
446 			continue;
447 
448 		if (tabView == NULL) {
449 			// if we don't need a container to put that view into,
450 			// we're done here
451 			return groupView;
452 		}
453 
454 		tabView->AddTab(groupView);
455 
456 		// enlarge the bounding rectangle as needed
457 
458 		if (groupView->Bounds().Width() + 5 > rect.Width())
459 			rect.right = groupView->Bounds().Width() - 1;
460 
461 		if (groupView->Bounds().Height() > rect.Height())
462 			rect.bottom = groupView->Bounds().Height();
463 	}
464 
465 	if (tabView != NULL) {
466 		tabView->ResizeTo(rect.right + 10, rect.bottom + tabView->TabHeight());
467 
468 		rect = tabView->Bounds();
469 		rect.InsetBySelf(1, 1);
470 		rect.top += tabView->TabHeight();
471 
472 		tabView->ContainerView()->ResizeTo(rect.Width(), rect.Height());
473 	}
474 
475 	return tabView;
476 }
477 
478 
479 BView *
480 DefaultMediaTheme::MakeViewFor(BParameterGroup &group, const BRect &hintRect)
481 {
482 	CALLED();
483 
484 	if (group.Flags() & B_HIDDEN_PARAMETER)
485 		return NULL;
486 
487 	BRect rect(hintRect);
488 	BView *view = new GroupView(rect, group.Name());
489 
490 	// Create the parameter views - but don't add them yet
491 
492 	rect.OffsetTo(B_ORIGIN);
493 	rect.InsetBySelf(5, 5);
494 
495 	BList views;
496 	for (int32 i = 0; i < group.CountParameters(); i++) {
497 		BParameter *parameter = group.ParameterAt(i);
498 		if (parameter == NULL)
499 			continue;
500 
501 		BView *parameterView = MakeSelfHostingViewFor(*parameter, rect);
502 		if (parameterView == NULL)
503 			continue;
504 
505 		parameterView->SetViewColor(view->ViewColor());
506 			// ToDo: dunno why this is needed, but the controls
507 			// sometimes (!) have a white background without it
508 
509 		views.AddItem(parameterView);
510 	}
511 
512 	// Identify a title view, and add it at the top if present
513 
514 	TitleView *titleView = dynamic_cast<TitleView *>((BView *)views.ItemAt(0));
515 	if (titleView != NULL) {
516 		view->AddChild(titleView);
517 		rect.OffsetBy(0, titleView->Bounds().Height());
518 	}
519 
520 	// Add the sub-group views
521 
522 	rect.right = rect.left + 50;
523 	rect.bottom = rect.top + 10;
524 	float lastHeight = 0;
525 
526 	for (int32 i = 0; i < group.CountGroups(); i++) {
527 		BParameterGroup *subGroup = group.GroupAt(i);
528 		if (subGroup == NULL)
529 			continue;
530 
531 		BView *groupView = MakeViewFor(*subGroup, rect);
532 		if (groupView == NULL)
533 			continue;
534 
535 		if (i > 0) {
536 			// add separator view
537 			BRect separatorRect(groupView->Frame());
538 			separatorRect.left -= 3;
539 			separatorRect.right = separatorRect.left + 1;
540 			if (lastHeight > separatorRect.Height())
541 				separatorRect.bottom = separatorRect.top + lastHeight;
542 
543 			view->AddChild(new SeparatorView(separatorRect));
544 		}
545 
546 		view->AddChild(groupView);
547 
548 		rect.OffsetBy(groupView->Bounds().Width() + 5, 0);
549 
550 		lastHeight = groupView->Bounds().Height();
551 		if (lastHeight > rect.Height())
552 			rect.bottom = rect.top + lastHeight - 1;
553 	}
554 
555 	view->ResizeTo(rect.left + 10, rect.bottom + 5);
556 
557 	if (group.CountParameters() == 0)
558 		return view;
559 
560 	// add the parameter views part of the group
561 
562 	if (group.CountGroups() > 0) {
563 		rect.top = rect.bottom + 10;
564 		rect.bottom = rect.top + 20;
565 	}
566 
567 	bool center = false;
568 
569 	for (int32 i = 0; i < views.CountItems(); i++) {
570 		BView *parameterView = static_cast<BView *>(views.ItemAt(i));
571 
572 		if (parameterView->Bounds().Width() + 5 > rect.Width())
573 			rect.right = parameterView->Bounds().Width() + rect.left + 5;
574 
575 		// we don't need to add the title view again
576 		if (parameterView == titleView)
577 			continue;
578 
579 		// if there is a BChannelSlider (ToDo: or any vertical slider?)
580 		// the views will be centered
581 		if (dynamic_cast<BChannelSlider *>(parameterView) != NULL)
582 			center = true;
583 
584 		parameterView->MoveTo(parameterView->Frame().left, rect.top);
585 		view->AddChild(parameterView);
586 
587 		rect.OffsetBy(0, parameterView->Bounds().Height() + 5);
588 	}
589 
590 	if (views.CountItems() > (titleView != NULL ? 1 : 0))
591 		view->ResizeTo(rect.right + 5, rect.top + 5);
592 
593 	// center the parameter views if needed, and tweak some views
594 
595 	float width = view->Bounds().Width();
596 
597 	for (int32 i = 0; i < views.CountItems(); i++) {
598 		BView *subView = static_cast<BView *>(views.ItemAt(i));
599 		BRect frame = subView->Frame();
600 
601 		if (center)
602 			subView->MoveTo((width - frame.Width()) / 2, frame.top);
603 		else {
604 			// tweak the PopUp views to look better
605 			if (dynamic_cast<BOptionPopUp *>(subView) != NULL)
606 				subView->ResizeTo(width, frame.Height());
607 		}
608 	}
609 
610 	return view;
611 }
612 
613 
614 /** This creates a view that handles all incoming messages itself - that's
615  *	what is meant with self-hosting.
616  */
617 
618 BView *
619 DefaultMediaTheme::MakeSelfHostingViewFor(BParameter &parameter, const BRect &hintRect)
620 {
621 	if (parameter.Flags() & B_HIDDEN_PARAMETER
622 		|| parameter_should_be_hidden(parameter))
623 		return NULL;
624 
625 	BView *view = MakeViewFor(&parameter, &hintRect);
626 	if (view == NULL) {
627 		// The MakeViewFor() method above returns a BControl - which we
628 		// don't need for a null parameter; that's why it returns NULL.
629 		// But we want to see something anyway, so we add a string view
630 		// here.
631 		if (parameter.Type() == BParameter::B_NULL_PARAMETER) {
632 			if (parameter.Group()->ParameterAt(0) == &parameter) {
633 				// this is the first parameter in this group, so
634 				// let's use a nice title view
635 
636 				TitleView *titleView = new TitleView(hintRect, parameter.Name());
637 				titleView->ResizeToPreferred();
638 
639 				return titleView;
640 			}
641 			BStringView *stringView = new BStringView(hintRect, parameter.Name(), parameter.Name());
642 			stringView->SetAlignment(B_ALIGN_CENTER);
643 			stringView->ResizeToPreferred();
644 
645 			return stringView;
646 		}
647 
648 		return NULL;
649 	}
650 
651 	MessageFilter *filter = MessageFilter::FilterFor(view, parameter);
652 	if (filter != NULL)
653 		view->AddFilter(filter);
654 
655 	return view;
656 }
657 
658 
659 BControl *
660 DefaultMediaTheme::MakeViewFor(BParameter *parameter, const BRect *hintRect)
661 {
662 	BRect rect;
663 	if (hintRect)
664 		rect = *hintRect;
665 	else
666 		rect.Set(0, 0, 50, 100);
667 
668 	switch (parameter->Type()) {
669 		case BParameter::B_NULL_PARAMETER:
670 			// there is no default view for a null parameter
671 			return NULL;
672 
673 		case BParameter::B_DISCRETE_PARAMETER:
674 		{
675 			BDiscreteParameter &discrete = static_cast<BDiscreteParameter &>(*parameter);
676 
677 			if (!strcmp(discrete.Kind(), B_ENABLE)
678 				|| !strcmp(discrete.Kind(), B_MUTE)
679 				|| discrete.CountItems() == 0) {
680 				// create a checkbox item
681 
682 				BCheckBox *checkBox = new BCheckBox(rect, discrete.Name(), discrete.Name(), NULL);
683 				checkBox->ResizeToPreferred();
684 
685 				return checkBox;
686 			} else {
687 				// create a pop up menu field
688 
689 				// ToDo: replace BOptionPopUp (or fix it in OpenBeOS...)
690 				// this is a workaround for a bug in BOptionPopUp - you need to
691 				// know the actual width before creating the object - very nice...
692 
693 				BFont font;
694 				float width = 0;
695 				for (int32 i = 0; i < discrete.CountItems(); i++) {
696 					float labelWidth = font.StringWidth(discrete.ItemNameAt(i));
697 					if (labelWidth > width)
698 						width = labelWidth;
699 				}
700 				width += font.StringWidth(discrete.Name()) + 55;
701 				rect.right = rect.left + width;
702 
703 				BOptionPopUp *popUp = new BOptionPopUp(rect, discrete.Name(), discrete.Name(), NULL);
704 
705 				for (int32 i = 0; i < discrete.CountItems(); i++) {
706 					popUp->AddOption(discrete.ItemNameAt(i), discrete.ItemValueAt(i));
707 				}
708 
709 				popUp->ResizeToPreferred();
710 
711 				return popUp;
712 			}
713 		}
714 
715 		case BParameter::B_CONTINUOUS_PARAMETER:
716 		{
717 			BContinuousParameter &continuous = static_cast<BContinuousParameter &>(*parameter);
718 
719 			if (!strcmp(continuous.Kind(), B_MASTER_GAIN)
720 				|| !strcmp(continuous.Kind(), B_GAIN))
721 			{
722 				BChannelSlider *slider = new BChannelSlider(rect, continuous.Name(),
723 					continuous.Name(), NULL, B_VERTICAL, continuous.CountChannels());
724 
725 				char minLabel[64], maxLabel[64];
726 
727 				const char *unit = continuous.Unit();
728 				if (unit[0]) {
729 					// if we have a unit, print it next to the limit values
730 					sprintf(minLabel, "%g %s", continuous.MinValue(), continuous.Unit());
731 					sprintf(maxLabel, "%g %s", continuous.MaxValue(), continuous.Unit());
732 				} else {
733 					sprintf(minLabel, "%g", continuous.MinValue());
734 					sprintf(maxLabel, "%g", continuous.MaxValue());
735 				}
736 				slider->SetLimitLabels(minLabel, maxLabel);
737 
738 				float width, height;
739 				slider->GetPreferredSize(&width, &height);
740 				slider->ResizeTo(width, 190);
741 
742 				// ToDo: take BContinuousParameter::GetResponse() & ValueStep() into account!
743 
744 				for (int32 i = 0; i < continuous.CountChannels(); i++)
745 					slider->SetLimitsFor(i, continuous.MinValue() * 1000, continuous.MaxValue() * 1000);
746 
747 				return slider;
748 			}
749 
750 			BSlider *slider = new BSlider(rect, parameter->Name(), parameter->Name(),
751 				NULL, 0, 100);
752 
753 			float width, height;
754 			slider->GetPreferredSize(&width, &height);
755 			slider->ResizeTo(100, height);
756 
757 			return slider;
758 		}
759 
760 		default:
761 			ERROR("BMediaTheme: Don't know parameter type: 0x%lx\n", parameter->Type());
762 	}
763 	return NULL;
764 }
765 
766