#include "VolumeControl.h"
#include <string.h>
#include <stdio.h>
#include <Application.h>
#include <Beep.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <Dragger.h>
#include <MediaRoster.h>
#include <MessageRunner.h>
#include <AppMisc.h>
#include "desklink.h"
#include "MixerControl.h"
#include "VolumeWindow.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "VolumeControl"
static const uint32 kMsgReconnectVolume = 'rcms';
VolumeControl::VolumeControl(int32 volumeWhich, bool beep, BMessage* message)
:
BSlider("VolumeControl", B_TRANSLATE("Volume"), message, 0, 1, B_HORIZONTAL),
fMixerControl(new MixerControl(volumeWhich)),
fBeep(beep),
fSnapping(false),
fConnectRetries(0)
{
font_height fontHeight;
GetFontHeight(&fontHeight);
SetBarThickness(ceilf((fontHeight.ascent + fontHeight.descent) * 0.7));
BRect rect(Bounds());
rect.top = rect.bottom - 7;
rect.left = rect.right - 7;
BDragger* dragger = new BDragger(rect, this, B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
AddChild(dragger);
}
VolumeControl::VolumeControl(BMessage* archive)
:
BSlider(archive),
fMixerControl(NULL),
fSnapping(false),
fConnectRetries(0)
{
if (archive->FindBool("beep", &fBeep) != B_OK)
fBeep = false;
int32 volumeWhich;
if (archive->FindInt32("volume which", &volumeWhich) != B_OK)
volumeWhich = VOLUME_USE_MIXER;
fMixerControl = new MixerControl(volumeWhich);
BMessage msg(B_QUIT_REQUESTED);
archive->SendReply(&msg);
}
VolumeControl::~VolumeControl()
{
delete fMixerControl;
}
status_t
VolumeControl::Archive(BMessage* into, bool deep) const
{
status_t status;
status = BView::Archive(into, deep);
if (status < B_OK)
return status;
status = into->AddString("add_on", kAppSignature);
if (status < B_OK)
return status;
status = into->AddBool("beep", fBeep);
if (status != B_OK)
return status;
return into->AddInt32("volume which", fMixerControl->VolumeWhich());
}
VolumeControl*
VolumeControl::Instantiate(BMessage* archive)
{
if (!validate_instantiation(archive, "VolumeControl"))
return NULL;
return new VolumeControl(archive);
}
void
VolumeControl::AttachedToWindow()
{
BSlider::AttachedToWindow();
if (_IsReplicant()) {
SetEventMask(0, 0);
SetDrawingMode(B_OP_ALPHA);
SetFlags(Flags() | B_TRANSPARENT_BACKGROUND);
SetViewColor(B_TRANSPARENT_COLOR);
} else
SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
BMediaRoster* roster = BMediaRoster::Roster();
roster->StartWatching(BMessenger(this), B_MEDIA_SERVER_STARTED);
roster->StartWatching(BMessenger(this), B_MEDIA_SERVER_QUIT);
_ConnectVolume();
if (!fMixerControl->Connected()) {
BMessage reconnect(kMsgReconnectVolume);
BMessageRunner::StartSending(this, &reconnect, 1000000LL, 1);
fConnectRetries = 3;
}
}
void
VolumeControl::DetachedFromWindow()
{
_DisconnectVolume();
BMediaRoster* roster = BMediaRoster::CurrentRoster();
roster->StopWatching(BMessenger(this), B_MEDIA_SERVER_STARTED);
roster->StopWatching(BMessenger(this), B_MEDIA_SERVER_QUIT);
}
void
VolumeControl::MouseDown(BPoint where)
{
int32 viewToken;
if (Bounds().Contains(where) && Looper()->CurrentMessage() != NULL
&& Looper()->CurrentMessage()->FindInt32("_view_token",
&viewToken) == B_OK
&& viewToken != _get_object_token_(this))
return;
#if 0
if (BView* dragger = ChildAt(0)) {
if (!dragger->IsHidden() && dragger->Frame().Contains(where))
return;
}
#endif
if (!IsEnabled() || !Bounds().Contains(where)) {
Invoke();
return;
}
BSlider::MouseDown(where);
}
void
VolumeControl::MouseUp(BPoint where)
{
fSnapping = false;
BSlider::MouseUp(where);
}
void
VolumeControl::MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage)
{
if (!IsTracking()) {
BSlider::MouseMoved(where, transit, dragMessage);
return;
}
float cursorPosition = Orientation() == B_HORIZONTAL ? where.x : where.y;
if (fSnapping && cursorPosition >= fMinSnap && cursorPosition <= fMaxSnap) {
return;
}
fSnapping = false;
int32 oldValue = Value();
int32 newValue = ValueForPoint(where);
if (oldValue == newValue) {
BSlider::MouseMoved(where, transit, dragMessage);
return;
}
if ((oldValue < 0 && newValue >= 0) || (oldValue > 0 && newValue <= 0)) {
SetValue(0);
if (ModificationMessage() != NULL)
Messenger().SendMessage(ModificationMessage());
float snapPoint = _PointForValue(0);
const float kMinSnapOffset = 6;
if (oldValue > newValue) {
fMinSnap = _PointForValue(-4);
if (fabs(snapPoint - fMinSnap) < kMinSnapOffset)
fMinSnap = snapPoint - kMinSnapOffset;
fMaxSnap = _PointForValue(1);
} else {
fMinSnap = _PointForValue(-1);
fMaxSnap = _PointForValue(4);
if (fabs(snapPoint - fMaxSnap) < kMinSnapOffset)
fMaxSnap = snapPoint + kMinSnapOffset;
}
fSnapping = true;
return;
}
BSlider::MouseMoved(where, transit, dragMessage);
}
void
VolumeControl::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case B_MOUSE_WHEEL_CHANGED:
{
if (!fMixerControl->Connected())
return;
float deltaY = 0.0f;
msg->FindFloat("be:wheel_delta_y", &deltaY);
if (deltaY == 0.0f)
return;
int32 currentValue = Value();
int32 newValue = currentValue - int32(deltaY) * 3;
if (newValue != currentValue) {
SetValue(newValue);
InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
}
break;
}
case B_MEDIA_NEW_PARAMETER_VALUE:
if (IsTracking())
break;
SetValue((int32)fMixerControl->Volume());
break;
case B_MEDIA_SERVER_STARTED:
{
BMessage reconnect(kMsgReconnectVolume);
BMessageRunner::StartSending(this, &reconnect, 1000000LL, 1);
fConnectRetries = 3;
break;
}
case B_MEDIA_SERVER_QUIT:
{
SetLabel(B_TRANSLATE("No media server running"));
SetEnabled(false);
break;
}
case B_QUIT_REQUESTED:
Window()->MessageReceived(msg);
break;
case kMsgReconnectVolume:
_ConnectVolume();
if (!fMixerControl->Connected() && --fConnectRetries > 1) {
BMessage reconnect(kMsgReconnectVolume);
BMessageRunner::StartSending(this, &reconnect,
6000000LL / fConnectRetries, 1);
}
break;
case B_WORKSPACE_ACTIVATED:
if (_IsReplicant())
Invalidate();
break;
default:
return BView::MessageReceived(msg);
}
}
status_t
VolumeControl::Invoke(BMessage* message)
{
if (fBeep && fOriginalValue != Value() && message == NULL) {
beep();
fOriginalValue = Value();
}
fMixerControl->SetVolume(Value());
return BSlider::Invoke(message);
}
void
VolumeControl::DrawBar()
{
BRect frame = BarFrame();
BView* view = OffscreenView();
uint32 flags = be_control_look->Flags(this);
rgb_color base = LowColor();
rgb_color rightFillColor = make_color(255, 109, 38, 255);
rgb_color leftFillColor = make_color(116, 224, 0, 255);
int32 min, max;
GetLimits(&min, &max);
float position = (float)min / (min - max);
be_control_look->DrawSliderBar(view, frame, frame, base, leftFillColor,
rightFillColor, position, flags, Orientation());
}
const char*
VolumeControl::UpdateText() const
{
if (!IsEnabled())
return NULL;
fText.SetToFormat(B_TRANSLATE("%" B_PRId32 " dB"), Value());
return fText.String();
}
void
VolumeControl::_DisconnectVolume()
{
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL && fMixerControl->GainNode() != media_node::null) {
roster->StopWatching(this, fMixerControl->GainNode(),
B_MEDIA_NEW_PARAMETER_VALUE);
}
}
void
VolumeControl::_ConnectVolume()
{
_DisconnectVolume();
const char* errorString = NULL;
float volume = 0.0;
fMixerControl->Connect(fMixerControl->VolumeWhich(), &volume, &errorString);
if (errorString != NULL) {
SetLabel(errorString);
SetLimits(-60, 18);
} else {
SetLabel(B_TRANSLATE("Volume"));
SetLimits((int32)floorf(fMixerControl->Minimum()),
(int32)ceilf(fMixerControl->Maximum()));
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL && fMixerControl->GainNode() != media_node::null) {
roster->StartWatching(this, fMixerControl->GainNode(),
B_MEDIA_NEW_PARAMETER_VALUE);
}
}
SetEnabled(errorString == NULL);
fOriginalValue = (int32)volume;
SetValue((int32)volume);
}
float
VolumeControl::_PointForValue(int32 value) const
{
int32 min, max;
GetLimits(&min, &max);
if (Orientation() == B_HORIZONTAL) {
return ceilf(1.0f * (value - min) / (max - min)
* (BarFrame().Width() - 2) + BarFrame().left + 1);
}
return ceilf(BarFrame().top - 1.0f * (value - min) / (max - min)
* BarFrame().Height());
}
bool
VolumeControl::_IsReplicant() const
{
return dynamic_cast<VolumeWindow*>(Window()) == NULL;
}