xref: /haiku/src/preferences/media/MediaViews.cpp (revision cdf894ce17f72d23be5e729a5d6877b7124c4654)
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 
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 			.AddGlue()
287 			.Add(MakeRestartButton())
288 			.End()
289 		.AddGlue();
290 }
291 
292 
293 void
294 AudioSettingsView::SetDefaultChannel(int32 channelID)
295 {
296 	for (int32 i = 0; i < fChannelMenu->CountItems(); i++) {
297 		ChannelMenuItem* item = _ChannelMenuItemAt(i);
298 		item->SetMarked(item->DestinationID() == channelID);
299 	}
300 }
301 
302 
303 void
304 AudioSettingsView::AttachedToWindow()
305 {
306 	SettingsView::AttachedToWindow();
307 
308 	BMessenger thisMessenger(this);
309 	fChannelMenu->SetTargetForItems(thisMessenger);
310 	fVolumeCheckBox->SetTarget(thisMessenger);
311 }
312 
313 
314 void
315 AudioSettingsView::MessageReceived(BMessage* message)
316 {
317 	switch (message->what) {
318 		case ML_DEFAULT_CHANNEL_CHANGED:
319 			{
320 				int32 index;
321 				if (message->FindInt32("index", &index) != B_OK)
322 					break;
323 				ChannelMenuItem* item = _ChannelMenuItemAt(index);
324 
325 				if (item) {
326 					BMediaRoster* roster = BMediaRoster::Roster();
327 					roster->SetAudioOutput(*item->Input());
328 				} else
329 					fprintf(stderr, "ChannelMenuItem not found\n");
330 			}
331 			break;
332 		case MEDIA_SHOW_HIDE_VOLUME_CONTROL:
333 		{
334 			if (fVolumeCheckBox->Value() == B_CONTROL_ON)
335 				_ShowDeskbarVolumeControl();
336 			else
337 				_HideDeskbarVolumeControl();
338 			break;
339 		}
340 
341 		default:
342 			SettingsView::MessageReceived(message);
343 	}
344 }
345 
346 
347 void
348 AudioSettingsView::SetDefaultInput(const dormant_node_info* info)
349 {
350 	SettingsView::SetDefaultInput(info);
351 	_MediaWindow()->UpdateInputListItem(MediaListItem::AUDIO_TYPE, info);
352 	BMediaRoster::Roster()->SetAudioInput(*info);
353 }
354 
355 
356 void
357 AudioSettingsView::SetDefaultOutput(const dormant_node_info* info)
358 {
359 	SettingsView::SetDefaultOutput(info);
360 	_MediaWindow()->UpdateOutputListItem(MediaListItem::AUDIO_TYPE, info);
361 	_FillChannelMenu(info);
362 	BMediaRoster::Roster()->SetAudioOutput(*info);
363 }
364 
365 
366 BMenuField*
367 AudioSettingsView::_MakeChannelMenu()
368 {
369 	fChannelMenu = new BPopUpMenu(B_TRANSLATE("<none>"));
370 	fChannelMenu->SetLabelFromMarked(true);
371 	BMenuField* channelMenuField = new BMenuField("channelMenuField",
372 		B_TRANSLATE("Channel:"), fChannelMenu);
373 	return channelMenuField;
374 }
375 
376 
377 BCheckBox*
378 AudioSettingsView::_MakeVolumeCheckBox()
379 {
380 	fVolumeCheckBox = new BCheckBox("volumeCheckBox",
381 		B_TRANSLATE("Show volume control on Deskbar"),
382 		new BMessage(MEDIA_SHOW_HIDE_VOLUME_CONTROL));
383 
384 	if (BDeskbar().HasItem("MediaReplicant"))
385 		fVolumeCheckBox->SetValue(B_CONTROL_ON);
386 
387 	return fVolumeCheckBox;
388 }
389 
390 
391 void
392 AudioSettingsView::_FillChannelMenu(const dormant_node_info* nodeInfo)
393 {
394 	_EmptyMenu(fChannelMenu);
395 
396 	BMediaRoster* roster = BMediaRoster::Roster();
397 	media_node node;
398 	media_node_id node_id;
399 
400 	status_t err = roster->GetInstancesFor(nodeInfo->addon,
401 		nodeInfo->flavor_id, &node_id);
402 	if (err != B_OK) {
403 		err = roster->InstantiateDormantNode(*nodeInfo, &node,
404 			B_FLAVOR_IS_GLOBAL);
405 	} else {
406 		err = roster->GetNodeFor(node_id, &node);
407 	}
408 
409 	if (err == B_OK) {
410 		int32 inputCount = 4;
411 		media_input* inputs = new media_input[inputCount];
412 		BPrivate::ArrayDeleter<media_input> inputDeleter(inputs);
413 
414 		while (true) {
415 			int32 realInputCount = 0;
416 			err = roster->GetAllInputsFor(node, inputs,
417 				inputCount, &realInputCount);
418 			if (realInputCount > inputCount) {
419 				inputCount *= 2;
420 				inputs = new media_input[inputCount];
421 				inputDeleter.SetTo(inputs);
422 			} else {
423 				inputCount = realInputCount;
424 				break;
425 			}
426 		}
427 
428 		if (err == B_OK) {
429 			BMessage message(ML_DEFAULT_CHANNEL_CHANGED);
430 
431 			for (int32 i = 0; i < inputCount; i++) {
432 				media_input* input = new media_input();
433 				memcpy(input, &inputs[i], sizeof(*input));
434 				ChannelMenuItem* channelItem = new ChannelMenuItem(input,
435 					new BMessage(message));
436 				fChannelMenu->AddItem(channelItem);
437 
438 				if (channelItem->DestinationID() == 0)
439 					channelItem->SetMarked(true);
440 			}
441 		}
442 	}
443 
444 	if (Window())
445 		fChannelMenu->SetTargetForItems(BMessenger(this));
446 }
447 
448 
449 void
450 AudioSettingsView::_ShowDeskbarVolumeControl()
451 {
452 	BDeskbar deskbar;
453 	BEntry entry("/bin/desklink", true);
454 	int32 id;
455 	entry_ref ref;
456 	status_t status = entry.GetRef(&ref);
457 	if (status == B_OK)
458 		status = deskbar.AddItem(&ref, &id);
459 
460 	if (status != B_OK) {
461 		fprintf(stderr, B_TRANSLATE(
462 			"Couldn't add volume control in Deskbar: %s\n"),
463 			strerror(status));
464 	}
465 }
466 
467 
468 void
469 AudioSettingsView::_HideDeskbarVolumeControl()
470 {
471 	BDeskbar deskbar;
472 	status_t status = deskbar.RemoveItem("MediaReplicant");
473 	if (status != B_OK) {
474 		fprintf(stderr, B_TRANSLATE(
475 			"Couldn't remove volume control in Deskbar: %s\n"),
476 			strerror(status));
477 	}
478 }
479 
480 
481 ChannelMenuItem*
482 AudioSettingsView::_ChannelMenuItemAt(int32 index)
483 {
484 	return static_cast<ChannelMenuItem*>(fChannelMenu->ItemAt(index));
485 }
486 
487 
488 VideoSettingsView::VideoSettingsView()
489 {
490 	BBox* defaultsBox = new BBox("defaults");
491 	defaultsBox->SetLabel(B_TRANSLATE("Defaults"));
492 	BGridView* defaultsGridView = new BGridView();
493 
494 	BMenuField* inputMenuField = new BMenuField("inputMenuField",
495 		B_TRANSLATE("Video input:"), InputMenu());
496 
497 	BMenuField* outputMenuField = new BMenuField("outputMenuField",
498 		B_TRANSLATE("Video output:"), OutputMenu());
499 
500 	BLayoutBuilder::Grid<>(defaultsGridView)
501 		.SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING,
502 			B_USE_DEFAULT_SPACING)
503 		.AddMenuField(inputMenuField, 0, 0)
504 		.AddMenuField(outputMenuField, 0, 1);
505 
506 	defaultsBox->AddChild(defaultsGridView);
507 
508 	BLayoutBuilder::Group<>(this)
509 		.SetInsets(0, 0, 0, 0)
510 		.Add(defaultsBox)
511 		.AddGroup(B_HORIZONTAL)
512 			.AddGlue()
513 			.Add(MakeRestartButton())
514 			.End()
515 		.AddGlue();
516 }
517 
518 
519 void
520 VideoSettingsView::SetDefaultInput(const dormant_node_info* info)
521 {
522 	SettingsView::SetDefaultInput(info);
523 	_MediaWindow()->UpdateInputListItem(MediaListItem::VIDEO_TYPE, info);
524 	BMediaRoster::Roster()->SetVideoInput(*info);
525 }
526 
527 
528 void
529 VideoSettingsView::SetDefaultOutput(const dormant_node_info* info)
530 {
531 	SettingsView::SetDefaultOutput(info);
532 	_MediaWindow()->UpdateOutputListItem(MediaListItem::VIDEO_TYPE, info);
533 	BMediaRoster::Roster()->SetVideoOutput(*info);
534 }
535