xref: /haiku/src/kits/media/DefaultMediaTheme.cpp (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
1 /*
2  * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "DefaultMediaTheme.h"
8 
9 #include <Box.h>
10 #include <Button.h>
11 #include <ChannelSlider.h>
12 #include <CheckBox.h>
13 #include <MediaRoster.h>
14 #include <MenuField.h>
15 #include <MessageFilter.h>
16 #include <OptionPopUp.h>
17 #include <ParameterWeb.h>
18 #include <ScrollBar.h>
19 #include <Slider.h>
20 #include <StringView.h>
21 #include <TabView.h>
22 #include <TextControl.h>
23 #include <Window.h>
24 
25 #include "debug.h"
26 
27 
28 using namespace BPrivate;
29 
30 
31 namespace BPrivate {
32 
33 class DynamicScrollView : public BView {
34 	public:
35 		DynamicScrollView(const char *name, BView *target);
36 		virtual ~DynamicScrollView();
37 
38 		virtual void AttachedToWindow(void);
39 		virtual void FrameResized(float width, float height);
40 		virtual void FrameMoved(BPoint newPosition);
41 		virtual void GetPreferredSize(float *_width, float *_height);
42 
43 		void SetContentBounds(BRect bounds);
44 		BRect ContentBounds() const { return fContentBounds; }
45 
46 	private:
47 		void UpdateBars();
48 
49 		BScrollBar	*fHorizontalScrollBar, *fVerticalScrollBar;
50 		BRect		fContentBounds;
51 		BView		*fTarget;
52 		bool		fIsDocumentScroller;
53 };
54 
55 class GroupView : public BView {
56 	public:
57 		GroupView(BRect frame, const char *name);
58 		virtual ~GroupView();
59 
60 		virtual void AttachedToWindow();
61 		virtual void AllAttached();
62 		virtual void GetPreferredSize(float *_width, float *_height);
63 
64 		virtual	BSize MinSize();
65 		virtual	BSize MaxSize();
66 		virtual	BSize PreferredSize();
67 
68 		void SetContentBounds(BRect bounds);
69 		BRect ContentBounds() const { return fContentBounds; }
70 
71 	private:
72 		BRect		fContentBounds;
73 };
74 
75 class TabView : public BTabView {
76 	public:
77 		TabView(BRect frame, const char *name, button_width width = B_WIDTH_FROM_LABEL,
78 			uint32 resizingMode = B_FOLLOW_ALL, uint32 flags = B_FULL_UPDATE_ON_RESIZE
79 				| B_WILL_DRAW | B_NAVIGABLE_JUMP | B_FRAME_EVENTS | B_NAVIGABLE);
80 
81 		virtual void FrameResized(float width, float height);
82 		virtual void Select(int32 tab);
83 };
84 
85 class SeparatorView : public BView {
86 	public:
87 		SeparatorView(BRect frame);
88 		virtual ~SeparatorView();
89 
90 		virtual void Draw(BRect updateRect);
91 
92 	private:
93 		bool	fVertical;
94 };
95 
96 class TitleView : public BView {
97 	public:
98 		TitleView(BRect frame, const char *title);
99 		virtual ~TitleView();
100 
101 		virtual void Draw(BRect updateRect);
102 		virtual void GetPreferredSize(float *width, float *height);
103 
104 	private:
105 		const char *fTitle;
106 };
107 
108 class MessageFilter : public BMessageFilter {
109 	public:
110 		static MessageFilter *FilterFor(BView *view, BParameter &parameter);
111 
112 	protected:
113 		MessageFilter();
114 };
115 
116 class ContinuousMessageFilter : public MessageFilter {
117 	public:
118 		ContinuousMessageFilter(BControl *control,
119 			BContinuousParameter &parameter);
120 		virtual ~ContinuousMessageFilter();
121 
122 		virtual filter_result Filter(BMessage *message, BHandler **target);
123 
124 	private:
125 		void _UpdateControl();
126 
127 		BControl				*fControl;
128 		BContinuousParameter	&fParameter;
129 		bool					fRegistered;
130 };
131 
132 class DiscreteMessageFilter : public MessageFilter {
133 	public:
134 		DiscreteMessageFilter(BControl *control, BDiscreteParameter &parameter);
135 		virtual ~DiscreteMessageFilter();
136 
137 		virtual filter_result Filter(BMessage *message, BHandler **target);
138 
139 	private:
140 		BDiscreteParameter	&fParameter;
141 };
142 
143 }	// namespace BPrivate
144 
145 
146 const uint32 kMsgParameterChanged = '_mPC';
147 
148 
149 static bool
150 parameter_should_be_hidden(BParameter &parameter)
151 {
152 	// ToDo: note, this is probably completely stupid, but it's the only
153 	// way I could safely remove the null parameters that are not shown
154 	// by the R5 media theme
155 	if (parameter.Type() != BParameter::B_NULL_PARAMETER
156 		|| strcmp(parameter.Kind(), B_WEB_PHYSICAL_INPUT))
157 		return false;
158 
159 	for (int32 i = 0; i < parameter.CountOutputs(); i++) {
160 		if (!strcmp(parameter.OutputAt(0)->Kind(), B_INPUT_MUX))
161 			return true;
162 	}
163 
164 	return false;
165 }
166 
167 
168 //	#pragma mark -
169 
170 
171 DynamicScrollView::DynamicScrollView(const char *name, BView *target)
172 	: BView(target->Frame(), name, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS),
173 	fHorizontalScrollBar(NULL),
174 	fVerticalScrollBar(NULL),
175 	fTarget(target),
176 	fIsDocumentScroller(false)
177 {
178 	fContentBounds.Set(-1, -1, -1, -1);
179 	SetViewColor(fTarget->ViewColor());
180 	target->MoveTo(B_ORIGIN);
181 	AddChild(target);
182 }
183 
184 
185 DynamicScrollView::~DynamicScrollView()
186 {
187 }
188 
189 
190 void
191 DynamicScrollView::AttachedToWindow(void)
192 {
193 	BRect frame = ConvertToScreen(Bounds());
194 	BRect windowFrame = Window()->Frame();
195 
196 	fIsDocumentScroller = Parent() == NULL
197 		&& Window() != NULL
198 		&& Window()->Look() == B_DOCUMENT_WINDOW_LOOK
199 		&& frame.right == windowFrame.right
200 		&& frame.bottom == windowFrame.bottom;
201 
202 	UpdateBars();
203 }
204 
205 
206 void
207 DynamicScrollView::FrameResized(float width, float height)
208 {
209 	UpdateBars();
210 }
211 
212 
213 void
214 DynamicScrollView::FrameMoved(BPoint newPosition)
215 {
216 	UpdateBars();
217 }
218 
219 
220 void
221 DynamicScrollView::GetPreferredSize(float *_width, float *_height)
222 {
223 	float width = 50;
224 	if (fVerticalScrollBar)
225 		width += B_V_SCROLL_BAR_WIDTH;
226 	float height = 50;
227 	if (fHorizontalScrollBar)
228 		height += B_H_SCROLL_BAR_HEIGHT;
229 	if (_width)
230 		*_width = width;
231 	if (_height)
232 		*_height = height;
233 }
234 
235 
236 void
237 DynamicScrollView::SetContentBounds(BRect bounds)
238 {
239 	fContentBounds = bounds;
240 	if (Window())
241 		UpdateBars();
242 }
243 
244 
245 void
246 DynamicScrollView::UpdateBars()
247 {
248 	// we need the size that the view wants to have, and the one
249 	// it could have (without the space for the scroll bars)
250 
251 	float width, height;
252 	if (fContentBounds == BRect(-1, -1, -1, -1))
253 		fTarget->GetPreferredSize(&width, &height);
254 	else {
255 		width = fContentBounds.Width();
256 		height = fContentBounds.Height();
257 	}
258 
259 	BRect bounds = Bounds();
260 
261 	// do we have to remove a scroll bar?
262 
263 	bool horizontal = width > bounds.Width();
264 	bool vertical = height > bounds.Height();
265 
266 	if (!horizontal && fHorizontalScrollBar != NULL) {
267 		RemoveChild(fHorizontalScrollBar);
268 		delete fHorizontalScrollBar;
269 		fHorizontalScrollBar = NULL;
270 	}
271 
272 	if (!vertical && fVerticalScrollBar != NULL) {
273 		RemoveChild(fVerticalScrollBar);
274 		delete fVerticalScrollBar;
275 		fVerticalScrollBar = NULL;
276 	}
277 
278 	// or do we have to add a scroll bar?
279 
280 	if (horizontal && fHorizontalScrollBar == NULL) {
281 		BRect rect = Bounds();
282 		rect.top = rect.bottom + 1 - B_H_SCROLL_BAR_HEIGHT;
283 		if (vertical || fIsDocumentScroller)
284 			rect.right -= B_V_SCROLL_BAR_WIDTH;
285 
286 		fHorizontalScrollBar = new BScrollBar(rect, "horizontal", fTarget, 0,
287 			width, B_HORIZONTAL);
288 		AddChild(fHorizontalScrollBar);
289 	}
290 
291 	if (vertical && fVerticalScrollBar == NULL) {
292 		BRect rect = Bounds();
293 		rect.left = rect.right + 1 - B_V_SCROLL_BAR_WIDTH;
294 		if (horizontal || fIsDocumentScroller)
295 			rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
296 
297 		fVerticalScrollBar = new BScrollBar(rect, "vertical", fTarget, 0,
298 			height, B_VERTICAL);
299 		AddChild(fVerticalScrollBar);
300 	}
301 
302 	// update the scroll bar range & proportions and layout views
303 
304 	bounds = Bounds();
305 	if (fHorizontalScrollBar != NULL)
306 		bounds.bottom -= B_H_SCROLL_BAR_HEIGHT + 1;
307 	if (fVerticalScrollBar != NULL)
308 		bounds.right -= B_V_SCROLL_BAR_WIDTH + 1;
309 
310 	fTarget->MoveTo(bounds.LeftTop());
311 	fTarget->ResizeTo(bounds.Width(), bounds.Height());
312 
313 	if (fHorizontalScrollBar != NULL) {
314 		float delta = width - bounds.Width();
315 		if (delta < 0)
316 			delta = 0;
317 
318 		fHorizontalScrollBar->SetRange(0, delta);
319 		fHorizontalScrollBar->SetSteps(1, bounds.Width());
320 		fHorizontalScrollBar->SetProportion(bounds.Width() / width);
321 
322 		float barWidth = Bounds().Width();
323 		if (vertical) {
324 			// scrollbars overlap one pixel of the frame
325 			barWidth += 1;
326 		}
327 		if (vertical || fIsDocumentScroller)
328 			barWidth -= B_V_SCROLL_BAR_WIDTH + 1;
329 
330 		fHorizontalScrollBar->MoveTo(bounds.left, bounds.bottom + 1);
331 		fHorizontalScrollBar->ResizeTo(barWidth, B_H_SCROLL_BAR_HEIGHT);
332 	}
333 	if (fVerticalScrollBar != NULL) {
334 		float delta = height - bounds.Height();
335 		if (delta < 0)
336 			delta = 0;
337 
338 		fVerticalScrollBar->SetRange(0, delta);
339 		fVerticalScrollBar->SetSteps(1, bounds.Height());
340 		fVerticalScrollBar->SetProportion(bounds.Height() / height);
341 
342 		float barHeight = Bounds().Height();
343 		if (horizontal) {
344 			// scrollbars overlap one pixel of the frame
345 			barHeight += 1;
346 		}
347 		if (horizontal || fIsDocumentScroller)
348 			barHeight -= B_H_SCROLL_BAR_HEIGHT + 1;
349 
350 		fVerticalScrollBar->MoveTo(bounds.right + 1, bounds.top);
351 		fVerticalScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, barHeight);
352 	}
353 }
354 
355 
356 //	#pragma mark -
357 
358 
359 GroupView::GroupView(BRect frame, const char *name)
360 	: BView(frame, name, B_FOLLOW_NONE, B_WILL_DRAW)
361 {
362 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
363 }
364 
365 
366 GroupView::~GroupView()
367 {
368 }
369 
370 
371 void
372 GroupView::AttachedToWindow()
373 {
374 	for (int32 i = CountChildren(); i-- > 0;) {
375 		BControl *control = dynamic_cast<BControl *>(ChildAt(i));
376 		if (control == NULL)
377 			continue;
378 
379 		control->SetTarget(control);
380 	}
381 }
382 
383 
384 void
385 GroupView::AllAttached()
386 {
387 }
388 
389 
390 void
391 GroupView::GetPreferredSize(float *_width, float *_height)
392 {
393 	if (_width)
394 		*_width = fContentBounds.Width();
395 
396 	if (_height)
397 		*_height = fContentBounds.Height();
398 }
399 
400 
401 BSize
402 GroupView::MinSize()
403 {
404 	return BSize(100, 100);
405 }
406 
407 
408 BSize
409 GroupView::PreferredSize()
410 {
411 	return MinSize();
412 }
413 
414 
415 BSize
416 GroupView::MaxSize()
417 {
418 	BSize max;
419 	GetPreferredSize(&max.width, &max.height);
420 	return max;
421 }
422 
423 
424 void
425 GroupView::SetContentBounds(BRect bounds)
426 {
427 	fContentBounds = bounds;
428 }
429 
430 
431 //	#pragma mark -
432 
433 
434 /** BTabView is really stupid - it doesn't even resize its content
435  *	view when it is resized itself.
436  *	This derived class fixes this issue, and also resizes all tab
437  *	content views to the size of the container view when they are
438  *	selected (does not take their resize flags into account, though).
439  */
440 
441 TabView::TabView(BRect frame, const char *name, button_width width,
442 	uint32 resizingMode, uint32 flags)
443 	: BTabView(frame, name, width, resizingMode, flags)
444 {
445 }
446 
447 
448 void
449 TabView::FrameResized(float width, float height)
450 {
451 	BRect rect = Bounds();
452 	rect.top += TabHeight();
453 	rect.InsetBy(3.0f, 3.0f);
454 		//ContainerView is inseted by 3.0 in BTabView::_InitObject()
455 
456 	ContainerView()->ResizeTo(rect.Width(), rect.Height());
457 }
458 
459 
460 void
461 TabView::Select(int32 tab)
462 {
463 	BTabView::Select(tab);
464 
465 	BView *view = ViewForTab(Selection());
466 	if (view != NULL) {
467 		BRect rect = ContainerView()->Bounds();
468 		view->ResizeTo(rect.Width(), rect.Height());
469 	}
470 }
471 
472 
473 //	#pragma mark -
474 
475 
476 SeparatorView::SeparatorView(BRect frame)
477 	: BView(frame, "-", B_FOLLOW_NONE, B_WILL_DRAW)
478 {
479 	fVertical = frame.Width() < frame.Height();
480 	SetViewColor(B_TRANSPARENT_COLOR);
481 }
482 
483 
484 SeparatorView::~SeparatorView()
485 {
486 }
487 
488 
489 void
490 SeparatorView::Draw(BRect updateRect)
491 {
492 	rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR);
493 	BRect rect = updateRect & Bounds();
494 
495 	SetHighColor(tint_color(color, B_DARKEN_1_TINT));
496 	if (fVertical)
497 		StrokeLine(BPoint(0, rect.top), BPoint(0, rect.bottom));
498 	else
499 		StrokeLine(BPoint(rect.left, 0), BPoint(rect.right, 0));
500 
501 	SetHighColor(tint_color(color, B_LIGHTEN_1_TINT));
502 	if (fVertical)
503 		StrokeLine(BPoint(1, rect.top), BPoint(1, rect.bottom));
504 	else
505 		StrokeLine(BPoint(rect.left, 1), BPoint(rect.right, 1));
506 }
507 
508 
509 //	#pragma mark -
510 
511 
512 TitleView::TitleView(BRect frame, const char *title)
513 	: BView(frame, title, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW)
514 {
515 	fTitle = strdup(title);
516 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
517 	SetLowColor(ViewColor());
518 }
519 
520 
521 TitleView::~TitleView()
522 {
523 	free((char *)fTitle);
524 }
525 
526 
527 void
528 TitleView::Draw(BRect updateRect)
529 {
530 	BRect rect(Bounds());
531 
532 	SetDrawingMode(B_OP_COPY);
533 	SetHighColor(240, 240, 240);
534 	DrawString(fTitle, BPoint(rect.left + 1, rect.bottom - 8));
535 
536 	SetDrawingMode(B_OP_OVER);
537 	SetHighColor(80, 20, 20);
538 	DrawString(fTitle, BPoint(rect.left, rect.bottom - 9));
539 }
540 
541 
542 void
543 TitleView::GetPreferredSize(float *_width, float *_height)
544 {
545 	if (_width)
546 		*_width = StringWidth(fTitle) + 2;
547 
548 	if (_height) {
549 		font_height fontHeight;
550 		GetFontHeight(&fontHeight);
551 
552 		*_height = fontHeight.ascent + fontHeight.descent + fontHeight.leading
553 			+ 8;
554 	}
555 }
556 
557 
558 //	#pragma mark -
559 
560 
561 MessageFilter::MessageFilter()
562 	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
563 {
564 }
565 
566 
567 MessageFilter *
568 MessageFilter::FilterFor(BView *view, BParameter &parameter)
569 {
570 	BControl *control = dynamic_cast<BControl *>(view);
571 	if (control == NULL)
572 		return NULL;
573 
574 	switch (parameter.Type()) {
575 		case BParameter::B_CONTINUOUS_PARAMETER:
576 			return new ContinuousMessageFilter(control,
577 				static_cast<BContinuousParameter &>(parameter));
578 
579 		case BParameter::B_DISCRETE_PARAMETER:
580 			return new DiscreteMessageFilter(control,
581 				static_cast<BDiscreteParameter &>(parameter));
582 
583 		case BParameter::B_NULL_PARAMETER: /* fall through */
584 		default:
585 			return NULL;
586 	}
587 }
588 
589 
590 //	#pragma mark -
591 
592 
593 ContinuousMessageFilter::ContinuousMessageFilter(BControl *control,
594 		BContinuousParameter &parameter)
595 	: MessageFilter(),
596 	fControl(control),
597 	fParameter(parameter),
598 	fRegistered(false)
599 {
600 	// initialize view for us
601 	control->SetMessage(new BMessage(kMsgParameterChanged));
602 
603 	if (BSlider *slider = dynamic_cast<BSlider *>(fControl))
604 		slider->SetModificationMessage(new BMessage(kMsgParameterChanged));
605 	else if (BChannelSlider *slider = dynamic_cast<BChannelSlider *>(fControl))
606 		slider->SetModificationMessage(new BMessage(kMsgParameterChanged));
607 	else
608 		ERROR("ContinuousMessageFilter: unknown continuous parameter view\n");
609 
610 	// set initial value
611 	_UpdateControl();
612 }
613 
614 
615 ContinuousMessageFilter::~ContinuousMessageFilter()
616 {
617 }
618 
619 
620 filter_result
621 ContinuousMessageFilter::Filter(BMessage *message, BHandler **target)
622 {
623 	if (*target != fControl)
624 		return B_DISPATCH_MESSAGE;
625 
626 	// TODO: remove this work-around! We can solve this by subclassing the
627 	// slider classes, and start watching in their AttachedToWindow() method
628 	if (!fRegistered) {
629 		if (BMediaRoster* roster = BMediaRoster::CurrentRoster()) {
630 			roster->StartWatching(fControl, fParameter.Web()->Node(),
631 				B_MEDIA_NEW_PARAMETER_VALUE);
632 		}
633 		fRegistered = true;
634 	}
635 
636 	if (message->what == kMsgParameterChanged) {
637 		// update parameter from control
638 		// TODO: support for response!
639 
640 		float value[fParameter.CountChannels()];
641 
642 		if (BSlider *slider = dynamic_cast<BSlider *>(fControl)) {
643 			value[0] = (float)(slider->Value() / 1000.0);
644 		} else if (BChannelSlider *slider
645 				= dynamic_cast<BChannelSlider *>(fControl)) {
646 			for (int32 i = 0; i < fParameter.CountChannels(); i++)
647 				value[i] = (float)(slider->ValueFor(i) / 1000.0);
648 		}
649 
650 		TRACE("ContinuousMessageFilter::Filter: update view %s, %ld "
651 			"channels\n", fControl->Name(), fParameter.CountChannels());
652 
653 		if (fParameter.SetValue((void *)value, sizeof(value),
654 				system_time()) < B_OK) {
655 			ERROR("ContinuousMessageFilter::Filter: Could not set parameter "
656 				"value for %p\n", &fParameter);
657 			return B_DISPATCH_MESSAGE;
658 		}
659 		return B_SKIP_MESSAGE;
660 	}
661 	if (message->what == B_MEDIA_NEW_PARAMETER_VALUE) {
662 		// update view from parameter -- if the message concerns us
663 		const media_node* node;
664 		int32 parameterID;
665 		ssize_t size;
666 		if (message->FindInt32("parameter", &parameterID) != B_OK
667 			|| fParameter.ID() != parameterID
668 			|| message->FindData("node", B_RAW_TYPE, (const void**)&node,
669 					&size) != B_OK
670 			|| fParameter.Web()->Node() != *node)
671 			return B_DISPATCH_MESSAGE;
672 
673 		_UpdateControl();
674 		return B_SKIP_MESSAGE;
675 	}
676 
677 	return B_DISPATCH_MESSAGE;
678 }
679 
680 
681 void
682 ContinuousMessageFilter::_UpdateControl()
683 {
684 	// TODO: response support!
685 
686 	float value[fParameter.CountChannels()];
687 	size_t size = sizeof(value);
688 	if (fParameter.GetValue((void *)&value, &size, NULL) < B_OK) {
689 		ERROR("ContinuousMessageFilter: Could not get value for continuous "
690 			"parameter %p (name '%s', node %d)\n", &fParameter,
691 			fParameter.Name(), (int)fParameter.Web()->Node().node);
692 		return;
693 	}
694 
695 	if (BSlider *slider = dynamic_cast<BSlider *>(fControl)) {
696 		slider->SetValue((int32) (1000 * value[0]));
697 		slider->SetModificationMessage(new BMessage(kMsgParameterChanged));
698 	} else if (BChannelSlider *slider
699 			= dynamic_cast<BChannelSlider *>(fControl)) {
700 		for (int32 i = 0; i < fParameter.CountChannels(); i++) {
701 			slider->SetValueFor(i, (int32) (1000 * value[i]));
702 		}
703 	}
704 }
705 
706 
707 //	#pragma mark -
708 
709 
710 DiscreteMessageFilter::DiscreteMessageFilter(BControl *control,
711 		BDiscreteParameter &parameter)
712 	: MessageFilter(),
713 	fParameter(parameter)
714 {
715 	// initialize view for us
716 	control->SetMessage(new BMessage(kMsgParameterChanged));
717 
718 	// set initial value
719 
720 	size_t size = sizeof(int32);
721 	int32 value;
722 	if (parameter.GetValue((void *)&value, &size, NULL) < B_OK) {
723 		ERROR("DiscreteMessageFilter: Could not get value for discrete "
724 			"parameter %p (name '%s', node %d)\n", &parameter,
725 			parameter.Name(), (int)(parameter.Web()->Node().node));
726 		return;
727 	}
728 
729 	if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) {
730 		checkBox->SetValue(value);
731 	} else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) {
732 		popUp->SelectOptionFor(value);
733 	} else
734 		ERROR("DiscreteMessageFilter: unknown discrete parameter view\n");
735 }
736 
737 
738 DiscreteMessageFilter::~DiscreteMessageFilter()
739 {
740 }
741 
742 
743 filter_result
744 DiscreteMessageFilter::Filter(BMessage *message, BHandler **target)
745 {
746 	BControl *control;
747 
748 	if (message->what != kMsgParameterChanged
749 		|| (control = dynamic_cast<BControl *>(*target)) == NULL)
750 		return B_DISPATCH_MESSAGE;
751 
752 	// update view
753 
754 	int32 value = 0;
755 
756 	if (BCheckBox *checkBox = dynamic_cast<BCheckBox *>(control)) {
757 		value = checkBox->Value();
758 	} else if (BOptionPopUp *popUp = dynamic_cast<BOptionPopUp *>(control)) {
759 		popUp->SelectedOption(NULL, &value);
760 	}
761 
762 	TRACE("DiscreteMessageFilter::Filter: update view %s, value = %ld\n", control->Name(), value);
763 
764 	if (fParameter.SetValue((void *)&value, sizeof(value), system_time()) < B_OK) {
765 		ERROR("DiscreteMessageFilter::Filter: Could not set parameter value for %p\n", &fParameter);
766 		return B_DISPATCH_MESSAGE;
767 	}
768 
769 	return B_SKIP_MESSAGE;
770 }
771 
772 
773 //	#pragma mark -
774 
775 
776 DefaultMediaTheme::DefaultMediaTheme()
777 	: BMediaTheme("Haiku Theme", "Haiku built-in theme version 0.1")
778 {
779 	CALLED();
780 }
781 
782 
783 BControl *
784 DefaultMediaTheme::MakeControlFor(BParameter *parameter)
785 {
786 	CALLED();
787 
788 	BRect rect(0, 0, 150, 100);
789 	return MakeViewFor(parameter, &rect);
790 }
791 
792 
793 BView *
794 DefaultMediaTheme::MakeViewFor(BParameterWeb *web, const BRect *hintRect)
795 {
796 	CALLED();
797 
798 	if (web == NULL)
799 		return NULL;
800 
801 	BRect rect;
802 	if (hintRect)
803 		rect = *hintRect;
804 
805 	BRect bestRect;
806 
807 	// do we have more than one attached parameter group?
808 	// if so, use a tabbed view with a tab for each group
809 
810 	TabView *tabView = NULL;
811 
812 	if (web->CountGroups() > 1)
813 		tabView = new TabView(rect, "web");
814 
815 	rect.OffsetTo(B_ORIGIN);
816 
817 	for (int32 i = 0; i < web->CountGroups(); i++) {
818 		BParameterGroup *group = web->GroupAt(i);
819 		if (group == NULL)
820 			continue;
821 
822 		BView *groupView = MakeViewFor(*group, hintRect ? &rect : NULL);
823 		if (groupView == NULL)
824 			continue;
825 
826 		if (GroupView *view = dynamic_cast<GroupView *>(groupView)) {
827 			// the top-level group views must not be larger than their hintRect,
828 			// but unlike their children, they should follow all sides when
829 			// their parent is resized
830 			if (hintRect != NULL)
831 				view->ResizeTo(rect.Width() - 10, rect.Height() - 10);
832 			view->SetResizingMode(B_FOLLOW_ALL);
833 		}
834 
835 		if (tabView == NULL) {
836 			// if we don't need a container to put that view into,
837 			// we're done here (but the groupView may span over the
838 			// whole hintRect)
839 			if (groupView->Frame().LeftTop() == BPoint(5, 5)) {
840 				// remove insets, as they are not needed
841 				groupView->MoveBy(-5, -5);
842 				groupView->ResizeBy(10, 10);
843 			}
844 
845 			return new DynamicScrollView(groupView->Name(), groupView);
846 		}
847 
848 		DynamicScrollView *scrollView = new DynamicScrollView(groupView->Name(), groupView);
849 		tabView->AddTab(scrollView);
850 
851 		if (!hintRect) {
852 			bestRect = bestRect | scrollView->Bounds();
853 		}
854 	}
855 
856 	if (tabView != NULL) {
857 		// this adjustment must be kept in sync with TabView::FrameResized
858 		bestRect.bottom += tabView->TabHeight();
859 		bestRect.InsetBy(-3.0,-3.0);
860 
861 		tabView->ResizeTo(bestRect.Width(), bestRect.Height());
862 		tabView->FrameResized(bestRect.Width(), bestRect.Height());
863 			//needed since we're not attached to a window yet
864 	}
865 
866 	return tabView;
867 }
868 
869 
870 BView *
871 DefaultMediaTheme::MakeViewFor(BParameterGroup& group, const BRect* hintRect)
872 {
873 	CALLED();
874 
875 	if (group.Flags() & B_HIDDEN_PARAMETER)
876 		return NULL;
877 
878 	BRect rect;
879 	if (hintRect != NULL)
880 		rect = *hintRect;
881 
882 	GroupView *view = new GroupView(rect, group.Name());
883 
884 	// Create the parameter views - but don't add them yet
885 
886 	rect.OffsetTo(B_ORIGIN);
887 	rect.InsetBySelf(5, 5);
888 
889 	BList views;
890 	for (int32 i = 0; i < group.CountParameters(); i++) {
891 		BParameter *parameter = group.ParameterAt(i);
892 		if (parameter == NULL)
893 			continue;
894 
895 		BView *parameterView = MakeSelfHostingViewFor(*parameter,
896 			hintRect ? &rect : NULL);
897 		if (parameterView == NULL)
898 			continue;
899 
900 		parameterView->SetViewColor(view->ViewColor());
901 			// ToDo: dunno why this is needed, but the controls
902 			// sometimes (!) have a white background without it
903 
904 		views.AddItem(parameterView);
905 	}
906 
907 	// Identify a title view, and add it at the top if present
908 
909 	TitleView *titleView = dynamic_cast<TitleView *>((BView *)views.ItemAt(0));
910 	if (titleView != NULL) {
911 		view->AddChild(titleView);
912 		rect.OffsetBy(0, titleView->Bounds().Height());
913 	}
914 
915 	// Add the sub-group views
916 
917 	rect.right = rect.left + 20;
918 	rect.bottom = rect.top + 20;
919 	float lastHeight = 0;
920 
921 	for (int32 i = 0; i < group.CountGroups(); i++) {
922 		BParameterGroup *subGroup = group.GroupAt(i);
923 		if (subGroup == NULL)
924 			continue;
925 
926 		BView *groupView = MakeViewFor(*subGroup, &rect);
927 		if (groupView == NULL)
928 			continue;
929 
930 		if (i > 0) {
931 			// add separator view
932 			BRect separatorRect(groupView->Frame());
933 			separatorRect.left -= 3;
934 			separatorRect.right = separatorRect.left + 1;
935 			if (lastHeight > separatorRect.Height())
936 				separatorRect.bottom = separatorRect.top + lastHeight;
937 
938 			view->AddChild(new SeparatorView(separatorRect));
939 		}
940 
941 		view->AddChild(groupView);
942 
943 		rect.OffsetBy(groupView->Bounds().Width() + 5, 0);
944 
945 		lastHeight = groupView->Bounds().Height();
946 		if (lastHeight > rect.Height())
947 			rect.bottom = rect.top + lastHeight - 1;
948 	}
949 
950 	view->ResizeTo(rect.left + 10, rect.bottom + 5);
951 	view->SetContentBounds(view->Bounds());
952 
953 	if (group.CountParameters() == 0)
954 		return view;
955 
956 	// add the parameter views part of the group
957 
958 	if (group.CountGroups() > 0) {
959 		rect.top = rect.bottom + 10;
960 		rect.bottom = rect.top + 20;
961 	}
962 
963 	bool center = false;
964 
965 	for (int32 i = 0; i < views.CountItems(); i++) {
966 		BView *parameterView = static_cast<BView *>(views.ItemAt(i));
967 
968 		if (parameterView->Bounds().Width() + 5 > rect.Width())
969 			rect.right = parameterView->Bounds().Width() + rect.left + 5;
970 
971 		// we don't need to add the title view again
972 		if (parameterView == titleView)
973 			continue;
974 
975 		// if there is a BChannelSlider (ToDo: or any vertical slider?)
976 		// the views will be centered
977 		if (dynamic_cast<BChannelSlider *>(parameterView) != NULL)
978 			center = true;
979 
980 		parameterView->MoveTo(parameterView->Frame().left, rect.top);
981 		view->AddChild(parameterView);
982 
983 		rect.OffsetBy(0, parameterView->Bounds().Height() + 5);
984 	}
985 
986 	if (views.CountItems() > (titleView != NULL ? 1 : 0))
987 		view->ResizeTo(rect.right + 5, rect.top + 5);
988 
989 	// center the parameter views if needed, and tweak some views
990 
991 	float width = view->Bounds().Width();
992 
993 	for (int32 i = 0; i < views.CountItems(); i++) {
994 		BView *subView = static_cast<BView *>(views.ItemAt(i));
995 		BRect frame = subView->Frame();
996 
997 		if (center)
998 			subView->MoveTo((width - frame.Width()) / 2, frame.top);
999 		else {
1000 			// tweak the PopUp views to look better
1001 			if (dynamic_cast<BOptionPopUp *>(subView) != NULL)
1002 				subView->ResizeTo(width, frame.Height());
1003 		}
1004 	}
1005 
1006 	view->SetContentBounds(view->Bounds());
1007 	return view;
1008 }
1009 
1010 
1011 /*!	This creates a view that handles all incoming messages itself - that's
1012 	what is meant with self-hosting.
1013 */
1014 BView *
1015 DefaultMediaTheme::MakeSelfHostingViewFor(BParameter& parameter,
1016 	const BRect* hintRect)
1017 {
1018 	if (parameter.Flags() & B_HIDDEN_PARAMETER
1019 		|| parameter_should_be_hidden(parameter))
1020 		return NULL;
1021 
1022 	BView *view = MakeViewFor(&parameter, hintRect);
1023 	if (view == NULL) {
1024 		// The MakeViewFor() method above returns a BControl - which we
1025 		// don't need for a null parameter; that's why it returns NULL.
1026 		// But we want to see something anyway, so we add a string view
1027 		// here.
1028 		if (parameter.Type() == BParameter::B_NULL_PARAMETER) {
1029 			if (parameter.Group()->ParameterAt(0) == &parameter) {
1030 				// this is the first parameter in this group, so
1031 				// let's use a nice title view
1032 
1033 				TitleView *titleView = new TitleView(BRect(0, 0, 10, 10), parameter.Name());
1034 				titleView->ResizeToPreferred();
1035 
1036 				return titleView;
1037 			}
1038 			BStringView *stringView = new BStringView(BRect(0, 0, 10, 10),
1039 				parameter.Name(), parameter.Name());
1040 			stringView->SetAlignment(B_ALIGN_CENTER);
1041 			stringView->ResizeToPreferred();
1042 
1043 			return stringView;
1044 		}
1045 
1046 		return NULL;
1047 	}
1048 
1049 	MessageFilter *filter = MessageFilter::FilterFor(view, parameter);
1050 	if (filter != NULL)
1051 		view->AddFilter(filter);
1052 
1053 	return view;
1054 }
1055 
1056 
1057 BControl *
1058 DefaultMediaTheme::MakeViewFor(BParameter *parameter, const BRect *hintRect)
1059 {
1060 	BRect rect;
1061 	if (hintRect)
1062 		rect = *hintRect;
1063 	else
1064 		rect.Set(0, 0, 50, 100);
1065 
1066 	switch (parameter->Type()) {
1067 		case BParameter::B_NULL_PARAMETER:
1068 			// there is no default view for a null parameter
1069 			return NULL;
1070 
1071 		case BParameter::B_DISCRETE_PARAMETER:
1072 		{
1073 			BDiscreteParameter &discrete = static_cast<BDiscreteParameter &>(*parameter);
1074 
1075 			if (!strcmp(discrete.Kind(), B_ENABLE)
1076 				|| !strcmp(discrete.Kind(), B_MUTE)
1077 				|| discrete.CountItems() == 0) {
1078 				// create a checkbox item
1079 
1080 				BCheckBox *checkBox = new BCheckBox(rect, discrete.Name(),
1081 					discrete.Name(), NULL);
1082 				checkBox->ResizeToPreferred();
1083 
1084 				return checkBox;
1085 			} else {
1086 				// create a pop up menu field
1087 
1088 				// ToDo: replace BOptionPopUp (or fix it in Haiku...)
1089 				// this is a workaround for a bug in BOptionPopUp - you need to
1090 				// know the actual width before creating the object - very nice...
1091 
1092 				BFont font;
1093 				float width = 0;
1094 				for (int32 i = 0; i < discrete.CountItems(); i++) {
1095 					float labelWidth = font.StringWidth(discrete.ItemNameAt(i));
1096 					if (labelWidth > width)
1097 						width = labelWidth;
1098 				}
1099 				width += font.StringWidth(discrete.Name()) + 55;
1100 				rect.right = rect.left + width;
1101 
1102 				BOptionPopUp *popUp = new BOptionPopUp(rect, discrete.Name(),
1103 					discrete.Name(), NULL);
1104 
1105 				for (int32 i = 0; i < discrete.CountItems(); i++) {
1106 					popUp->AddOption(discrete.ItemNameAt(i), discrete.ItemValueAt(i));
1107 				}
1108 
1109 				popUp->ResizeToPreferred();
1110 
1111 				return popUp;
1112 			}
1113 		}
1114 
1115 		case BParameter::B_CONTINUOUS_PARAMETER:
1116 		{
1117 			BContinuousParameter &continuous = static_cast<BContinuousParameter &>(*parameter);
1118 
1119 			if (!strcmp(continuous.Kind(), B_MASTER_GAIN)
1120 				|| !strcmp(continuous.Kind(), B_GAIN)) {
1121 				BChannelSlider *slider = new BChannelSlider(rect, continuous.Name(),
1122 					continuous.Name(), NULL, B_VERTICAL, continuous.CountChannels());
1123 
1124 				char minLabel[64], maxLabel[64];
1125 
1126 				const char *unit = continuous.Unit();
1127 				if (unit[0]) {
1128 					// if we have a unit, print it next to the limit values
1129 					sprintf(minLabel, "%g %s", continuous.MinValue(), continuous.Unit());
1130 					sprintf(maxLabel, "%g %s", continuous.MaxValue(), continuous.Unit());
1131 				} else {
1132 					sprintf(minLabel, "%g", continuous.MinValue());
1133 					sprintf(maxLabel, "%g", continuous.MaxValue());
1134 				}
1135 				slider->SetLimitLabels(minLabel, maxLabel);
1136 
1137 				float width, height;
1138 				slider->GetPreferredSize(&width, &height);
1139 				slider->ResizeTo(width, 190);
1140 
1141 				// ToDo: take BContinuousParameter::GetResponse() & ValueStep() into account!
1142 
1143 				for (int32 i = 0; i < continuous.CountChannels(); i++) {
1144 					slider->SetLimitsFor(i, int32(continuous.MinValue() * 1000),
1145 						int32(continuous.MaxValue() * 1000));
1146 				}
1147 
1148 				return slider;
1149 			}
1150 
1151 			BSlider *slider = new BSlider(rect, parameter->Name(), parameter->Name(),
1152 				NULL, 0, 100);
1153 
1154 			float width, height;
1155 			slider->GetPreferredSize(&width, &height);
1156 			slider->ResizeTo(100, height);
1157 
1158 			return slider;
1159 		}
1160 
1161 		default:
1162 			ERROR("BMediaTheme: Don't know parameter type: 0x%x\n",
1163 				parameter->Type());
1164 	}
1165 	return NULL;
1166 }
1167 
1168