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