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