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