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