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