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