* Copyright 2003-2011, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Sikosis
* Jérôme Duval
*/
#include "MediaViews.h"
#include <AutoDeleter.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <Deskbar.h>
#include <Entry.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <MediaAddOn.h>
#include <MediaRoster.h>
#include <MenuField.h>
#include <PopUpMenu.h>
#include <String.h>
#include <StringView.h>
#include <TextView.h>
#include <assert.h>
#include <stdio.h>
#include "MediaWindow.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Media views"
#define MEDIA_DEFAULT_INPUT_CHANGE 'dich'
#define MEDIA_DEFAULT_OUTPUT_CHANGE 'doch'
#define MEDIA_SHOW_HIDE_VOLUME_CONTROL 'shvc'
SettingsView::SettingsView()
:
BGroupView(B_VERTICAL, B_USE_DEFAULT_SPACING),
fInputMenu(NULL),
fOutputMenu(NULL)
{
fInputMenu = new BPopUpMenu(B_TRANSLATE_ALL("<none>",
"VideoInputMenu", "Used when no video input is available"));
fInputMenu->SetLabelFromMarked(true);
fOutputMenu = new BPopUpMenu(B_TRANSLATE_ALL("<none>",
"VideoOutputMenu", "Used when no video output is available"));
fOutputMenu->SetLabelFromMarked(true);
}
BButton*
SettingsView::MakeRestartButton()
{
return new BButton("restartButton",
B_TRANSLATE("Restart media services"),
new BMessage(ML_RESTART_MEDIA_SERVER));
}
void
SettingsView::AddInputNodes(NodeList& list)
{
_EmptyMenu(fInputMenu);
BMessage message(MEDIA_DEFAULT_INPUT_CHANGE);
_PopulateMenu(fInputMenu, list, message);
}
void
SettingsView::AddOutputNodes(NodeList& list)
{
_EmptyMenu(fOutputMenu);
BMessage message(MEDIA_DEFAULT_OUTPUT_CHANGE);
_PopulateMenu(fOutputMenu, list, message);
}
void
SettingsView::SetDefaultInput(const dormant_node_info* info)
{
_ClearMenuSelection(fInputMenu);
NodeMenuItem* item = _FindNodeItem(fInputMenu, info);
if (item)
item->SetMarked(true);
}
void
SettingsView::SetDefaultOutput(const dormant_node_info* info)
{
_ClearMenuSelection(fOutputMenu);
NodeMenuItem* item = _FindNodeItem(fOutputMenu, info);
if (item)
item->SetMarked(true);
}
void
SettingsView::MessageReceived(BMessage* message)
{
switch (message->what) {
case MEDIA_DEFAULT_INPUT_CHANGE:
{
int32 index;
if (message->FindInt32("index", &index)!=B_OK)
break;
NodeMenuItem* item
= static_cast<NodeMenuItem*>(fInputMenu->ItemAt(index));
SetDefaultInput(item->NodeInfo());
break;
}
case MEDIA_DEFAULT_OUTPUT_CHANGE:
{
int32 index;
if (message->FindInt32("index", &index)!=B_OK)
break;
NodeMenuItem* item
= static_cast<NodeMenuItem*>(fOutputMenu->ItemAt(index));
SetDefaultOutput(item->NodeInfo());
break;
}
default:
BGroupView::MessageReceived(message);
}
}
void
SettingsView::AttachedToWindow()
{
BMessenger thisMessenger(this);
fInputMenu->SetTargetForItems(thisMessenger);
fOutputMenu->SetTargetForItems(thisMessenger);
}
MediaWindow*
SettingsView::_MediaWindow() const
{
return static_cast<MediaWindow*>(Window());
}
void
SettingsView::_EmptyMenu(BMenu* menu)
{
while (menu->CountItems() > 0)
delete menu->RemoveItem((int32)0);
}
void
SettingsView::_PopulateMenu(BMenu* menu, NodeList& nodes,
const BMessage& message)
{
for (int32 i = 0; i < nodes.CountItems(); i++) {
dormant_node_info* info = nodes.ItemAt(i);
menu->AddItem(new NodeMenuItem(info, new BMessage(message)));
}
if (Window() != NULL)
menu->SetTargetForItems(BMessenger(this));
}
NodeMenuItem*
SettingsView::_FindNodeItem(BMenu* menu, const dormant_node_info* nodeInfo)
{
for (int32 i = 0; i < menu->CountItems(); i++) {
NodeMenuItem* item = static_cast<NodeMenuItem*>(menu->ItemAt(i));
const dormant_node_info* itemInfo = item->NodeInfo();
if (itemInfo && itemInfo->addon == nodeInfo->addon
&& itemInfo->flavor_id == nodeInfo->flavor_id) {
return item;
}
}
return NULL;
}
void
SettingsView::_ClearMenuSelection(BMenu* menu)
{
for (int32 i = 0; i < menu->CountItems(); i++) {
BMenuItem* item = menu->ItemAt(i);
item->SetMarked(false);
}
}
NodeMenuItem::NodeMenuItem(const dormant_node_info* info, BMessage* message,
char shortcut, uint32 modifiers)
:
BMenuItem(info->name, message, shortcut, modifiers),
fInfo(info)
{
}
status_t
NodeMenuItem::Invoke(BMessage* message)
{
if (IsMarked())
return B_OK;
return BMenuItem::Invoke(message);
}
ChannelMenuItem::ChannelMenuItem(media_input* input, BMessage* message,
char shortcut, uint32 modifiers)
:
BMenuItem(input->name, message, shortcut, modifiers),
fInput(input)
{
}
ChannelMenuItem::~ChannelMenuItem()
{
delete fInput;
}
int32
ChannelMenuItem::DestinationID()
{
return fInput->destination.id;
}
media_input*
ChannelMenuItem::Input()
{
return fInput;
}
status_t
ChannelMenuItem::Invoke(BMessage* message)
{
if (IsMarked())
return B_OK;
return BMenuItem::Invoke(message);
}
AudioSettingsView::AudioSettingsView()
{
BBox* defaultsBox = new BBox("defaults");
defaultsBox->SetLabel(B_TRANSLATE("Defaults"));
BGridView* defaultsGridView = new BGridView();
BMenuField* inputMenuField = new BMenuField("inputMenuField",
B_TRANSLATE("Audio input:"), InputMenu());
BMenuField* outputMenuField = new BMenuField("outputMenuField",
B_TRANSLATE("Audio output:"), OutputMenu());
BLayoutBuilder::Grid<>(defaultsGridView)
.SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING,
B_USE_DEFAULT_SPACING)
.AddMenuField(inputMenuField, 0, 0, B_ALIGN_HORIZONTAL_UNSET, 1, 3, 1)
.AddMenuField(outputMenuField, 0, 1)
.AddMenuField(_MakeChannelMenu(), 2, 1);
defaultsBox->AddChild(defaultsGridView);
BLayoutBuilder::Group<>(this)
.SetInsets(0, 0, 0, 0)
.Add(defaultsBox)
.AddGroup(B_HORIZONTAL)
.Add(_MakeVolumeCheckBox())
.AddGlue()
.Add(MakeRestartButton())
.End()
.AddGlue();
}
void
AudioSettingsView::SetDefaultChannel(int32 channelID)
{
for (int32 i = 0; i < fChannelMenu->CountItems(); i++) {
ChannelMenuItem* item = _ChannelMenuItemAt(i);
item->SetMarked(item->DestinationID() == channelID);
}
}
void
AudioSettingsView::AttachedToWindow()
{
SettingsView::AttachedToWindow();
BMessenger thisMessenger(this);
fChannelMenu->SetTargetForItems(thisMessenger);
fVolumeCheckBox->SetTarget(thisMessenger);
}
void
AudioSettingsView::MessageReceived(BMessage* message)
{
switch (message->what) {
case ML_DEFAULT_CHANNEL_CHANGED:
{
int32 index;
if (message->FindInt32("index", &index) != B_OK)
break;
ChannelMenuItem* item = _ChannelMenuItemAt(index);
if (item) {
BMediaRoster* roster = BMediaRoster::Roster();
roster->SetAudioOutput(*item->Input());
} else
fprintf(stderr, "ChannelMenuItem not found\n");
}
break;
case MEDIA_SHOW_HIDE_VOLUME_CONTROL:
{
if (fVolumeCheckBox->Value() == B_CONTROL_ON)
_ShowDeskbarVolumeControl();
else
_HideDeskbarVolumeControl();
break;
}
default:
SettingsView::MessageReceived(message);
}
}
void
AudioSettingsView::SetDefaultInput(const dormant_node_info* info)
{
SettingsView::SetDefaultInput(info);
_MediaWindow()->UpdateInputListItem(MediaListItem::AUDIO_TYPE, info);
BMediaRoster::Roster()->SetAudioInput(*info);
}
void
AudioSettingsView::SetDefaultOutput(const dormant_node_info* info)
{
SettingsView::SetDefaultOutput(info);
_MediaWindow()->UpdateOutputListItem(MediaListItem::AUDIO_TYPE, info);
_FillChannelMenu(info);
BMediaRoster::Roster()->SetAudioOutput(*info);
}
BMenuField*
AudioSettingsView::_MakeChannelMenu()
{
fChannelMenu = new BPopUpMenu(B_TRANSLATE("<none>"));
fChannelMenu->SetLabelFromMarked(true);
BMenuField* channelMenuField = new BMenuField("channelMenuField",
B_TRANSLATE("Channel:"), fChannelMenu);
return channelMenuField;
}
BCheckBox*
AudioSettingsView::_MakeVolumeCheckBox()
{
fVolumeCheckBox = new BCheckBox("volumeCheckBox",
B_TRANSLATE("Show volume control on Deskbar"),
new BMessage(MEDIA_SHOW_HIDE_VOLUME_CONTROL));
if (BDeskbar().HasItem("MediaReplicant"))
fVolumeCheckBox->SetValue(B_CONTROL_ON);
return fVolumeCheckBox;
}
void
AudioSettingsView::_FillChannelMenu(const dormant_node_info* nodeInfo)
{
_EmptyMenu(fChannelMenu);
BMediaRoster* roster = BMediaRoster::Roster();
media_node node;
media_node_id node_id;
status_t err = roster->GetInstancesFor(nodeInfo->addon,
nodeInfo->flavor_id, &node_id);
if (err != B_OK) {
err = roster->InstantiateDormantNode(*nodeInfo, &node,
B_FLAVOR_IS_GLOBAL);
} else {
err = roster->GetNodeFor(node_id, &node);
}
if (err == B_OK) {
int32 inputCount = 4;
media_input* inputs = new media_input[inputCount];
BPrivate::ArrayDeleter<media_input> inputDeleter(inputs);
while (true) {
int32 realInputCount = 0;
err = roster->GetAllInputsFor(node, inputs,
inputCount, &realInputCount);
if (realInputCount > inputCount) {
inputCount *= 2;
inputs = new media_input[inputCount];
inputDeleter.SetTo(inputs);
} else {
inputCount = realInputCount;
break;
}
}
if (err == B_OK) {
BMessage message(ML_DEFAULT_CHANNEL_CHANGED);
for (int32 i = 0; i < inputCount; i++) {
media_input* input = new media_input();
*input = inputs[i];
ChannelMenuItem* channelItem = new ChannelMenuItem(input,
new BMessage(message));
fChannelMenu->AddItem(channelItem);
if (channelItem->DestinationID() == 0)
channelItem->SetMarked(true);
}
}
}
if (Window())
fChannelMenu->SetTargetForItems(BMessenger(this));
}
void
AudioSettingsView::_ShowDeskbarVolumeControl()
{
BDeskbar deskbar;
BEntry entry("/bin/desklink", true);
int32 id;
entry_ref ref;
status_t status = entry.GetRef(&ref);
if (status == B_OK)
status = deskbar.AddItem(&ref, &id);
if (status != B_OK) {
fprintf(stderr, B_TRANSLATE(
"Couldn't add volume control in Deskbar: %s\n"),
strerror(status));
}
}
void
AudioSettingsView::_HideDeskbarVolumeControl()
{
BDeskbar deskbar;
status_t status = deskbar.RemoveItem("MediaReplicant");
if (status != B_OK) {
fprintf(stderr, B_TRANSLATE(
"Couldn't remove volume control in Deskbar: %s\n"),
strerror(status));
}
}
ChannelMenuItem*
AudioSettingsView::_ChannelMenuItemAt(int32 index)
{
return static_cast<ChannelMenuItem*>(fChannelMenu->ItemAt(index));
}
VideoSettingsView::VideoSettingsView()
{
BBox* defaultsBox = new BBox("defaults");
defaultsBox->SetLabel(B_TRANSLATE("Defaults"));
BGridView* defaultsGridView = new BGridView();
BMenuField* inputMenuField = new BMenuField("inputMenuField",
B_TRANSLATE("Video input:"), InputMenu());
BMenuField* outputMenuField = new BMenuField("outputMenuField",
B_TRANSLATE("Video output:"), OutputMenu());
BLayoutBuilder::Grid<>(defaultsGridView)
.SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING,
B_USE_DEFAULT_SPACING)
.AddMenuField(inputMenuField, 0, 0)
.AddMenuField(outputMenuField, 0, 1);
defaultsBox->AddChild(defaultsGridView);
BLayoutBuilder::Group<>(this)
.SetInsets(0, 0, 0, 0)
.Add(defaultsBox)
.AddGroup(B_HORIZONTAL)
.AddGlue()
.Add(MakeRestartButton())
.End()
.AddGlue();
}
void
VideoSettingsView::SetDefaultInput(const dormant_node_info* info)
{
SettingsView::SetDefaultInput(info);
_MediaWindow()->UpdateInputListItem(MediaListItem::VIDEO_TYPE, info);
BMediaRoster::Roster()->SetVideoInput(*info);
}
void
VideoSettingsView::SetDefaultOutput(const dormant_node_info* info)
{
SettingsView::SetDefaultOutput(info);
_MediaWindow()->UpdateOutputListItem(MediaListItem::VIDEO_TYPE, info);
BMediaRoster::Roster()->SetVideoOutput(*info);
}