xref: /haiku/src/preferences/media/MediaWindow.cpp (revision e10d416e692354b37f4d9a0461f5690b2c4d1be4)
1 /*
2  * Copyright 2003-2012, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Sikosis, Jérôme Duval
7  *		yourpalal, Alex Wilson
8  */
9 
10 
11 #include "MediaWindow.h"
12 
13 #include <stdio.h>
14 
15 #include <Application.h>
16 #include <Autolock.h>
17 #include <Button.h>
18 #include <CardLayout.h>
19 #include <Catalog.h>
20 #include <Debug.h>
21 #include <Deskbar.h>
22 #include <IconUtils.h>
23 #include <LayoutBuilder.h>
24 #include <Locale.h>
25 #include <MediaRoster.h>
26 #include <MediaTheme.h>
27 #include <Resources.h>
28 #include <Roster.h>
29 #include <Screen.h>
30 #include <ScrollView.h>
31 #include <SeparatorView.h>
32 #include <SpaceLayoutItem.h>
33 #include <StorageKit.h>
34 #include <String.h>
35 #include <TextView.h>
36 
37 #include "Media.h"
38 #include "MediaIcons.h"
39 #include "MidiSettingsView.h"
40 
41 #undef B_TRANSLATION_CONTEXT
42 #define B_TRANSLATION_CONTEXT "Media Window"
43 
44 
45 const uint32 ML_SELECTED_NODE = 'MlSN';
46 const uint32 ML_RESTART_THREAD_FINISHED = 'MlRF';
47 
48 
49 class NodeListItemUpdater : public MediaListItem::Visitor {
50 public:
51 	typedef void (NodeListItem::*UpdateMethod)(bool);
52 
53 	NodeListItemUpdater(NodeListItem* target, UpdateMethod action)
54 		:
55 		fComparator(target),
56 		fAction(action)
57 	{
58 	}
59 
60 
61 	virtual	void	Visit(AudioMixerListItem*){}
62 	virtual	void	Visit(DeviceListItem*){}
63 	virtual	void	Visit(MidiListItem*){}
64 	virtual void	Visit(NodeListItem* item)
65 	{
66 		item->Accept(fComparator);
67 		(item->*(fAction))(fComparator.result == 0);
68 	}
69 
70 private:
71 
72 			NodeListItem::Comparator		fComparator;
73 			UpdateMethod					fAction;
74 };
75 
76 
77 MediaWindow::SmartNode::SmartNode(const BMessenger& notifyHandler)
78 	:
79 	fNode(NULL),
80 	fMessenger(notifyHandler)
81 {
82 }
83 
84 
85 MediaWindow::SmartNode::~SmartNode()
86 {
87 	_FreeNode();
88 }
89 
90 
91 void
92 MediaWindow::SmartNode::SetTo(const dormant_node_info* info)
93 {
94 	_FreeNode();
95 	if (!info)
96 		return;
97 
98 	fNode = new media_node();
99 	BMediaRoster* roster = BMediaRoster::Roster();
100 
101 	status_t status = B_OK;
102 	media_node_id node_id;
103 	if (roster->GetInstancesFor(info->addon, info->flavor_id, &node_id) == B_OK)
104 		status = roster->GetNodeFor(node_id, fNode);
105 	else
106 		status = roster->InstantiateDormantNode(*info, fNode, B_FLAVOR_IS_GLOBAL);
107 
108 	if (status != B_OK) {
109 		fprintf(stderr, "SmartNode::SetTo error with node %" B_PRId32
110 			": %s\n", fNode->node, strerror(status));
111 	}
112 
113 	status = roster->StartWatching(fMessenger, *fNode, B_MEDIA_WILDCARD);
114 	if (status != B_OK) {
115 		fprintf(stderr, "SmartNode::SetTo can't start watching for"
116 			" node %" B_PRId32 "\n", fNode->node);
117 	}
118 }
119 
120 
121 void
122 MediaWindow::SmartNode::SetTo(const media_node& node)
123 {
124 	_FreeNode();
125 	fNode = new media_node(node);
126 	BMediaRoster* roster = BMediaRoster::Roster();
127 	roster->StartWatching(fMessenger, *fNode, B_MEDIA_WILDCARD);
128 }
129 
130 
131 bool
132 MediaWindow::SmartNode::IsSet()
133 {
134 	return fNode != NULL;
135 }
136 
137 
138 MediaWindow::SmartNode::operator media_node()
139 {
140 	if (fNode)
141 		return *fNode;
142 	media_node node;
143 	return node;
144 }
145 
146 
147 void
148 MediaWindow::SmartNode::_FreeNode()
149 {
150 	if (!IsSet())
151 		return;
152 
153 	BMediaRoster* roster = BMediaRoster::Roster();
154 	if (roster != NULL) {
155 		status_t status = roster->StopWatching(fMessenger,
156 			*fNode, B_MEDIA_WILDCARD);
157 		if (status != B_OK) {
158 			fprintf(stderr, "SmartNode::_FreeNode can't unwatch"
159 				" media services for node %" B_PRId32 "\n", fNode->node);
160 		}
161 
162 		roster->ReleaseNode(*fNode);
163 		if (status != B_OK) {
164 			fprintf(stderr, "SmartNode::_FreeNode can't release"
165 				" node %" B_PRId32 "\n", fNode->node);
166 		}
167 	}
168 	delete fNode;
169 	fNode = NULL;
170 }
171 
172 
173 // #pragma mark -
174 
175 
176 MediaWindow::MediaWindow(BRect frame)
177 	:
178 	BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Media"), B_TITLED_WINDOW,
179 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
180 	fCurrentNode(BMessenger(this)),
181 	fParamWeb(NULL),
182 	fAudioInputs(5, true),
183 	fAudioOutputs(5, true),
184 	fVideoInputs(5, true),
185 	fVideoOutputs(5, true),
186 	fInitCheck(B_OK),
187 	fRestartThread(-1),
188 	fRestartAlert(NULL)
189 {
190 	_InitWindow();
191 
192 	BMediaRoster* roster = BMediaRoster::Roster();
193 	roster->StartWatching(BMessenger(this, this),
194 		B_MEDIA_SERVER_STARTED);
195 	roster->StartWatching(BMessenger(this, this),
196 		B_MEDIA_SERVER_QUIT);
197 }
198 
199 
200 MediaWindow::~MediaWindow()
201 {
202 	_EmptyNodeLists();
203 	_ClearParamView();
204 
205 	char buffer[512];
206 	BRect rect = Frame();
207 	PRINT_OBJECT(rect);
208 	snprintf(buffer, 512, "# MediaPrefs Settings\n rect = %i,%i,%i,%i\n",
209 		int(rect.left), int(rect.top), int(rect.right), int(rect.bottom));
210 
211 	BPath path;
212 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
213 		path.Append(SETTINGS_FILE);
214 		BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
215 		if (file.InitCheck() == B_OK)
216 			file.Write(buffer, strlen(buffer));
217 	}
218 
219 	BMediaRoster* roster = BMediaRoster::CurrentRoster();
220 	roster->StopWatching(BMessenger(this, this),
221 		B_MEDIA_SERVER_STARTED);
222 	roster->StartWatching(BMessenger(this, this),
223 		B_MEDIA_SERVER_QUIT);
224 }
225 
226 
227 status_t
228 MediaWindow::InitCheck()
229 {
230 	return fInitCheck;
231 }
232 
233 
234 void
235 MediaWindow::SelectNode(const dormant_node_info* node)
236 {
237 	fCurrentNode.SetTo(node);
238 	_MakeParamView();
239 	fTitleView->SetLabel(node->name);
240 }
241 
242 
243 void
244 MediaWindow::SelectAudioSettings(const char* title)
245 {
246 	fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fAudioView));
247 	fTitleView->SetLabel(title);
248 }
249 
250 
251 void
252 MediaWindow::SelectVideoSettings(const char* title)
253 {
254 	fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fVideoView));
255 	fTitleView->SetLabel(title);
256 }
257 
258 
259 void
260 MediaWindow::SelectAudioMixer(const char* title)
261 {
262 	media_node mixerNode;
263 	BMediaRoster* roster = BMediaRoster::Roster();
264 	roster->GetAudioMixer(&mixerNode);
265 	fCurrentNode.SetTo(mixerNode);
266 	_MakeParamView();
267 	fTitleView->SetLabel(title);
268 }
269 
270 
271 void
272 MediaWindow::SelectMidiSettings(const char* title)
273 {
274 	fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fMidiView));
275 	fTitleView->SetLabel(title);
276 }
277 
278 
279 void
280 MediaWindow::UpdateInputListItem(MediaListItem::media_type type,
281 	const dormant_node_info* node)
282 {
283 	NodeListItem compareTo(node, type);
284 	NodeListItemUpdater updater(&compareTo, &NodeListItem::SetDefaultInput);
285 	for (int32 i = 0; i < fListView->CountItems(); i++) {
286 		MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
287 		item->Accept(updater);
288 	}
289 	fListView->Invalidate();
290 }
291 
292 
293 void
294 MediaWindow::UpdateOutputListItem(MediaListItem::media_type type,
295 	const dormant_node_info* node)
296 {
297 	NodeListItem compareTo(node, type);
298 	NodeListItemUpdater updater(&compareTo, &NodeListItem::SetDefaultOutput);
299 	for (int32 i = 0; i < fListView->CountItems(); i++) {
300 		MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
301 		item->Accept(updater);
302 	}
303 	fListView->Invalidate();
304 }
305 
306 
307 bool
308 MediaWindow::QuitRequested()
309 {
310 	if (fRestartThread > 0) {
311 		BString text(B_TRANSLATE("Quitting Media now will stop the "
312 			"restarting of the media services. Flaky or unavailable media "
313 			"functionality is the likely result."));
314 
315 		fRestartAlert = new BAlert(B_TRANSLATE("Warning!"), text,
316 			B_TRANSLATE("Quit anyway"), NULL, NULL,
317 			B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
318 
319 		fRestartAlert->Go();
320 	}
321 	// Stop watching the MediaRoster
322 	fCurrentNode.SetTo(NULL);
323 	be_app->PostMessage(B_QUIT_REQUESTED);
324 	return true;
325 }
326 
327 
328 void
329 MediaWindow::MessageReceived(BMessage* message)
330 {
331 	switch (message->what) {
332 		case ML_RESTART_THREAD_FINISHED:
333 			fRestartThread = -1;
334 			_InitMedia(false);
335 			break;
336 
337 		case ML_RESTART_MEDIA_SERVER:
338 		{
339 			fRestartThread = spawn_thread(&MediaWindow::_RestartMediaServices,
340 				"restart_thread", B_NORMAL_PRIORITY, this);
341 			if (fRestartThread < 0)
342 				fprintf(stderr, "couldn't create restart thread\n");
343 			else
344 				resume_thread(fRestartThread);
345 			break;
346 		}
347 
348 		case B_MEDIA_WEB_CHANGED:
349 		case ML_SELECTED_NODE:
350 		{
351 			PRINT_OBJECT(*message);
352 
353 			MediaListItem* item = static_cast<MediaListItem*>(
354 					fListView->ItemAt(fListView->CurrentSelection()));
355 			if (item == NULL)
356 				break;
357 
358 			fCurrentNode.SetTo(NULL);
359 			_ClearParamView();
360 			item->AlterWindow(this);
361 			break;
362 		}
363 
364 		case B_MEDIA_SERVER_STARTED:
365 		case B_MEDIA_SERVER_QUIT:
366 		{
367 			PRINT_OBJECT(*message);
368 			_InitMedia(false);
369 			break;
370 		}
371 
372 		default:
373 			BWindow::MessageReceived(message);
374 			break;
375 	}
376 }
377 
378 
379 // #pragma mark - private
380 
381 
382 void
383 MediaWindow::_InitWindow()
384 {
385 	fListView = new BListView("media_list_view");
386 	fListView->SetSelectionMessage(new BMessage(ML_SELECTED_NODE));
387 	fListView->SetExplicitMinSize(BSize(140, B_SIZE_UNSET));
388 
389 	// Add ScrollView to Media Menu for pretty border
390 	BScrollView* scrollView = new BScrollView("listscroller",
391 		fListView, 0, false, false, B_FANCY_BORDER);
392 
393 	// Create the Views
394 	fTitleView = new BSeparatorView(B_HORIZONTAL, B_FANCY_BORDER);
395 	fTitleView->SetLabel(B_TRANSLATE("Audio settings"));
396 	fTitleView->SetFont(be_bold_font);
397 
398 	fContentLayout = new BCardLayout();
399 	new BView("content view", 0, fContentLayout);
400 	fContentLayout->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
401 	fContentLayout->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
402 
403 	fAudioView = new AudioSettingsView();
404 	fContentLayout->AddView(fAudioView);
405 
406 	fVideoView = new VideoSettingsView();
407 	fContentLayout->AddView(fVideoView);
408 
409 	fMidiView = new MidiSettingsView();
410 	fContentLayout->AddView(fMidiView);
411 
412 	// Layout all views
413 	BLayoutBuilder::Group<>(this, B_HORIZONTAL)
414 		.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
415 			B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
416 		.Add(scrollView, 0.0f)
417 		.AddGroup(B_VERTICAL)
418 			.SetInsets(0, 0, 0, 0)
419 			.Add(fTitleView)
420 			.Add(fContentLayout);
421 
422 	// Start the window
423 	fInitCheck = _InitMedia(true);
424 	if (fInitCheck != B_OK)
425 		PostMessage(B_QUIT_REQUESTED);
426 	else if (IsHidden())
427 		Show();
428 }
429 
430 
431 status_t
432 MediaWindow::_InitMedia(bool first)
433 {
434 	status_t err = B_OK;
435 	BMediaRoster* roster = BMediaRoster::Roster(&err);
436 
437 	if (first && err != B_OK) {
438 		BAlert* alert = new BAlert("start_media_server",
439 			B_TRANSLATE("Could not connect to the media server.\n"
440 				"Would you like to start it ?"),
441 			B_TRANSLATE("Quit"),
442 			B_TRANSLATE("Start media server"), NULL,
443 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
444 		alert->SetShortcut(0, B_ESCAPE);
445 		if (alert->Go() == 0)
446 			return B_ERROR;
447 
448 		Show();
449 
450 		launch_media_server();
451 	}
452 
453 	Lock();
454 
455 	bool isVideoSelected = true;
456 	if (!first && fListView->ItemAt(0) != NULL
457 		&& fListView->ItemAt(0)->IsSelected())
458 		isVideoSelected = false;
459 
460 	while (fListView->CountItems() > 0)
461 		delete fListView->RemoveItem((int32)0);
462 	_EmptyNodeLists();
463 
464 	// Grab Media Info
465 	_FindNodes();
466 
467 	// Add video nodes first. They might have an additional audio
468 	// output or input, but still should be listed as video node.
469 	_AddNodeItems(fVideoOutputs, MediaListItem::VIDEO_TYPE);
470 	_AddNodeItems(fVideoInputs, MediaListItem::VIDEO_TYPE);
471 	_AddNodeItems(fAudioOutputs, MediaListItem::AUDIO_TYPE);
472 	_AddNodeItems(fAudioInputs, MediaListItem::AUDIO_TYPE);
473 
474 	fAudioView->AddOutputNodes(fAudioOutputs);
475 	fAudioView->AddInputNodes(fAudioInputs);
476 	fVideoView->AddOutputNodes(fVideoOutputs);
477 	fVideoView->AddInputNodes(fVideoInputs);
478 
479 	// build our list view
480 	DeviceListItem* audio = new DeviceListItem(B_TRANSLATE("Audio settings"),
481 		MediaListItem::AUDIO_TYPE);
482 	fListView->AddItem(audio);
483 
484 	MidiListItem* midi = new MidiListItem(B_TRANSLATE("MIDI Settings"));
485 	fListView->AddItem(midi);
486 
487 	MediaListItem* video = new DeviceListItem(B_TRANSLATE("Video settings"),
488 		MediaListItem::VIDEO_TYPE);
489 	fListView->AddItem(video);
490 
491 	MediaListItem* mixer = new AudioMixerListItem(B_TRANSLATE("Audio mixer"));
492 	fListView->AddItem(mixer);
493 
494 	fListView->SortItems(&MediaListItem::Compare);
495 	_UpdateListViewMinWidth();
496 
497 	// Set default nodes for our setting views
498 	media_node defaultNode;
499 	dormant_node_info nodeInfo;
500 	int32 outputID;
501 	BString outputName;
502 
503 	if (roster->GetAudioInput(&defaultNode) == B_OK) {
504 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
505 		fAudioView->SetDefaultInput(&nodeInfo);
506 			// this causes our listview to be updated as well
507 	}
508 
509 	if (roster->GetAudioOutput(&defaultNode, &outputID, &outputName) == B_OK) {
510 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
511 		fAudioView->SetDefaultOutput(&nodeInfo);
512 		fAudioView->SetDefaultChannel(outputID);
513 			// this causes our listview to be updated as well
514 	}
515 
516 	if (roster->GetVideoInput(&defaultNode) == B_OK) {
517 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
518 		fVideoView->SetDefaultInput(&nodeInfo);
519 			// this causes our listview to be updated as well
520 	}
521 
522 	if (roster->GetVideoOutput(&defaultNode) == B_OK) {
523 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
524 		fVideoView->SetDefaultOutput(&nodeInfo);
525 			// this causes our listview to be updated as well
526 	}
527 
528 	if (first)
529 		fListView->Select(fListView->IndexOf(mixer));
530 	else if (isVideoSelected)
531 		fListView->Select(fListView->IndexOf(video));
532 	else
533 		fListView->Select(fListView->IndexOf(audio));
534 
535 	Unlock();
536 
537 	return B_OK;
538 }
539 
540 
541 void
542 MediaWindow::_FindNodes()
543 {
544 	_FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
545 	_FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
546 	_FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
547 	_FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
548 	_FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
549 	_FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
550 	_FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
551 	_FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
552 }
553 
554 
555 void
556 MediaWindow::_FindNodes(media_type type, uint64 kind, NodeList& into)
557 {
558 	dormant_node_info nodeInfo[64];
559 	int32 nodeInfoCount = 64;
560 
561 	media_format format;
562 	media_format* nodeInputFormat = NULL;
563 	media_format* nodeOutputFormat = NULL;
564 	format.type = type;
565 
566 	// output nodes must be BBufferConsumers => they have an input format
567 	// input nodes must be BBufferProducers => they have an output format
568 	if ((kind & B_PHYSICAL_OUTPUT) != 0)
569 		nodeInputFormat = &format;
570 	else if ((kind & B_PHYSICAL_INPUT) != 0)
571 		nodeOutputFormat = &format;
572 	else
573 		return;
574 
575 	BMediaRoster* roster = BMediaRoster::Roster();
576 
577 	if (roster->GetDormantNodes(nodeInfo, &nodeInfoCount, nodeInputFormat,
578 			nodeOutputFormat, NULL, kind) != B_OK) {
579 		// TODO: better error reporting!
580 		fprintf(stderr, "error\n");
581 		return;
582 	}
583 
584 	for (int32 i = 0; i < nodeInfoCount; i++) {
585 		PRINT(("node : %s, media_addon %i, flavor_id %i\n",
586 			nodeInfo[i].name, (int)nodeInfo[i].addon,
587 			(int)nodeInfo[i].flavor_id));
588 
589 		dormant_node_info* info = new dormant_node_info();
590 		strncpy(info->name, nodeInfo[i].name, B_MEDIA_NAME_LENGTH);
591 		info->flavor_id = nodeInfo[i].flavor_id;
592 		info->addon = nodeInfo[i].addon;
593 		into.AddItem(info);
594 	}
595 }
596 
597 
598 void
599 MediaWindow::_AddNodeItems(NodeList& list, MediaListItem::media_type type)
600 {
601 	int32 count = list.CountItems();
602 	for (int32 i = 0; i < count; i++) {
603 		dormant_node_info* info = list.ItemAt(i);
604 		if (_FindNodeListItem(info) == NULL)
605 			fListView->AddItem(new NodeListItem(info, type));
606 	}
607 }
608 
609 
610 void
611 MediaWindow::_EmptyNodeLists()
612 {
613 	fAudioOutputs.MakeEmpty();
614 	fAudioInputs.MakeEmpty();
615 	fVideoOutputs.MakeEmpty();
616 	fVideoInputs.MakeEmpty();
617 }
618 
619 
620 NodeListItem*
621 MediaWindow::_FindNodeListItem(dormant_node_info* info)
622 {
623 	NodeListItem audioItem(info, MediaListItem::AUDIO_TYPE);
624 	NodeListItem videoItem(info, MediaListItem::VIDEO_TYPE);
625 
626 	NodeListItem::Comparator audioComparator(&audioItem);
627 	NodeListItem::Comparator videoComparator(&videoItem);
628 
629 	for (int32 i = 0; i < fListView->CountItems(); i++) {
630 		MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
631 		item->Accept(audioComparator);
632 		if (audioComparator.result == 0)
633 			return static_cast<NodeListItem*>(item);
634 
635 		item->Accept(videoComparator);
636 		if (videoComparator.result == 0)
637 			return static_cast<NodeListItem*>(item);
638 	}
639 	return NULL;
640 }
641 
642 
643 void
644 MediaWindow::_UpdateListViewMinWidth()
645 {
646 	float width = 0;
647 	for (int32 i = 0; i < fListView->CountItems(); i++) {
648 		BListItem* item = fListView->ItemAt(i);
649 		width = max_c(width, item->Width());
650 	}
651 	fListView->SetExplicitMinSize(BSize(width, B_SIZE_UNSET));
652 	fListView->InvalidateLayout();
653 }
654 
655 
656 status_t
657 MediaWindow::_RestartMediaServices(void* data)
658 {
659 	MediaWindow* window = (MediaWindow*)data;
660 
661 	shutdown_media_server();
662 	launch_media_server();
663 
664 	if (window->fRestartAlert != NULL
665 			&& window->fRestartAlert->Lock()) {
666 		window->fRestartAlert->Quit();
667 	}
668 
669 	return window->PostMessage(ML_RESTART_THREAD_FINISHED);
670 }
671 
672 
673 void
674 MediaWindow::_ClearParamView()
675 {
676 	BLayoutItem* item = fContentLayout->VisibleItem();
677 	if (!item)
678 		return;
679 
680 	BView* view = item->View();
681 	if (view != fVideoView && view != fAudioView && view != fMidiView) {
682 		fContentLayout->RemoveItem(item);
683 		delete item;
684 		delete view;
685 		delete fParamWeb;
686 		fParamWeb = NULL;
687 	}
688 }
689 
690 
691 void
692 MediaWindow::_MakeParamView()
693 {
694 	if (!fCurrentNode.IsSet())
695 		return;
696 
697 	fParamWeb = NULL;
698 	BMediaRoster* roster = BMediaRoster::Roster();
699 	if (roster->GetParameterWebFor(fCurrentNode, &fParamWeb) == B_OK) {
700 		BRect hint(fContentLayout->Frame());
701 		BView* paramView = BMediaTheme::ViewFor(fParamWeb, &hint);
702 		if (paramView) {
703 			fContentLayout->AddView(paramView);
704 			fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1);
705 			return;
706 		}
707 	}
708 
709 	_MakeEmptyParamView();
710 }
711 
712 
713 void
714 MediaWindow::_MakeEmptyParamView()
715 {
716 	fParamWeb = NULL;
717 
718 	BStringView* stringView = new BStringView("noControls",
719 		B_TRANSLATE("This hardware has no controls."));
720 
721 	BSize unlimited(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
722 	stringView->SetExplicitMaxSize(unlimited);
723 
724 	BAlignment centered(B_ALIGN_HORIZONTAL_CENTER,
725 		B_ALIGN_VERTICAL_CENTER);
726 	stringView->SetExplicitAlignment(centered);
727 	stringView->SetAlignment(B_ALIGN_CENTER);
728 
729 	fContentLayout->AddView(stringView);
730 	fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1);
731 
732 	rgb_color panel = stringView->LowColor();
733 	stringView->SetHighColor(tint_color(panel,
734 		B_DISABLED_LABEL_TINT));
735 }
736 
737