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