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