xref: /haiku/src/preferences/media/MediaViews.cpp (revision 38b015579ff6f59763abc5ec499e401f34c3be98)
1 /*
2  * Copyright 2003-2011, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Sikosis
7  *		Jérôme Duval
8  */
9 
10 
11 #include "MediaViews.h"
12 
13 #include <AutoDeleter.h>
14 #include <Box.h>
15 #include <Button.h>
16 #include <Catalog.h>
17 #include <CheckBox.h>
18 #include <Deskbar.h>
19 #include <Entry.h>
20 #include <LayoutBuilder.h>
21 #include <Locale.h>
22 #include <MediaAddOn.h>
23 #include <MediaRoster.h>
24 #include <MenuField.h>
25 #include <PopUpMenu.h>
26 #include <String.h>
27 #include <StringView.h>
28 #include <TextView.h>
29 
30 #include <assert.h>
31 #include <stdio.h>
32 
33 #include "MediaWindow.h"
34 
35 
36 #undef B_TRANSLATION_CONTEXT
37 #define B_TRANSLATION_CONTEXT "Media views"
38 
39 #define MEDIA_DEFAULT_INPUT_CHANGE 'dich'
40 #define MEDIA_DEFAULT_OUTPUT_CHANGE 'doch'
41 #define MEDIA_SHOW_HIDE_VOLUME_CONTROL 'shvc'
42 
43 
SettingsView()44 SettingsView::SettingsView()
45 	:
46 	BGroupView(B_VERTICAL, B_USE_DEFAULT_SPACING),
47 	fInputMenu(NULL),
48 	fOutputMenu(NULL)
49 {
50 	// input menu
51 	fInputMenu = new BPopUpMenu(B_TRANSLATE_ALL("<none>",
52 		"VideoInputMenu", "Used when no video input is available"));
53 	fInputMenu->SetLabelFromMarked(true);
54 
55 	// output menu
56 	fOutputMenu = new BPopUpMenu(B_TRANSLATE_ALL("<none>",
57 		"VideoOutputMenu", "Used when no video output is available"));
58 	fOutputMenu->SetLabelFromMarked(true);
59 }
60 
61 
62 BButton*
MakeRestartButton()63 SettingsView::MakeRestartButton()
64 {
65 	return new BButton("restartButton",
66 		B_TRANSLATE("Restart media services"),
67 		new BMessage(ML_RESTART_MEDIA_SERVER));
68 }
69 
70 
71 
72 void
AddInputNodes(NodeList & list)73 SettingsView::AddInputNodes(NodeList& list)
74 {
75 	_EmptyMenu(fInputMenu);
76 
77 	BMessage message(MEDIA_DEFAULT_INPUT_CHANGE);
78 	_PopulateMenu(fInputMenu, list, message);
79 }
80 
81 
82 void
AddOutputNodes(NodeList & list)83 SettingsView::AddOutputNodes(NodeList& list)
84 {
85 	_EmptyMenu(fOutputMenu);
86 
87 	BMessage message(MEDIA_DEFAULT_OUTPUT_CHANGE);
88 	_PopulateMenu(fOutputMenu, list, message);
89 }
90 
91 
92 void
SetDefaultInput(const dormant_node_info * info)93 SettingsView::SetDefaultInput(const dormant_node_info* info)
94 {
95 	_ClearMenuSelection(fInputMenu);
96 	NodeMenuItem* item = _FindNodeItem(fInputMenu, info);
97 	if (item)
98 		item->SetMarked(true);
99 }
100 
101 
102 void
SetDefaultOutput(const dormant_node_info * info)103 SettingsView::SetDefaultOutput(const dormant_node_info* info)
104 {
105 	_ClearMenuSelection(fOutputMenu);
106 	NodeMenuItem* item = _FindNodeItem(fOutputMenu, info);
107 	if (item)
108 		item->SetMarked(true);
109 }
110 
111 
112 void
MessageReceived(BMessage * message)113 SettingsView::MessageReceived(BMessage* message)
114 {
115 	switch (message->what) {
116 		case MEDIA_DEFAULT_INPUT_CHANGE:
117 		{
118 			int32 index;
119 			if (message->FindInt32("index", &index)!=B_OK)
120 				break;
121 			NodeMenuItem* item
122 				= static_cast<NodeMenuItem*>(fInputMenu->ItemAt(index));
123 			SetDefaultInput(item->NodeInfo());
124 			break;
125 		}
126 		case MEDIA_DEFAULT_OUTPUT_CHANGE:
127 		{
128 			int32 index;
129 			if (message->FindInt32("index", &index)!=B_OK)
130 				break;
131 			NodeMenuItem* item
132 				= static_cast<NodeMenuItem*>(fOutputMenu->ItemAt(index));
133 			SetDefaultOutput(item->NodeInfo());
134 			break;
135 		}
136 		default:
137 			BGroupView::MessageReceived(message);
138 	}
139 }
140 
141 
142 void
AttachedToWindow()143 SettingsView::AttachedToWindow()
144 {
145 	BMessenger thisMessenger(this);
146 	fInputMenu->SetTargetForItems(thisMessenger);
147 	fOutputMenu->SetTargetForItems(thisMessenger);
148 }
149 
150 
151 MediaWindow*
_MediaWindow() const152 SettingsView::_MediaWindow() const
153 {
154 	return static_cast<MediaWindow*>(Window());
155 }
156 
157 
158 void
_EmptyMenu(BMenu * menu)159 SettingsView::_EmptyMenu(BMenu* menu)
160 {
161 	while (menu->CountItems() > 0)
162 		delete menu->RemoveItem((int32)0);
163 }
164 
165 
166 void
_PopulateMenu(BMenu * menu,NodeList & nodes,const BMessage & message)167 SettingsView::_PopulateMenu(BMenu* menu, NodeList& nodes,
168 	const BMessage& message)
169 {
170 	for (int32 i = 0; i < nodes.CountItems(); i++) {
171 		dormant_node_info* info = nodes.ItemAt(i);
172 		menu->AddItem(new NodeMenuItem(info, new BMessage(message)));
173 	}
174 
175 	if (Window() != NULL)
176 		menu->SetTargetForItems(BMessenger(this));
177 }
178 
179 
180 NodeMenuItem*
_FindNodeItem(BMenu * menu,const dormant_node_info * nodeInfo)181 SettingsView::_FindNodeItem(BMenu* menu, const dormant_node_info* nodeInfo)
182 {
183 	for (int32 i = 0; i < menu->CountItems(); i++) {
184 		NodeMenuItem* item = static_cast<NodeMenuItem*>(menu->ItemAt(i));
185 		const dormant_node_info* itemInfo = item->NodeInfo();
186 		if (itemInfo && itemInfo->addon == nodeInfo->addon
187 			&& itemInfo->flavor_id == nodeInfo->flavor_id) {
188 			return item;
189 		}
190 	}
191 	return NULL;
192 }
193 
194 
195 void
_ClearMenuSelection(BMenu * menu)196 SettingsView::_ClearMenuSelection(BMenu* menu)
197 {
198 	for (int32 i = 0; i < menu->CountItems(); i++) {
199 		BMenuItem* item = menu->ItemAt(i);
200 		item->SetMarked(false);
201 	}
202 }
203 
204 
NodeMenuItem(const dormant_node_info * info,BMessage * message,char shortcut,uint32 modifiers)205 NodeMenuItem::NodeMenuItem(const dormant_node_info* info, BMessage* message,
206 	char shortcut, uint32 modifiers)
207 	:
208 	BMenuItem(info->name, message, shortcut, modifiers),
209 	fInfo(info)
210 {
211 
212 }
213 
214 
215 status_t
Invoke(BMessage * message)216 NodeMenuItem::Invoke(BMessage* message)
217 {
218 	if (IsMarked())
219 		return B_OK;
220 	return BMenuItem::Invoke(message);
221 }
222 
223 
ChannelMenuItem(media_input * input,BMessage * message,char shortcut,uint32 modifiers)224 ChannelMenuItem::ChannelMenuItem(media_input* input, BMessage* message,
225 	char shortcut, uint32 modifiers)
226 	:
227 	BMenuItem(input->name, message, shortcut, modifiers),
228 	fInput(input)
229 {
230 }
231 
232 
~ChannelMenuItem()233 ChannelMenuItem::~ChannelMenuItem()
234 {
235 	delete fInput;
236 }
237 
238 
239 int32
DestinationID()240 ChannelMenuItem::DestinationID()
241 {
242 	return fInput->destination.id;
243 }
244 
245 
246 media_input*
Input()247 ChannelMenuItem::Input()
248 {
249 	return fInput;
250 }
251 
252 
253 status_t
Invoke(BMessage * message)254 ChannelMenuItem::Invoke(BMessage* message)
255 {
256 	if (IsMarked())
257 		return B_OK;
258 	return BMenuItem::Invoke(message);
259 }
260 
261 
AudioSettingsView()262 AudioSettingsView::AudioSettingsView()
263 {
264 	BBox* defaultsBox = new BBox("defaults");
265 	defaultsBox->SetLabel(B_TRANSLATE("Defaults"));
266 	BGridView* defaultsGridView = new BGridView();
267 
268 	BMenuField* inputMenuField = new BMenuField("inputMenuField",
269 		B_TRANSLATE("Audio input:"), InputMenu());
270 
271 	BMenuField* outputMenuField = new BMenuField("outputMenuField",
272 		B_TRANSLATE("Audio output:"), OutputMenu());
273 
274 	BLayoutBuilder::Grid<>(defaultsGridView)
275 		.SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING,
276 			B_USE_DEFAULT_SPACING)
277 		.AddMenuField(inputMenuField, 0, 0, B_ALIGN_HORIZONTAL_UNSET, 1, 3, 1)
278 		.AddMenuField(outputMenuField, 0, 1)
279 		.AddMenuField(_MakeChannelMenu(), 2, 1);
280 
281 	defaultsBox->AddChild(defaultsGridView);
282 
283 	BLayoutBuilder::Group<>(this)
284 		.SetInsets(0, 0, 0, 0)
285 		.Add(defaultsBox)
286 		.AddGroup(B_HORIZONTAL)
287 			.Add(_MakeVolumeCheckBox())
288 			.AddGlue()
289 			.Add(MakeRestartButton())
290 			.End()
291 		.AddGlue();
292 }
293 
294 
295 void
SetDefaultChannel(int32 channelID)296 AudioSettingsView::SetDefaultChannel(int32 channelID)
297 {
298 	for (int32 i = 0; i < fChannelMenu->CountItems(); i++) {
299 		ChannelMenuItem* item = _ChannelMenuItemAt(i);
300 		item->SetMarked(item->DestinationID() == channelID);
301 	}
302 }
303 
304 
305 void
AttachedToWindow()306 AudioSettingsView::AttachedToWindow()
307 {
308 	SettingsView::AttachedToWindow();
309 
310 	BMessenger thisMessenger(this);
311 	fChannelMenu->SetTargetForItems(thisMessenger);
312 	fVolumeCheckBox->SetTarget(thisMessenger);
313 }
314 
315 
316 void
MessageReceived(BMessage * message)317 AudioSettingsView::MessageReceived(BMessage* message)
318 {
319 	switch (message->what) {
320 		case ML_DEFAULT_CHANNEL_CHANGED:
321 			{
322 				int32 index;
323 				if (message->FindInt32("index", &index) != B_OK)
324 					break;
325 				ChannelMenuItem* item = _ChannelMenuItemAt(index);
326 
327 				if (item) {
328 					BMediaRoster* roster = BMediaRoster::Roster();
329 					roster->SetAudioOutput(*item->Input());
330 				} else
331 					fprintf(stderr, "ChannelMenuItem not found\n");
332 			}
333 			break;
334 		case MEDIA_SHOW_HIDE_VOLUME_CONTROL:
335 		{
336 			if (fVolumeCheckBox->Value() == B_CONTROL_ON)
337 				_ShowDeskbarVolumeControl();
338 			else
339 				_HideDeskbarVolumeControl();
340 			break;
341 		}
342 
343 		default:
344 			SettingsView::MessageReceived(message);
345 	}
346 }
347 
348 
349 void
SetDefaultInput(const dormant_node_info * info)350 AudioSettingsView::SetDefaultInput(const dormant_node_info* info)
351 {
352 	SettingsView::SetDefaultInput(info);
353 	_MediaWindow()->UpdateInputListItem(MediaListItem::AUDIO_TYPE, info);
354 	BMediaRoster::Roster()->SetAudioInput(*info);
355 }
356 
357 
358 void
SetDefaultOutput(const dormant_node_info * info)359 AudioSettingsView::SetDefaultOutput(const dormant_node_info* info)
360 {
361 	SettingsView::SetDefaultOutput(info);
362 	_MediaWindow()->UpdateOutputListItem(MediaListItem::AUDIO_TYPE, info);
363 	_FillChannelMenu(info);
364 	BMediaRoster::Roster()->SetAudioOutput(*info);
365 }
366 
367 
368 BMenuField*
_MakeChannelMenu()369 AudioSettingsView::_MakeChannelMenu()
370 {
371 	fChannelMenu = new BPopUpMenu(B_TRANSLATE("<none>"));
372 	fChannelMenu->SetLabelFromMarked(true);
373 	BMenuField* channelMenuField = new BMenuField("channelMenuField",
374 		B_TRANSLATE("Channel:"), fChannelMenu);
375 	return channelMenuField;
376 }
377 
378 
379 BCheckBox*
_MakeVolumeCheckBox()380 AudioSettingsView::_MakeVolumeCheckBox()
381 {
382 	fVolumeCheckBox = new BCheckBox("volumeCheckBox",
383 		B_TRANSLATE("Show volume control on Deskbar"),
384 		new BMessage(MEDIA_SHOW_HIDE_VOLUME_CONTROL));
385 
386 	if (BDeskbar().HasItem("MediaReplicant"))
387 		fVolumeCheckBox->SetValue(B_CONTROL_ON);
388 
389 	return fVolumeCheckBox;
390 }
391 
392 
393 void
_FillChannelMenu(const dormant_node_info * nodeInfo)394 AudioSettingsView::_FillChannelMenu(const dormant_node_info* nodeInfo)
395 {
396 	_EmptyMenu(fChannelMenu);
397 
398 	BMediaRoster* roster = BMediaRoster::Roster();
399 	media_node node;
400 	media_node_id node_id;
401 
402 	status_t err = roster->GetInstancesFor(nodeInfo->addon,
403 		nodeInfo->flavor_id, &node_id);
404 	if (err != B_OK) {
405 		err = roster->InstantiateDormantNode(*nodeInfo, &node,
406 			B_FLAVOR_IS_GLOBAL);
407 	} else {
408 		err = roster->GetNodeFor(node_id, &node);
409 	}
410 
411 	if (err == B_OK) {
412 		int32 inputCount = 4;
413 		media_input* inputs = new media_input[inputCount];
414 		BPrivate::ArrayDeleter<media_input> inputDeleter(inputs);
415 
416 		while (true) {
417 			int32 realInputCount = 0;
418 			err = roster->GetAllInputsFor(node, inputs,
419 				inputCount, &realInputCount);
420 			if (realInputCount > inputCount) {
421 				inputCount *= 2;
422 				inputs = new media_input[inputCount];
423 				inputDeleter.SetTo(inputs);
424 			} else {
425 				inputCount = realInputCount;
426 				break;
427 			}
428 		}
429 
430 		if (err == B_OK) {
431 			BMessage message(ML_DEFAULT_CHANNEL_CHANGED);
432 
433 			for (int32 i = 0; i < inputCount; i++) {
434 				media_input* input = new media_input();
435 				*input = inputs[i];
436 				ChannelMenuItem* channelItem = new ChannelMenuItem(input,
437 					new BMessage(message));
438 				fChannelMenu->AddItem(channelItem);
439 
440 				if (channelItem->DestinationID() == 0)
441 					channelItem->SetMarked(true);
442 			}
443 		}
444 	}
445 
446 	if (Window())
447 		fChannelMenu->SetTargetForItems(BMessenger(this));
448 }
449 
450 
451 void
_ShowDeskbarVolumeControl()452 AudioSettingsView::_ShowDeskbarVolumeControl()
453 {
454 	BDeskbar deskbar;
455 	BEntry entry("/bin/desklink", true);
456 	int32 id;
457 	entry_ref ref;
458 	status_t status = entry.GetRef(&ref);
459 	if (status == B_OK)
460 		status = deskbar.AddItem(&ref, &id);
461 
462 	if (status != B_OK) {
463 		fprintf(stderr, B_TRANSLATE(
464 			"Couldn't add volume control in Deskbar: %s\n"),
465 			strerror(status));
466 	}
467 }
468 
469 
470 void
_HideDeskbarVolumeControl()471 AudioSettingsView::_HideDeskbarVolumeControl()
472 {
473 	BDeskbar deskbar;
474 	status_t status = deskbar.RemoveItem("MediaReplicant");
475 	if (status != B_OK) {
476 		fprintf(stderr, B_TRANSLATE(
477 			"Couldn't remove volume control in Deskbar: %s\n"),
478 			strerror(status));
479 	}
480 }
481 
482 
483 ChannelMenuItem*
_ChannelMenuItemAt(int32 index)484 AudioSettingsView::_ChannelMenuItemAt(int32 index)
485 {
486 	return static_cast<ChannelMenuItem*>(fChannelMenu->ItemAt(index));
487 }
488 
489 
VideoSettingsView()490 VideoSettingsView::VideoSettingsView()
491 {
492 	BBox* defaultsBox = new BBox("defaults");
493 	defaultsBox->SetLabel(B_TRANSLATE("Defaults"));
494 	BGridView* defaultsGridView = new BGridView();
495 
496 	BMenuField* inputMenuField = new BMenuField("inputMenuField",
497 		B_TRANSLATE("Video input:"), InputMenu());
498 
499 	BMenuField* outputMenuField = new BMenuField("outputMenuField",
500 		B_TRANSLATE("Video output:"), OutputMenu());
501 
502 	BLayoutBuilder::Grid<>(defaultsGridView)
503 		.SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING,
504 			B_USE_DEFAULT_SPACING)
505 		.AddMenuField(inputMenuField, 0, 0)
506 		.AddMenuField(outputMenuField, 0, 1);
507 
508 	defaultsBox->AddChild(defaultsGridView);
509 
510 	BLayoutBuilder::Group<>(this)
511 		.SetInsets(0, 0, 0, 0)
512 		.Add(defaultsBox)
513 		.AddGroup(B_HORIZONTAL)
514 			.AddGlue()
515 			.Add(MakeRestartButton())
516 			.End()
517 		.AddGlue();
518 }
519 
520 
521 void
SetDefaultInput(const dormant_node_info * info)522 VideoSettingsView::SetDefaultInput(const dormant_node_info* info)
523 {
524 	SettingsView::SetDefaultInput(info);
525 	_MediaWindow()->UpdateInputListItem(MediaListItem::VIDEO_TYPE, info);
526 	BMediaRoster::Roster()->SetVideoInput(*info);
527 }
528 
529 
530 void
SetDefaultOutput(const dormant_node_info * info)531 VideoSettingsView::SetDefaultOutput(const dormant_node_info* info)
532 {
533 	SettingsView::SetDefaultOutput(info);
534 	_MediaWindow()->UpdateOutputListItem(MediaListItem::VIDEO_TYPE, info);
535 	BMediaRoster::Roster()->SetVideoOutput(*info);
536 }
537