2 * Copyright 2003-2010, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
8 * Axel Dörfler, axeld@pinc-software.de.
12 #include "VolumeControl.h"
17 #include <Application.h>
20 #include <ControlLook.h>
22 #include <MediaRoster.h>
23 #include <MessageRunner.h>
28 #include "MixerControl.h"
29 #include "VolumeWindow.h"
32 #undef B_TRANSLATION_CONTEXT
33 #define B_TRANSLATION_CONTEXT "VolumeControl"
36 static const uint32 kMsgReconnectVolume
= 'rcms';
39 VolumeControl::VolumeControl(int32 volumeWhich
, bool beep
, BMessage
* message
)
41 BSlider("VolumeControl", B_TRANSLATE("Volume"),
42 message
, 0, 1, B_HORIZONTAL
),
43 fMixerControl(new MixerControl(volumeWhich
)),
48 font_height fontHeight
;
49 GetFontHeight(&fontHeight
);
50 SetBarThickness(ceilf((fontHeight
.ascent
+ fontHeight
.descent
) * 0.7));
53 rect
.top
= rect
.bottom
- 7;
54 rect
.left
= rect
.right
- 7;
55 BDragger
* dragger
= new BDragger(rect
, this,
56 B_FOLLOW_RIGHT
| B_FOLLOW_BOTTOM
);
61 VolumeControl::VolumeControl(BMessage
* archive
)
68 if (archive
->FindBool("beep", &fBeep
) != B_OK
)
72 if (archive
->FindInt32("volume which", &volumeWhich
) != B_OK
)
73 volumeWhich
= VOLUME_USE_MIXER
;
75 fMixerControl
= new MixerControl(volumeWhich
);
77 BMessage
msg(B_QUIT_REQUESTED
);
78 archive
->SendReply(&msg
);
82 VolumeControl::~VolumeControl()
89 VolumeControl::Archive(BMessage
* into
, bool deep
) const
93 status
= BView::Archive(into
, deep
);
97 status
= into
->AddString("add_on", kAppSignature
);
101 status
= into
->AddBool("beep", fBeep
);
105 return into
->AddInt32("volume which", fMixerControl
->VolumeWhich());
110 VolumeControl::Instantiate(BMessage
* archive
)
112 if (!validate_instantiation(archive
, "VolumeControl"))
115 return new VolumeControl(archive
);
120 VolumeControl::AttachedToWindow()
122 BSlider::AttachedToWindow();
127 SetEventMask(B_POINTER_EVENTS
, B_NO_POINTER_HISTORY
);
129 BMediaRoster
* roster
= BMediaRoster::Roster();
130 roster
->StartWatching(BMessenger(this), B_MEDIA_SERVER_STARTED
);
131 roster
->StartWatching(BMessenger(this), B_MEDIA_SERVER_QUIT
);
135 if (!fMixerControl
->Connected()) {
136 // Wait a bit, and try again - the media server might not have been
138 BMessage
reconnect(kMsgReconnectVolume
);
139 BMessageRunner::StartSending(this, &reconnect
, 1000000LL, 1);
146 VolumeControl::DetachedFromWindow()
150 BMediaRoster
* roster
= BMediaRoster::CurrentRoster();
151 roster
->StopWatching(BMessenger(this), B_MEDIA_SERVER_STARTED
);
152 roster
->StopWatching(BMessenger(this), B_MEDIA_SERVER_QUIT
);
156 /*! Since we have set a mouse event mask, we don't want to forward all
157 mouse downs to the slider - instead, we only invoke it, which causes a
158 message to our target. Within the VolumeWindow, this will actually
159 cause the window to close.
160 Also, we need to mask out the dragger in this case, or else dragging
161 us will also cause a volume update.
164 VolumeControl::MouseDown(BPoint where
)
166 // Ignore clicks on the dragger
168 if (Bounds().Contains(where
) && Looper()->CurrentMessage() != NULL
169 && Looper()->CurrentMessage()->FindInt32("_view_token",
171 && viewToken
!= _get_object_token_(this))
174 // TODO: investigate why this does not work as expected (the dragger
175 // frame seems to be off)
177 if (BView
* dragger
= ChildAt(0)) {
178 if (!dragger
->IsHidden() && dragger
->Frame().Contains(where
))
183 if (!IsEnabled() || !Bounds().Contains(where
)) {
188 BSlider::MouseDown(where
);
193 VolumeControl::MouseUp(BPoint where
)
196 BSlider::MouseUp(where
);
200 /*! Override the BSlider functionality to be able to grab the knob when
201 it's over 0 dB for some pixels.
204 VolumeControl::MouseMoved(BPoint where
, uint32 transit
,
205 const BMessage
* dragMessage
)
208 BSlider::MouseMoved(where
, transit
, dragMessage
);
212 float cursorPosition
= Orientation() == B_HORIZONTAL
? where
.x
: where
.y
;
214 if (fSnapping
&& cursorPosition
>= fMinSnap
&& cursorPosition
<= fMaxSnap
) {
215 // Don't move the slider, keep the current value for a few
222 int32 oldValue
= Value();
223 int32 newValue
= ValueForPoint(where
);
224 if (oldValue
== newValue
) {
225 BSlider::MouseMoved(where
, transit
, dragMessage
);
229 // Check if there is a 0 dB transition at all
230 if ((oldValue
< 0 && newValue
>= 0) || (oldValue
> 0 && newValue
<= 0)) {
232 if (ModificationMessage() != NULL
)
233 Messenger().SendMessage(ModificationMessage());
235 float snapPoint
= _PointForValue(0);
236 const float kMinSnapOffset
= 6;
238 if (oldValue
> newValue
) {
239 // movement from right to left
240 fMinSnap
= _PointForValue(-4);
241 if (fabs(snapPoint
- fMinSnap
) < kMinSnapOffset
)
242 fMinSnap
= snapPoint
- kMinSnapOffset
;
244 fMaxSnap
= _PointForValue(1);
246 // movement from left to right
247 fMinSnap
= _PointForValue(-1);
248 fMaxSnap
= _PointForValue(4);
249 if (fabs(snapPoint
- fMaxSnap
) < kMinSnapOffset
)
250 fMaxSnap
= snapPoint
+ kMinSnapOffset
;
257 BSlider::MouseMoved(where
, transit
, dragMessage
);
262 VolumeControl::MessageReceived(BMessage
* msg
)
265 case B_MOUSE_WHEEL_CHANGED
:
267 if (!fMixerControl
->Connected())
270 // Even though the volume bar is horizontal, we use the more common
271 // vertical mouse wheel change
274 msg
->FindFloat("be:wheel_delta_y", &deltaY
);
279 int32 currentValue
= Value();
280 int32 newValue
= currentValue
- int32(deltaY
) * 3;
282 if (newValue
!= currentValue
) {
284 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED
);
289 case B_MEDIA_NEW_PARAMETER_VALUE
:
293 SetValue((int32
)fMixerControl
->Volume());
296 case B_MEDIA_SERVER_STARTED
:
298 BMessage
reconnect(kMsgReconnectVolume
);
299 BMessageRunner::StartSending(this, &reconnect
, 1000000LL, 1);
304 case B_MEDIA_SERVER_QUIT
:
306 // No media server around
307 SetLabel(B_TRANSLATE("No media server running"));
312 case B_QUIT_REQUESTED
:
313 Window()->MessageReceived(msg
);
316 case kMsgReconnectVolume
:
318 if (!fMixerControl
->Connected() && --fConnectRetries
> 1) {
319 BMessage
reconnect(kMsgReconnectVolume
);
320 BMessageRunner::StartSending(this, &reconnect
,
321 6000000LL / fConnectRetries
, 1);
326 return BView::MessageReceived(msg
);
332 VolumeControl::Invoke(BMessage
* message
)
334 if (fBeep
&& fOriginalValue
!= Value() && message
== NULL
) {
336 fOriginalValue
= Value();
339 fMixerControl
->SetVolume(Value());
341 return BSlider::Invoke(message
);
346 VolumeControl::DrawBar()
348 BRect frame
= BarFrame();
349 BView
* view
= OffscreenView();
351 if (be_control_look
!= NULL
) {
352 uint32 flags
= be_control_look
->Flags(this);
353 rgb_color base
= LowColor();
354 rgb_color rightFillColor
= (rgb_color
){255, 109, 38, 255};
355 rgb_color leftFillColor
= (rgb_color
){116, 224, 0, 255};
358 GetLimits(&min
, &max
);
359 float position
= (float)min
/ (min
- max
);
361 be_control_look
->DrawSliderBar(view
, frame
, frame
, base
, leftFillColor
,
362 rightFillColor
, position
, flags
, Orientation());
371 VolumeControl::UpdateText() const
376 fText
.SetToFormat(B_TRANSLATE("%" B_PRId32
" dB"), Value());
377 return fText
.String();
382 VolumeControl::_DisconnectVolume()
384 BMediaRoster
* roster
= BMediaRoster::CurrentRoster();
385 if (roster
!= NULL
&& fMixerControl
->GainNode() != media_node::null
) {
386 roster
->StopWatching(this, fMixerControl
->GainNode(),
387 B_MEDIA_NEW_PARAMETER_VALUE
);
393 VolumeControl::_ConnectVolume()
397 const char* errorString
= NULL
;
399 fMixerControl
->Connect(fMixerControl
->VolumeWhich(), &volume
, &errorString
);
401 if (errorString
!= NULL
) {
402 SetLabel(errorString
);
405 SetLabel(B_TRANSLATE("Volume"));
406 SetLimits((int32
)floorf(fMixerControl
->Minimum()),
407 (int32
)ceilf(fMixerControl
->Maximum()));
409 BMediaRoster
* roster
= BMediaRoster::CurrentRoster();
410 if (roster
!= NULL
&& fMixerControl
->GainNode() != media_node::null
) {
411 roster
->StartWatching(this, fMixerControl
->GainNode(),
412 B_MEDIA_NEW_PARAMETER_VALUE
);
416 SetEnabled(errorString
== NULL
);
418 fOriginalValue
= (int32
)volume
;
419 SetValue((int32
)volume
);
424 VolumeControl::_PointForValue(int32 value
) const
427 GetLimits(&min
, &max
);
429 if (Orientation() == B_HORIZONTAL
) {
430 return ceilf(1.0f
* (value
- min
) / (max
- min
)
431 * (BarFrame().Width() - 2) + BarFrame().left
+ 1);
434 return ceilf(BarFrame().top
- 1.0f
* (value
- min
) / (max
- min
)
435 * BarFrame().Height());
440 VolumeControl::_IsReplicant() const
442 return dynamic_cast<VolumeWindow
*>(Window()) == NULL
;