xref: /haiku/src/preferences/media/MediaWindow.cpp (revision aa47adf6e0410809de37a3d57776a6bb2dffc520)
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 <Notification.h>
29 #include <Resources.h>
30 #include <Roster.h>
31 #include <Screen.h>
32 #include <ScrollView.h>
33 #include <SeparatorView.h>
34 #include <SpaceLayoutItem.h>
35 #include <StorageKit.h>
36 #include <String.h>
37 #include <TextView.h>
38 
39 #include "MediaIcons.h"
40 #include "MidiSettingsView.h"
41 
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "Media Window"
44 
45 
46 const uint32 ML_SELECTED_NODE = 'MlSN';
47 const uint32 ML_INIT_MEDIA = 'MlIM';
48 
49 
50 class NodeListItemUpdater : public MediaListItem::Visitor {
51 public:
52 	typedef void (NodeListItem::*UpdateMethod)(bool);
53 
54 	NodeListItemUpdater(NodeListItem* target, UpdateMethod action)
55 		:
56 		fComparator(target),
57 		fAction(action)
58 	{
59 	}
60 
61 
62 	virtual	void	Visit(AudioMixerListItem*){}
63 	virtual	void	Visit(DeviceListItem*){}
64 	virtual	void	Visit(MidiListItem*){}
65 	virtual void	Visit(NodeListItem* item)
66 	{
67 		item->Accept(fComparator);
68 		(item->*(fAction))(fComparator.result == 0);
69 	}
70 
71 private:
72 
73 			NodeListItem::Comparator		fComparator;
74 			UpdateMethod					fAction;
75 };
76 
77 
78 MediaWindow::SmartNode::SmartNode(const BMessenger& notifyHandler)
79 	:
80 	fNode(NULL),
81 	fMessenger(notifyHandler)
82 {
83 }
84 
85 
86 MediaWindow::SmartNode::~SmartNode()
87 {
88 	_FreeNode();
89 }
90 
91 
92 void
93 MediaWindow::SmartNode::SetTo(const dormant_node_info* info)
94 {
95 	_FreeNode();
96 	if (!info)
97 		return;
98 
99 	fNode = new media_node();
100 	BMediaRoster* roster = BMediaRoster::Roster();
101 
102 	// TODO: error codes
103 	media_node_id node_id;
104 	if (roster->GetInstancesFor(info->addon, info->flavor_id, &node_id) == B_OK)
105 		roster->GetNodeFor(node_id, fNode);
106 	else
107 		roster->InstantiateDormantNode(*info, fNode, B_FLAVOR_IS_GLOBAL);
108 	roster->StartWatching(fMessenger, *fNode, B_MEDIA_WILDCARD);
109 }
110 
111 
112 void
113 MediaWindow::SmartNode::SetTo(const media_node& node)
114 {
115 	_FreeNode();
116 	fNode = new media_node(node);
117 	BMediaRoster* roster = BMediaRoster::Roster();
118 	roster->StartWatching(fMessenger, *fNode, B_MEDIA_WILDCARD);
119 }
120 
121 
122 bool
123 MediaWindow::SmartNode::IsSet()
124 {
125 	return fNode != NULL;
126 }
127 
128 
129 MediaWindow::SmartNode::operator media_node()
130 {
131 	if (fNode)
132 		return *fNode;
133 	media_node node;
134 	return node;
135 }
136 
137 
138 void
139 MediaWindow::SmartNode::_FreeNode()
140 {
141 	if (!IsSet())
142 		return;
143 	// TODO: check error codes
144 	BMediaRoster* roster = BMediaRoster::Roster();
145 	roster->StopWatching(fMessenger, *fNode, B_MEDIA_WILDCARD);
146 	roster->ReleaseNode(*fNode);
147 	delete fNode;
148 	fNode = NULL;
149 }
150 
151 
152 // #pragma mark -
153 
154 
155 MediaWindow::MediaWindow(BRect frame)
156 	:
157 	BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Media"), B_TITLED_WINDOW,
158 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
159 	fCurrentNode(BMessenger(this)),
160 	fParamWeb(NULL),
161 	fAudioInputs(5, true),
162 	fAudioOutputs(5, true),
163 	fVideoInputs(5, true),
164 	fVideoOutputs(5, true),
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 
318 			BString mimeSig;
319 			if (message->FindString("be:signature", &mimeSig) == B_OK
320 				&& (mimeSig == "application/x-vnd.Be.addon-host"
321 					|| mimeSig == "application/x-vnd.Be.media-server")) {
322 				BNotification notificationPopup(B_PROGRESS_NOTIFICATION);
323 				notificationPopup.SetMessageID(MEDIA_SERVICE_NOTIFICATION_ID);
324 				notificationPopup.SetTitle(B_TRANSLATE("Media Service"));
325 				notificationPopup.SetProgress(0.5);
326 				notificationPopup.SetContent(
327 					B_TRANSLATE("Starting media server" B_UTF8_ELLIPSIS));
328 				notificationPopup.Send();
329 			}
330 			break;
331 		}
332 		case B_SOME_APP_QUIT:
333 		{
334 			PRINT_OBJECT(*message);
335 			BString mimeSig;
336 			if (message->FindString("be:signature", &mimeSig) == B_OK) {
337 				if (mimeSig == "application/x-vnd.Be.addon-host"
338 					|| mimeSig == "application/x-vnd.Be.media-server") {
339 					BMediaRoster* roster = BMediaRoster::CurrentRoster();
340 					if (roster != NULL && roster->Lock())
341 						roster->Quit();
342 				}
343 			}
344 			break;
345 		}
346 		default:
347 			BWindow::MessageReceived(message);
348 			break;
349 	}
350 }
351 
352 
353 // #pragma mark - private
354 
355 
356 void
357 MediaWindow::_InitWindow()
358 {
359 	fListView = new BListView("media_list_view");
360 	fListView->SetSelectionMessage(new BMessage(ML_SELECTED_NODE));
361 	fListView->SetExplicitMinSize(BSize(140, B_SIZE_UNSET));
362 
363 	// Add ScrollView to Media Menu for pretty border
364 	BScrollView* scrollView = new BScrollView("listscroller",
365 		fListView, 0, false, false, B_FANCY_BORDER);
366 
367 	// Create the Views
368 	fTitleView = new BSeparatorView(B_HORIZONTAL, B_FANCY_BORDER);
369 	fTitleView->SetLabel(B_TRANSLATE("Audio settings"));
370 	fTitleView->SetFont(be_bold_font);
371 
372 	fContentLayout = new BCardLayout();
373 	new BView("content view", 0, fContentLayout);
374 	fContentLayout->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
375 	fContentLayout->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
376 
377 	fAudioView = new AudioSettingsView();
378 	fContentLayout->AddView(fAudioView);
379 
380 	fVideoView = new VideoSettingsView();
381 	fContentLayout->AddView(fVideoView);
382 
383 	fMidiView = new MidiSettingsView();
384 	fContentLayout->AddView(fMidiView);
385 
386 	// Layout all views
387 	BLayoutBuilder::Group<>(this, B_HORIZONTAL)
388 		.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
389 			B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
390 		.Add(scrollView, 0.0f)
391 		.AddGroup(B_VERTICAL)
392 			.SetInsets(0, 0, 0, 0)
393 			.Add(fTitleView)
394 			.Add(fContentLayout);
395 
396 	// Start the window
397 	fInitCheck = _InitMedia(true);
398 	if (fInitCheck != B_OK)
399 		PostMessage(B_QUIT_REQUESTED);
400 	else if (IsHidden())
401 		Show();
402 }
403 
404 
405 status_t
406 MediaWindow::_InitMedia(bool first)
407 {
408 	status_t err = B_OK;
409 	BMediaRoster* roster = BMediaRoster::Roster(&err);
410 
411 	if (first && err != B_OK) {
412 		BAlert* alert = new BAlert("start_media_server",
413 			B_TRANSLATE("Could not connect to the media server.\n"
414 				"Would you like to start it ?"),
415 			B_TRANSLATE("Quit"),
416 			B_TRANSLATE("Start media server"), NULL,
417 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
418 		alert->SetShortcut(0, B_ESCAPE);
419 		if (alert->Go() == 0)
420 			return B_ERROR;
421 
422 		BNotification notificationPopup(B_PROGRESS_NOTIFICATION);
423 		notificationPopup.SetMessageID(MEDIA_SERVICE_NOTIFICATION_ID);
424 		notificationPopup.SetTitle(B_TRANSLATE("Media Service"));
425 		notificationPopup.SetProgress(0.5);
426 		notificationPopup.SetContent(
427 			B_TRANSLATE("Starting media server" B_UTF8_ELLIPSIS));
428 		notificationPopup.Send();
429 
430 		Show();
431 
432 		launch_media_server();
433 	}
434 
435 	Lock();
436 
437 	bool isVideoSelected = true;
438 	if (!first && fListView->ItemAt(0) != NULL
439 		&& fListView->ItemAt(0)->IsSelected())
440 		isVideoSelected = false;
441 
442 	if (!first || (first && err) ) {
443 		BNotification notificationPopup(B_PROGRESS_NOTIFICATION);
444 		notificationPopup.SetMessageID(MEDIA_SERVICE_NOTIFICATION_ID);
445 		notificationPopup.SetTitle(B_TRANSLATE("Media Service"));
446 		notificationPopup.SetProgress(1.0);
447 		notificationPopup.SetContent(
448 			B_TRANSLATE("Ready for use" B_UTF8_ELLIPSIS));
449 		notificationPopup.Send();
450 	}
451 
452 	while (fListView->CountItems() > 0)
453 		delete fListView->RemoveItem((int32)0);
454 	_EmptyNodeLists();
455 
456 	// Grab Media Info
457 	_FindNodes();
458 
459 	// Add video nodes first. They might have an additional audio
460 	// output or input, but still should be listed as video node.
461 	_AddNodeItems(fVideoOutputs, MediaListItem::VIDEO_TYPE);
462 	_AddNodeItems(fVideoInputs, MediaListItem::VIDEO_TYPE);
463 	_AddNodeItems(fAudioOutputs, MediaListItem::AUDIO_TYPE);
464 	_AddNodeItems(fAudioInputs, MediaListItem::AUDIO_TYPE);
465 
466 	fAudioView->AddOutputNodes(fAudioOutputs);
467 	fAudioView->AddInputNodes(fAudioInputs);
468 	fVideoView->AddOutputNodes(fVideoOutputs);
469 	fVideoView->AddInputNodes(fVideoInputs);
470 
471 	// build our list view
472 	DeviceListItem* audio = new DeviceListItem(B_TRANSLATE("Audio settings"),
473 		MediaListItem::AUDIO_TYPE);
474 	fListView->AddItem(audio);
475 
476 	MidiListItem* midi = new MidiListItem(B_TRANSLATE("MIDI Settings"));
477 	fListView->AddItem(midi);
478 
479 	MediaListItem* video = new DeviceListItem(B_TRANSLATE("Video settings"),
480 		MediaListItem::VIDEO_TYPE);
481 	fListView->AddItem(video);
482 
483 	MediaListItem* mixer = new AudioMixerListItem(B_TRANSLATE("Audio mixer"));
484 	fListView->AddItem(mixer);
485 
486 	fListView->SortItems(&MediaListItem::Compare);
487 	_UpdateListViewMinWidth();
488 
489 	// Set default nodes for our setting views
490 	media_node defaultNode;
491 	dormant_node_info nodeInfo;
492 	int32 outputID;
493 	BString outputName;
494 
495 	if (roster->GetAudioInput(&defaultNode) == B_OK) {
496 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
497 		fAudioView->SetDefaultInput(&nodeInfo);
498 			// this causes our listview to be updated as well
499 	}
500 
501 	if (roster->GetAudioOutput(&defaultNode, &outputID, &outputName) == B_OK) {
502 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
503 		fAudioView->SetDefaultOutput(&nodeInfo);
504 		fAudioView->SetDefaultChannel(outputID);
505 			// this causes our listview to be updated as well
506 	}
507 
508 	if (roster->GetVideoInput(&defaultNode) == B_OK) {
509 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
510 		fVideoView->SetDefaultInput(&nodeInfo);
511 			// this causes our listview to be updated as well
512 	}
513 
514 	if (roster->GetVideoOutput(&defaultNode) == B_OK) {
515 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
516 		fVideoView->SetDefaultOutput(&nodeInfo);
517 			// this causes our listview to be updated as well
518 	}
519 
520 	if (first)
521 		fListView->Select(fListView->IndexOf(mixer));
522 	else if (isVideoSelected)
523 		fListView->Select(fListView->IndexOf(video));
524 	else
525 		fListView->Select(fListView->IndexOf(audio));
526 
527 	Unlock();
528 
529 	return B_OK;
530 }
531 
532 
533 void
534 MediaWindow::_FindNodes()
535 {
536 	_FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
537 	_FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
538 	_FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
539 	_FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
540 	_FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
541 	_FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
542 	_FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
543 	_FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
544 }
545 
546 
547 void
548 MediaWindow::_FindNodes(media_type type, uint64 kind, NodeList& into)
549 {
550 	dormant_node_info nodeInfo[64];
551 	int32 nodeInfoCount = 64;
552 
553 	media_format format;
554 	media_format* nodeInputFormat = NULL;
555 	media_format* nodeOutputFormat = NULL;
556 	format.type = type;
557 
558 	// output nodes must be BBufferConsumers => they have an input format
559 	// input nodes must be BBufferProducers => they have an output format
560 	if ((kind & B_PHYSICAL_OUTPUT) != 0)
561 		nodeInputFormat = &format;
562 	else if ((kind & B_PHYSICAL_INPUT) != 0)
563 		nodeOutputFormat = &format;
564 	else
565 		return;
566 
567 	BMediaRoster* roster = BMediaRoster::Roster();
568 
569 	if (roster->GetDormantNodes(nodeInfo, &nodeInfoCount, nodeInputFormat,
570 			nodeOutputFormat, NULL, kind) != B_OK) {
571 		// TODO: better error reporting!
572 		fprintf(stderr, "error\n");
573 		return;
574 	}
575 
576 	for (int32 i = 0; i < nodeInfoCount; i++) {
577 		PRINT(("node : %s, media_addon %i, flavor_id %i\n",
578 			nodeInfo[i].name, (int)nodeInfo[i].addon,
579 			(int)nodeInfo[i].flavor_id));
580 
581 		dormant_node_info* info = new dormant_node_info();
582 		strncpy(info->name, nodeInfo[i].name, B_MEDIA_NAME_LENGTH);
583 		info->flavor_id = nodeInfo[i].flavor_id;
584 		info->addon = nodeInfo[i].addon;
585 		into.AddItem(info);
586 	}
587 }
588 
589 
590 void
591 MediaWindow::_AddNodeItems(NodeList& list, MediaListItem::media_type type)
592 {
593 	int32 count = list.CountItems();
594 	for (int32 i = 0; i < count; i++) {
595 		dormant_node_info* info = list.ItemAt(i);
596 		if (_FindNodeListItem(info) == NULL)
597 			fListView->AddItem(new NodeListItem(info, type));
598 	}
599 }
600 
601 
602 void
603 MediaWindow::_EmptyNodeLists()
604 {
605 	fAudioOutputs.MakeEmpty();
606 	fAudioInputs.MakeEmpty();
607 	fVideoOutputs.MakeEmpty();
608 	fVideoInputs.MakeEmpty();
609 }
610 
611 
612 NodeListItem*
613 MediaWindow::_FindNodeListItem(dormant_node_info* info)
614 {
615 	NodeListItem audioItem(info, MediaListItem::AUDIO_TYPE);
616 	NodeListItem videoItem(info, MediaListItem::VIDEO_TYPE);
617 
618 	NodeListItem::Comparator audioComparator(&audioItem);
619 	NodeListItem::Comparator videoComparator(&videoItem);
620 
621 	for (int32 i = 0; i < fListView->CountItems(); i++) {
622 		MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
623 		item->Accept(audioComparator);
624 		if (audioComparator.result == 0)
625 			return static_cast<NodeListItem*>(item);
626 
627 		item->Accept(videoComparator);
628 		if (videoComparator.result == 0)
629 			return static_cast<NodeListItem*>(item);
630 	}
631 	return NULL;
632 }
633 
634 
635 void
636 MediaWindow::_UpdateListViewMinWidth()
637 {
638 	float width = 0;
639 	for (int32 i = 0; i < fListView->CountItems(); i++) {
640 		BListItem* item = fListView->ItemAt(i);
641 		width = max_c(width, item->Width());
642 	}
643 	fListView->SetExplicitMinSize(BSize(width, B_SIZE_UNSET));
644 	fListView->InvalidateLayout();
645 }
646 
647 
648 status_t
649 MediaWindow::_RestartMediaServices(void* data)
650 {
651 	MediaWindow* window = (MediaWindow*)data;
652 
653 	BNotification notificationPopup(B_PROGRESS_NOTIFICATION);
654 	notificationPopup.SetMessageID(MEDIA_SERVICE_NOTIFICATION_ID);
655 	notificationPopup.SetTitle(B_TRANSLATE("Media Service"));
656 	notificationPopup.SetContent( B_TRANSLATE("Shutting down media server"));
657 
658 	shutdown_media_server(B_INFINITE_TIMEOUT, MediaWindow::_UpdateProgress,
659 		NULL);
660 
661 	notificationPopup.SetContent(
662 		B_TRANSLATE("Starting media server" B_UTF8_ELLIPSIS));
663 	notificationPopup.SetProgress(0.5);
664 	notificationPopup.Send();
665 
666 	launch_media_server();
667 
668 	notificationPopup.SetProgress(1);
669 	notificationPopup.Send();
670 
671 	return window->PostMessage(ML_INIT_MEDIA);
672 }
673 
674 
675 bool
676 MediaWindow::_UpdateProgress(int stage, const char* message, void* cookie)
677 {
678 	// parameters "message" and "cookie" are no longer used.
679 	// They remain here because they're declared within BeOS API and
680 	// thus could not be removed.
681 
682 	PRINT(("stage : %i\n", stage));
683 	const char* string = "Unknown stage";
684 	switch (stage) {
685 		case 10:
686 			string = B_TRANSLATE("Stopping media server" B_UTF8_ELLIPSIS);
687 			break;
688 		case 20:
689 			string = B_TRANSLATE("Telling media_addon_server to quit.");
690 			break;
691 		case 40:
692 			string = B_TRANSLATE("Waiting for media_server to quit.");
693 			break;
694 		case 70:
695 			string = B_TRANSLATE("Cleaning up.");
696 			break;
697 		case 100:
698 			string = B_TRANSLATE("Done shutting down.");
699 			break;
700 	}
701 
702 	BNotification info(B_PROGRESS_NOTIFICATION);
703 	info.SetMessageID(MEDIA_SERVICE_NOTIFICATION_ID);
704 	info.SetProgress(stage/100.0);
705 	info.SetTitle(B_TRANSLATE("Media Service"));
706 	info.SetContent(string);
707 	info.Send();
708 
709 	return true;
710 }
711 
712 
713 void
714 MediaWindow::_ClearParamView()
715 {
716 	BLayoutItem* item = fContentLayout->VisibleItem();
717 	if (!item)
718 		return;
719 
720 	BView* view = item->View();
721 	if (view != fVideoView && view != fAudioView && view != fMidiView) {
722 		fContentLayout->RemoveItem(item);
723 		delete item;
724 		delete view;
725 		delete fParamWeb;
726 		fParamWeb = NULL;
727 	}
728 }
729 
730 
731 void
732 MediaWindow::_MakeParamView()
733 {
734 	if (!fCurrentNode.IsSet())
735 		return;
736 
737 	fParamWeb = NULL;
738 	BMediaRoster* roster = BMediaRoster::Roster();
739 	if (roster->GetParameterWebFor(fCurrentNode, &fParamWeb) == B_OK) {
740 		BRect hint(fContentLayout->Frame());
741 		BView* paramView = BMediaTheme::ViewFor(fParamWeb, &hint);
742 		if (paramView) {
743 			fContentLayout->AddView(paramView);
744 			fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1);
745 			return;
746 		}
747 	}
748 
749 	_MakeEmptyParamView();
750 }
751 
752 
753 void
754 MediaWindow::_MakeEmptyParamView()
755 {
756 	fParamWeb = NULL;
757 
758 	BStringView* stringView = new BStringView("noControls",
759 		B_TRANSLATE("This hardware has no controls."));
760 
761 	BSize unlimited(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
762 	stringView->SetExplicitMaxSize(unlimited);
763 
764 	BAlignment centered(B_ALIGN_HORIZONTAL_CENTER,
765 		B_ALIGN_VERTICAL_CENTER);
766 	stringView->SetExplicitAlignment(centered);
767 	stringView->SetAlignment(B_ALIGN_CENTER);
768 
769 	fContentLayout->AddView(stringView);
770 	fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1);
771 
772 	rgb_color panel = stringView->LowColor();
773 	stringView->SetHighColor(tint_color(panel,
774 		B_DISABLED_LABEL_TINT));
775 }
776 
777