xref: /haiku/src/preferences/media/MediaWindow.cpp (revision b245bc8dbde0733516fd59e3ccae5eadd89863d4)
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 				_Notify(0.75, B_TRANSLATE("Starting media server"
323 					B_UTF8_ELLIPSIS));
324 			}
325 			break;
326 		}
327 		case B_SOME_APP_QUIT:
328 		{
329 			PRINT_OBJECT(*message);
330 			BString mimeSig;
331 			if (message->FindString("be:signature", &mimeSig) == B_OK) {
332 				if (mimeSig == "application/x-vnd.Be.addon-host"
333 					|| mimeSig == "application/x-vnd.Be.media-server") {
334 					BMediaRoster* roster = BMediaRoster::CurrentRoster();
335 					if (roster != NULL && roster->Lock())
336 						roster->Quit();
337 				}
338 			}
339 			break;
340 		}
341 		default:
342 			BWindow::MessageReceived(message);
343 			break;
344 	}
345 }
346 
347 
348 // #pragma mark - private
349 
350 
351 void
352 MediaWindow::_InitWindow()
353 {
354 	fListView = new BListView("media_list_view");
355 	fListView->SetSelectionMessage(new BMessage(ML_SELECTED_NODE));
356 	fListView->SetExplicitMinSize(BSize(140, B_SIZE_UNSET));
357 
358 	// Add ScrollView to Media Menu for pretty border
359 	BScrollView* scrollView = new BScrollView("listscroller",
360 		fListView, 0, false, false, B_FANCY_BORDER);
361 
362 	// Create the Views
363 	fTitleView = new BSeparatorView(B_HORIZONTAL, B_FANCY_BORDER);
364 	fTitleView->SetLabel(B_TRANSLATE("Audio settings"));
365 	fTitleView->SetFont(be_bold_font);
366 
367 	fContentLayout = new BCardLayout();
368 	new BView("content view", 0, fContentLayout);
369 	fContentLayout->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
370 	fContentLayout->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
371 
372 	fAudioView = new AudioSettingsView();
373 	fContentLayout->AddView(fAudioView);
374 
375 	fVideoView = new VideoSettingsView();
376 	fContentLayout->AddView(fVideoView);
377 
378 	fMidiView = new MidiSettingsView();
379 	fContentLayout->AddView(fMidiView);
380 
381 	// Layout all views
382 	BLayoutBuilder::Group<>(this, B_HORIZONTAL)
383 		.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
384 			B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
385 		.Add(scrollView, 0.0f)
386 		.AddGroup(B_VERTICAL)
387 			.SetInsets(0, 0, 0, 0)
388 			.Add(fTitleView)
389 			.Add(fContentLayout);
390 
391 	// Start the window
392 	fInitCheck = _InitMedia(true);
393 	if (fInitCheck != B_OK)
394 		PostMessage(B_QUIT_REQUESTED);
395 	else if (IsHidden())
396 		Show();
397 }
398 
399 
400 status_t
401 MediaWindow::_InitMedia(bool first)
402 {
403 	status_t err = B_OK;
404 	BMediaRoster* roster = BMediaRoster::Roster(&err);
405 
406 	if (first && err != B_OK) {
407 		BAlert* alert = new BAlert("start_media_server",
408 			B_TRANSLATE("Could not connect to the media server.\n"
409 				"Would you like to start it ?"),
410 			B_TRANSLATE("Quit"),
411 			B_TRANSLATE("Start media server"), NULL,
412 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
413 		alert->SetShortcut(0, B_ESCAPE);
414 		if (alert->Go() == 0)
415 			return B_ERROR;
416 
417 		_Notify(0, B_TRANSLATE("Starting media server" B_UTF8_ELLIPSIS));
418 
419 		Show();
420 
421 		launch_media_server();
422 	}
423 
424 	Lock();
425 
426 	bool isVideoSelected = true;
427 	if (!first && fListView->ItemAt(0) != NULL
428 		&& fListView->ItemAt(0)->IsSelected())
429 		isVideoSelected = false;
430 
431 	if (!first || (first && err) )
432 		_Notify(1, B_TRANSLATE("Ready for use" B_UTF8_ELLIPSIS));
433 
434 	while (fListView->CountItems() > 0)
435 		delete fListView->RemoveItem((int32)0);
436 	_EmptyNodeLists();
437 
438 	// Grab Media Info
439 	_FindNodes();
440 
441 	// Add video nodes first. They might have an additional audio
442 	// output or input, but still should be listed as video node.
443 	_AddNodeItems(fVideoOutputs, MediaListItem::VIDEO_TYPE);
444 	_AddNodeItems(fVideoInputs, MediaListItem::VIDEO_TYPE);
445 	_AddNodeItems(fAudioOutputs, MediaListItem::AUDIO_TYPE);
446 	_AddNodeItems(fAudioInputs, MediaListItem::AUDIO_TYPE);
447 
448 	fAudioView->AddOutputNodes(fAudioOutputs);
449 	fAudioView->AddInputNodes(fAudioInputs);
450 	fVideoView->AddOutputNodes(fVideoOutputs);
451 	fVideoView->AddInputNodes(fVideoInputs);
452 
453 	// build our list view
454 	DeviceListItem* audio = new DeviceListItem(B_TRANSLATE("Audio settings"),
455 		MediaListItem::AUDIO_TYPE);
456 	fListView->AddItem(audio);
457 
458 	MidiListItem* midi = new MidiListItem(B_TRANSLATE("MIDI Settings"));
459 	fListView->AddItem(midi);
460 
461 	MediaListItem* video = new DeviceListItem(B_TRANSLATE("Video settings"),
462 		MediaListItem::VIDEO_TYPE);
463 	fListView->AddItem(video);
464 
465 	MediaListItem* mixer = new AudioMixerListItem(B_TRANSLATE("Audio mixer"));
466 	fListView->AddItem(mixer);
467 
468 	fListView->SortItems(&MediaListItem::Compare);
469 	_UpdateListViewMinWidth();
470 
471 	// Set default nodes for our setting views
472 	media_node defaultNode;
473 	dormant_node_info nodeInfo;
474 	int32 outputID;
475 	BString outputName;
476 
477 	if (roster->GetAudioInput(&defaultNode) == B_OK) {
478 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
479 		fAudioView->SetDefaultInput(&nodeInfo);
480 			// this causes our listview to be updated as well
481 	}
482 
483 	if (roster->GetAudioOutput(&defaultNode, &outputID, &outputName) == B_OK) {
484 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
485 		fAudioView->SetDefaultOutput(&nodeInfo);
486 		fAudioView->SetDefaultChannel(outputID);
487 			// this causes our listview to be updated as well
488 	}
489 
490 	if (roster->GetVideoInput(&defaultNode) == B_OK) {
491 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
492 		fVideoView->SetDefaultInput(&nodeInfo);
493 			// this causes our listview to be updated as well
494 	}
495 
496 	if (roster->GetVideoOutput(&defaultNode) == B_OK) {
497 		roster->GetDormantNodeFor(defaultNode, &nodeInfo);
498 		fVideoView->SetDefaultOutput(&nodeInfo);
499 			// this causes our listview to be updated as well
500 	}
501 
502 	if (first)
503 		fListView->Select(fListView->IndexOf(mixer));
504 	else if (isVideoSelected)
505 		fListView->Select(fListView->IndexOf(video));
506 	else
507 		fListView->Select(fListView->IndexOf(audio));
508 
509 	Unlock();
510 
511 	return B_OK;
512 }
513 
514 
515 void
516 MediaWindow::_FindNodes()
517 {
518 	_FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
519 	_FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
520 	_FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
521 	_FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
522 	_FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
523 	_FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
524 	_FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
525 	_FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
526 }
527 
528 
529 void
530 MediaWindow::_FindNodes(media_type type, uint64 kind, NodeList& into)
531 {
532 	dormant_node_info nodeInfo[64];
533 	int32 nodeInfoCount = 64;
534 
535 	media_format format;
536 	media_format* nodeInputFormat = NULL;
537 	media_format* nodeOutputFormat = NULL;
538 	format.type = type;
539 
540 	// output nodes must be BBufferConsumers => they have an input format
541 	// input nodes must be BBufferProducers => they have an output format
542 	if ((kind & B_PHYSICAL_OUTPUT) != 0)
543 		nodeInputFormat = &format;
544 	else if ((kind & B_PHYSICAL_INPUT) != 0)
545 		nodeOutputFormat = &format;
546 	else
547 		return;
548 
549 	BMediaRoster* roster = BMediaRoster::Roster();
550 
551 	if (roster->GetDormantNodes(nodeInfo, &nodeInfoCount, nodeInputFormat,
552 			nodeOutputFormat, NULL, kind) != B_OK) {
553 		// TODO: better error reporting!
554 		fprintf(stderr, "error\n");
555 		return;
556 	}
557 
558 	for (int32 i = 0; i < nodeInfoCount; i++) {
559 		PRINT(("node : %s, media_addon %i, flavor_id %i\n",
560 			nodeInfo[i].name, (int)nodeInfo[i].addon,
561 			(int)nodeInfo[i].flavor_id));
562 
563 		dormant_node_info* info = new dormant_node_info();
564 		strncpy(info->name, nodeInfo[i].name, B_MEDIA_NAME_LENGTH);
565 		info->flavor_id = nodeInfo[i].flavor_id;
566 		info->addon = nodeInfo[i].addon;
567 		into.AddItem(info);
568 	}
569 }
570 
571 
572 void
573 MediaWindow::_AddNodeItems(NodeList& list, MediaListItem::media_type type)
574 {
575 	int32 count = list.CountItems();
576 	for (int32 i = 0; i < count; i++) {
577 		dormant_node_info* info = list.ItemAt(i);
578 		if (_FindNodeListItem(info) == NULL)
579 			fListView->AddItem(new NodeListItem(info, type));
580 	}
581 }
582 
583 
584 void
585 MediaWindow::_EmptyNodeLists()
586 {
587 	fAudioOutputs.MakeEmpty();
588 	fAudioInputs.MakeEmpty();
589 	fVideoOutputs.MakeEmpty();
590 	fVideoInputs.MakeEmpty();
591 }
592 
593 
594 NodeListItem*
595 MediaWindow::_FindNodeListItem(dormant_node_info* info)
596 {
597 	NodeListItem audioItem(info, MediaListItem::AUDIO_TYPE);
598 	NodeListItem videoItem(info, MediaListItem::VIDEO_TYPE);
599 
600 	NodeListItem::Comparator audioComparator(&audioItem);
601 	NodeListItem::Comparator videoComparator(&videoItem);
602 
603 	for (int32 i = 0; i < fListView->CountItems(); i++) {
604 		MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
605 		item->Accept(audioComparator);
606 		if (audioComparator.result == 0)
607 			return static_cast<NodeListItem*>(item);
608 
609 		item->Accept(videoComparator);
610 		if (videoComparator.result == 0)
611 			return static_cast<NodeListItem*>(item);
612 	}
613 	return NULL;
614 }
615 
616 
617 void
618 MediaWindow::_UpdateListViewMinWidth()
619 {
620 	float width = 0;
621 	for (int32 i = 0; i < fListView->CountItems(); i++) {
622 		BListItem* item = fListView->ItemAt(i);
623 		width = max_c(width, item->Width());
624 	}
625 	fListView->SetExplicitMinSize(BSize(width, B_SIZE_UNSET));
626 	fListView->InvalidateLayout();
627 }
628 
629 
630 status_t
631 MediaWindow::_RestartMediaServices(void* data)
632 {
633 	MediaWindow* window = (MediaWindow*)data;
634 
635 	shutdown_media_server(B_INFINITE_TIMEOUT, MediaWindow::_UpdateProgress,
636 		data);
637 
638 	launch_media_server();
639 
640 	return window->PostMessage(ML_INIT_MEDIA);
641 }
642 
643 
644 bool
645 MediaWindow::_UpdateProgress(int stage, const char* message, void* cookie)
646 {
647 	// parameter "message" is no longer used. It is kept for compatibility with
648 	// BeOS as this is used as a shutdown_media_server callback.
649 
650 	PRINT(("stage : %i\n", stage));
651 	const char* string = "Unknown stage";
652 	switch (stage) {
653 		case 10:
654 			string = B_TRANSLATE("Stopping media server" B_UTF8_ELLIPSIS);
655 			break;
656 		case 20:
657 			string = B_TRANSLATE("Telling media_addon_server to quit.");
658 			break;
659 		case 40:
660 			string = B_TRANSLATE("Waiting for media_server to quit.");
661 			break;
662 		case 70:
663 			string = B_TRANSLATE("Cleaning up.");
664 			break;
665 		case 100:
666 			string = B_TRANSLATE("Done shutting down.");
667 			break;
668 	}
669 
670 	((MediaWindow*)cookie)->_Notify(stage / 150.0, string);
671 
672 	return true;
673 }
674 
675 
676 void
677 MediaWindow::_Notify(float progress, const char* message)
678 {
679 	BNotification info(B_PROGRESS_NOTIFICATION);
680 	info.SetMessageID(MEDIA_SERVICE_NOTIFICATION_ID);
681 	info.SetProgress(progress);
682 	info.SetTitle(B_TRANSLATE("Media Service"));
683 	info.SetContent(message);
684 	info.Send();
685 }
686 
687 
688 void
689 MediaWindow::_ClearParamView()
690 {
691 	BLayoutItem* item = fContentLayout->VisibleItem();
692 	if (!item)
693 		return;
694 
695 	BView* view = item->View();
696 	if (view != fVideoView && view != fAudioView && view != fMidiView) {
697 		fContentLayout->RemoveItem(item);
698 		delete item;
699 		delete view;
700 		delete fParamWeb;
701 		fParamWeb = NULL;
702 	}
703 }
704 
705 
706 void
707 MediaWindow::_MakeParamView()
708 {
709 	if (!fCurrentNode.IsSet())
710 		return;
711 
712 	fParamWeb = NULL;
713 	BMediaRoster* roster = BMediaRoster::Roster();
714 	if (roster->GetParameterWebFor(fCurrentNode, &fParamWeb) == B_OK) {
715 		BRect hint(fContentLayout->Frame());
716 		BView* paramView = BMediaTheme::ViewFor(fParamWeb, &hint);
717 		if (paramView) {
718 			fContentLayout->AddView(paramView);
719 			fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1);
720 			return;
721 		}
722 	}
723 
724 	_MakeEmptyParamView();
725 }
726 
727 
728 void
729 MediaWindow::_MakeEmptyParamView()
730 {
731 	fParamWeb = NULL;
732 
733 	BStringView* stringView = new BStringView("noControls",
734 		B_TRANSLATE("This hardware has no controls."));
735 
736 	BSize unlimited(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
737 	stringView->SetExplicitMaxSize(unlimited);
738 
739 	BAlignment centered(B_ALIGN_HORIZONTAL_CENTER,
740 		B_ALIGN_VERTICAL_CENTER);
741 	stringView->SetExplicitAlignment(centered);
742 	stringView->SetAlignment(B_ALIGN_CENTER);
743 
744 	fContentLayout->AddView(stringView);
745 	fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1);
746 
747 	rgb_color panel = stringView->LowColor();
748 	stringView->SetHighColor(tint_color(panel,
749 		B_DISABLED_LABEL_TINT));
750 }
751 
752