xref: /haiku/src/preferences/media/MediaWindow.cpp (revision e3857211d305a595c2d0b58768f25623d5967675)
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 <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 #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_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 	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 	// 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 // #pragma mark -
152 
153 
154 MediaWindow::MediaWindow(BRect frame)
155 	:
156 	BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Media"), B_TITLED_WINDOW,
157 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
158 	fCurrentNode(BMessenger(this)),
159 	fParamWeb(NULL),
160 	fAudioInputs(5, true),
161 	fAudioOutputs(5, true),
162 	fVideoInputs(5, true),
163 	fVideoOutputs(5, true),
164 	fAlert(NULL),
165 	fInitCheck(B_OK)
166 {
167 	_InitWindow();
168 }
169 
170 
171 MediaWindow::~MediaWindow()
172 {
173 	_EmptyNodeLists();
174 	_ClearParamView();
175 
176 	char buffer[512];
177 	BRect rect = Frame();
178 	PRINT_OBJECT(rect);
179 	snprintf(buffer, 512, "# MediaPrefs Settings\n rect = %i,%i,%i,%i\n",
180 		int(rect.left), int(rect.top), int(rect.right), int(rect.bottom));
181 
182 	BPath path;
183 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
184 		path.Append(SETTINGS_FILE);
185 		BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
186 		if (file.InitCheck() == B_OK)
187 			file.Write(buffer, strlen(buffer));
188 	}
189 }
190 
191 
192 status_t
193 MediaWindow::InitCheck()
194 {
195 	return fInitCheck;
196 }
197 
198 
199 void
200 MediaWindow::SelectNode(const dormant_node_info* node)
201 {
202 	fCurrentNode.SetTo(node);
203 	_MakeParamView();
204 	fTitleView->SetLabel(node->name);
205 }
206 
207 
208 void
209 MediaWindow::SelectAudioSettings(const char* title)
210 {
211 	fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fAudioView));
212 	fTitleView->SetLabel(title);
213 }
214 
215 
216 void
217 MediaWindow::SelectVideoSettings(const char* title)
218 {
219 	fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fVideoView));
220 	fTitleView->SetLabel(title);
221 }
222 
223 
224 void
225 MediaWindow::SelectAudioMixer(const char* title)
226 {
227 	media_node mixerNode;
228 	BMediaRoster* roster = BMediaRoster::Roster();
229 	roster->GetAudioMixer(&mixerNode);
230 	fCurrentNode.SetTo(mixerNode);
231 	_MakeParamView();
232 	fTitleView->SetLabel(title);
233 }
234 
235 
236 void
237 MediaWindow::SelectMidiSettings(const char* title)
238 {
239 	fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fMidiView));
240 	fTitleView->SetLabel(title);
241 }
242 
243 
244 void
245 MediaWindow::UpdateInputListItem(MediaListItem::media_type type,
246 	const dormant_node_info* node)
247 {
248 	NodeListItem compareTo(node, type);
249 	NodeListItemUpdater updater(&compareTo, &NodeListItem::SetDefaultInput);
250 	for (int32 i = 0; i < fListView->CountItems(); i++) {
251 		MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
252 		item->Accept(updater);
253 	}
254 	fListView->Invalidate();
255 }
256 
257 
258 void
259 MediaWindow::UpdateOutputListItem(MediaListItem::media_type type,
260 	const dormant_node_info* node)
261 {
262 	NodeListItem compareTo(node, type);
263 	NodeListItemUpdater updater(&compareTo, &NodeListItem::SetDefaultOutput);
264 	for (int32 i = 0; i < fListView->CountItems(); i++) {
265 		MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
266 		item->Accept(updater);
267 	}
268 	fListView->Invalidate();
269 }
270 
271 
272 bool
273 MediaWindow::QuitRequested()
274 {
275 	// stop watching the MediaRoster
276 	fCurrentNode.SetTo(NULL);
277 	be_app->PostMessage(B_QUIT_REQUESTED);
278 	return true;
279 }
280 
281 
282 void
283 MediaWindow::MessageReceived(BMessage* message)
284 {
285 	switch (message->what) {
286 		case ML_INIT_MEDIA:
287 			_InitMedia(false);
288 			break;
289 		case ML_RESTART_MEDIA_SERVER:
290 		{
291 			thread_id thread = spawn_thread(&MediaWindow::_RestartMediaServices,
292 				"restart_thread", B_NORMAL_PRIORITY, this);
293 			if (thread < 0)
294 				fprintf(stderr, "couldn't create restart thread\n");
295 			else
296 				resume_thread(thread);
297 			break;
298 		}
299 		case B_MEDIA_WEB_CHANGED:
300 		case ML_SELECTED_NODE:
301 		{
302 			PRINT_OBJECT(*message);
303 
304 			MediaListItem* item = static_cast<MediaListItem*>(
305 					fListView->ItemAt(fListView->CurrentSelection()));
306 			if (item == NULL)
307 				break;
308 
309 			fCurrentNode.SetTo(NULL);
310 			_ClearParamView();
311 			item->AlterWindow(this);
312 			break;
313 		}
314 		case B_SOME_APP_LAUNCHED:
315 		{
316 			PRINT_OBJECT(*message);
317 			if (fAlert == NULL)
318 				break;
319 
320 			BString mimeSig;
321 			if (message->FindString("be:signature", &mimeSig) == B_OK
322 				&& (mimeSig == "application/x-vnd.Be.addon-host"
323 					|| mimeSig == "application/x-vnd.Be.media-server")) {
324 				fAlert->Lock();
325 				fAlert->TextView()->SetText(
326 					B_TRANSLATE("Starting media server" B_UTF8_ELLIPSIS));
327 				fAlert->Unlock();
328 			}
329 			break;
330 		}
331 		case B_SOME_APP_QUIT:
332 		{
333 			PRINT_OBJECT(*message);
334 			BString mimeSig;
335 			if (message->FindString("be:signature", &mimeSig) == B_OK) {
336 				if (mimeSig == "application/x-vnd.Be.addon-host"
337 					|| mimeSig == "application/x-vnd.Be.media-server") {
338 					BMediaRoster* roster = BMediaRoster::CurrentRoster();
339 					if (roster != NULL && roster->Lock())
340 						roster->Quit();
341 				}
342 			}
343 			break;
344 		}
345 		default:
346 			BWindow::MessageReceived(message);
347 			break;
348 	}
349 }
350 
351 
352 // #pragma mark - private
353 
354 
355 void
356 MediaWindow::_InitWindow()
357 {
358 	fListView = new BListView("media_list_view");
359 	fListView->SetSelectionMessage(new BMessage(ML_SELECTED_NODE));
360 	fListView->SetExplicitMinSize(BSize(140, B_SIZE_UNSET));
361 
362 	// Add ScrollView to Media Menu for pretty border
363 	BScrollView* scrollView = new BScrollView("listscroller",
364 		fListView, 0, false, false, B_FANCY_BORDER);
365 
366 	// Create the Views
367 	fTitleView = new BSeparatorView(B_HORIZONTAL, B_FANCY_BORDER);
368 	fTitleView->SetLabel(B_TRANSLATE("Audio settings"));
369 	fTitleView->SetFont(be_bold_font);
370 
371 	fContentLayout = new BCardLayout();
372 	new BView("content view", 0, fContentLayout);
373 	fContentLayout->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
374 	fContentLayout->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
375 
376 	fAudioView = new AudioSettingsView();
377 	fContentLayout->AddView(fAudioView);
378 
379 	fVideoView = new VideoSettingsView();
380 	fContentLayout->AddView(fVideoView);
381 
382 	fMidiView = new MidiSettingsView();
383 	fContentLayout->AddView(fMidiView);
384 
385 	// Layout all views
386 	BLayoutBuilder::Group<>(this, B_HORIZONTAL)
387 		.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
388 			B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
389 		.Add(scrollView, 0.0f)
390 		.AddGroup(B_VERTICAL)
391 			.SetInsets(0, 0, 0, 0)
392 			.Add(fTitleView)
393 			.Add(fContentLayout);
394 
395 	// Start the window
396 	fInitCheck = _InitMedia(true);
397 	if (fInitCheck != B_OK)
398 		PostMessage(B_QUIT_REQUESTED);
399 	else if (IsHidden())
400 		Show();
401 }
402 
403 
404 status_t
405 MediaWindow::_InitMedia(bool first)
406 {
407 	status_t err = B_OK;
408 	BMediaRoster* roster = BMediaRoster::Roster(&err);
409 
410 	if (first && err != B_OK) {
411 		BAlert* alert = new BAlert("start_media_server",
412 			B_TRANSLATE("Could not connect to the media server.\n"
413 				"Would you like to start it ?"),
414 			B_TRANSLATE("Quit"),
415 			B_TRANSLATE("Start media server"), NULL,
416 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
417 		alert->SetShortcut(0, B_ESCAPE);
418 		if (alert->Go() == 0)
419 			return B_ERROR;
420 
421 		fAlert = new MediaAlert(BRect(0, 0, 300, 60), "restart_alert",
422 			B_TRANSLATE("Restarting media services\nStarting media server"
423 				B_UTF8_ELLIPSIS "\n"));
424 		fAlert->Show();
425 
426 		Show();
427 
428 		launch_media_server();
429 	}
430 
431 	Lock();
432 
433 	bool isVideoSelected = true;
434 	if (!first && fListView->ItemAt(0) != NULL
435 		&& fListView->ItemAt(0)->IsSelected())
436 		isVideoSelected = false;
437 
438 	if ((!first || (first && err) ) && fAlert) {
439 		BAutolock locker(fAlert);
440 		if (locker.IsLocked())
441 			fAlert->TextView()->SetText(
442 				B_TRANSLATE("Ready for use" B_UTF8_ELLIPSIS));
443 	}
444 
445 	while (fListView->CountItems() > 0)
446 		delete fListView->RemoveItem((int32)0);
447 	_EmptyNodeLists();
448 
449 	// Grab Media Info
450 	_FindNodes();
451 
452 	// Add video nodes first. They might have an additional audio
453 	// output or input, but still should be listed as video node.
454 	_AddNodeItems(fVideoOutputs, MediaListItem::VIDEO_TYPE);
455 	_AddNodeItems(fVideoInputs, MediaListItem::VIDEO_TYPE);
456 	_AddNodeItems(fAudioOutputs, MediaListItem::AUDIO_TYPE);
457 	_AddNodeItems(fAudioInputs, MediaListItem::AUDIO_TYPE);
458 
459 	fAudioView->AddOutputNodes(fAudioOutputs);
460 	fAudioView->AddInputNodes(fAudioInputs);
461 	fVideoView->AddOutputNodes(fVideoOutputs);
462 	fVideoView->AddInputNodes(fVideoInputs);
463 
464 	// build our list view
465 	DeviceListItem* audio = new DeviceListItem(B_TRANSLATE("Audio settings"),
466 		MediaListItem::AUDIO_TYPE);
467 	fListView->AddItem(audio);
468 
469 	MidiListItem* midi = new MidiListItem(B_TRANSLATE("MIDI Settings"));
470 	fListView->AddItem(midi);
471 
472 	MediaListItem* video = new DeviceListItem(B_TRANSLATE("Video settings"),
473 		MediaListItem::VIDEO_TYPE);
474 	fListView->AddItem(video);
475 
476 	MediaListItem* mixer = new AudioMixerListItem(B_TRANSLATE("Audio mixer"));
477 	fListView->AddItem(mixer);
478 
479 	fListView->SortItems(&MediaListItem::Compare);
480 	_UpdateListViewMinWidth();
481 
482 	// Set default nodes for our setting views
483 	media_node defaultNode;
484 	dormant_node_info nodeInfo;
485 	int32 outputID;
486 	BString outputName;
487 
488 	if (roster->GetAudioInput(&defaultNode) == B_OK) {
489 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
490 		fAudioView->SetDefaultInput(&nodeInfo);
491 			// this causes our listview to be updated as well
492 	}
493 
494 	if (roster->GetAudioOutput(&defaultNode, &outputID, &outputName) == B_OK) {
495 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
496 		fAudioView->SetDefaultOutput(&nodeInfo);
497 		fAudioView->SetDefaultChannel(outputID);
498 			// this causes our listview to be updated as well
499 	}
500 
501 	if (roster->GetVideoInput(&defaultNode) == B_OK) {
502 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
503 		fVideoView->SetDefaultInput(&nodeInfo);
504 			// this causes our listview to be updated as well
505 	}
506 
507 	if (roster->GetVideoOutput(&defaultNode) == B_OK) {
508 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
509 		fVideoView->SetDefaultOutput(&nodeInfo);
510 			// this causes our listview to be updated as well
511 	}
512 
513 	if (first)
514 		fListView->Select(fListView->IndexOf(mixer));
515 	else if (isVideoSelected)
516 		fListView->Select(fListView->IndexOf(video));
517 	else
518 		fListView->Select(fListView->IndexOf(audio));
519 
520 	if (fAlert != NULL) {
521 		snooze(800000);
522 		fAlert->PostMessage(B_QUIT_REQUESTED);
523 	}
524 	fAlert = NULL;
525 
526 	Unlock();
527 
528 	return B_OK;
529 }
530 
531 
532 void
533 MediaWindow::_FindNodes()
534 {
535 	_FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
536 	_FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
537 	_FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
538 	_FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
539 	_FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
540 	_FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
541 	_FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
542 	_FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
543 }
544 
545 
546 void
547 MediaWindow::_FindNodes(media_type type, uint64 kind, NodeList& into)
548 {
549 	dormant_node_info nodeInfo[64];
550 	int32 nodeInfoCount = 64;
551 
552 	media_format format;
553 	media_format* nodeInputFormat = NULL;
554 	media_format* nodeOutputFormat = NULL;
555 	format.type = type;
556 
557 	// output nodes must be BBufferConsumers => they have an input format
558 	// input nodes must be BBufferProducers => they have an output format
559 	if ((kind & B_PHYSICAL_OUTPUT) != 0)
560 		nodeInputFormat = &format;
561 	else if ((kind & B_PHYSICAL_INPUT) != 0)
562 		nodeOutputFormat = &format;
563 	else
564 		return;
565 
566 	BMediaRoster* roster = BMediaRoster::Roster();
567 
568 	if (roster->GetDormantNodes(nodeInfo, &nodeInfoCount, nodeInputFormat,
569 			nodeOutputFormat, NULL, kind) != B_OK) {
570 		// TODO: better error reporting!
571 		fprintf(stderr, "error\n");
572 		return;
573 	}
574 
575 	for (int32 i = 0; i < nodeInfoCount; i++) {
576 		PRINT(("node : %s, media_addon %i, flavor_id %i\n",
577 			nodeInfo[i].name, (int)nodeInfo[i].addon,
578 			(int)nodeInfo[i].flavor_id));
579 
580 		dormant_node_info* info = new dormant_node_info();
581 		strncpy(info->name, nodeInfo[i].name, B_MEDIA_NAME_LENGTH);
582 		info->flavor_id = nodeInfo[i].flavor_id;
583 		info->addon = nodeInfo[i].addon;
584 		into.AddItem(info);
585 	}
586 }
587 
588 
589 void
590 MediaWindow::_AddNodeItems(NodeList& list, MediaListItem::media_type type)
591 {
592 	int32 count = list.CountItems();
593 	for (int32 i = 0; i < count; i++) {
594 		dormant_node_info* info = list.ItemAt(i);
595 		if (_FindNodeListItem(info) == NULL)
596 			fListView->AddItem(new NodeListItem(info, type));
597 	}
598 }
599 
600 
601 void
602 MediaWindow::_EmptyNodeLists()
603 {
604 	fAudioOutputs.MakeEmpty();
605 	fAudioInputs.MakeEmpty();
606 	fVideoOutputs.MakeEmpty();
607 	fVideoInputs.MakeEmpty();
608 }
609 
610 
611 NodeListItem*
612 MediaWindow::_FindNodeListItem(dormant_node_info* info)
613 {
614 	NodeListItem audioItem(info, MediaListItem::AUDIO_TYPE);
615 	NodeListItem videoItem(info, MediaListItem::VIDEO_TYPE);
616 
617 	NodeListItem::Comparator audioComparator(&audioItem);
618 	NodeListItem::Comparator videoComparator(&videoItem);
619 
620 	for (int32 i = 0; i < fListView->CountItems(); i++) {
621 		MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
622 		item->Accept(audioComparator);
623 		if (audioComparator.result == 0)
624 			return static_cast<NodeListItem*>(item);
625 
626 		item->Accept(videoComparator);
627 		if (videoComparator.result == 0)
628 			return static_cast<NodeListItem*>(item);
629 	}
630 	return NULL;
631 }
632 
633 
634 void
635 MediaWindow::_UpdateListViewMinWidth()
636 {
637 	float width = 0;
638 	for (int32 i = 0; i < fListView->CountItems(); i++) {
639 		BListItem* item = fListView->ItemAt(i);
640 		width = max_c(width, item->Width());
641 	}
642 	fListView->SetExplicitMinSize(BSize(width, B_SIZE_UNSET));
643 	fListView->InvalidateLayout();
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