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