2 * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Mike Berg <mike@berg-net.us>
7 * Julun <host.haiku@gmx.de>
8 * Stephan Aßmus <superstippi@gmx.de>
9 * Clemens <mail@Clemens-Zeidler.de>
10 * Hamish Morrison <hamish@lavabit.com>
14 #include "AnalogClock.h"
19 #include <LayoutUtils.h>
23 #include "TimeMessages.h"
26 #define DRAG_DELTA_PHI 0.2
29 TAnalogClock::TAnalogClock(const char* name
, bool drawSecondHand
,
32 BView(name
, B_WILL_DRAW
| B_DRAW_ON_CHILDREN
| B_FRAME_EVENTS
),
41 fMinuteDragging(false),
42 fDrawSecondHand(drawSecondHand
),
43 fInteractive(interactive
),
44 fTimeChangeIsOngoing(false)
46 SetFlags(Flags() | B_SUBPIXEL_PRECISE
);
50 TAnalogClock::~TAnalogClock()
56 TAnalogClock::Draw(BRect
/*updateRect*/)
64 TAnalogClock::MessageReceived(BMessage
* message
)
67 switch (message
->what
) {
68 case B_OBSERVER_NOTICE_CHANGE
:
69 message
->FindInt32(B_OBSERVE_WHAT_CHANGE
, &change
);
76 if (message
->FindInt32("hour", &hour
) == B_OK
77 && message
->FindInt32("minute", &minute
) == B_OK
78 && message
->FindInt32("second", &second
) == B_OK
)
79 SetTime(hour
, minute
, second
);
83 BView::MessageReceived(message
);
88 BView::MessageReceived(message
);
95 TAnalogClock::MouseDown(BPoint point
)
98 BView::MouseDown(point
);
102 if (InMinuteHand(point
)) {
103 fMinuteDragging
= true;
105 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
);
110 if (InHourHand(point
)) {
111 fHourDragging
= true;
113 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
);
121 TAnalogClock::MouseUp(BPoint point
)
124 BView::MouseUp(point
);
128 if (fHourDragging
|| fMinuteDragging
) {
129 int32 hour
, minute
, second
;
130 GetTime(&hour
, &minute
, &second
);
131 BMessage
message(H_USER_CHANGE
);
132 message
.AddBool("time", true);
133 message
.AddInt32("hour", hour
);
134 message
.AddInt32("minute", minute
);
135 Window()->PostMessage(&message
);
136 fTimeChangeIsOngoing
= true;
138 fHourDragging
= false;
140 fMinuteDragging
= false;
146 TAnalogClock::MouseMoved(BPoint point
, uint32 transit
, const BMessage
* message
)
149 BView::MouseMoved(point
, transit
, message
);
154 SetMinuteHand(point
);
163 TAnalogClock::DoLayout()
165 BRect bounds
= Bounds();
167 // + 0.5 is for the offset to pixel centers
168 // (important when drawing with B_SUBPIXEL_PRECISE)
169 fCenterX
= floorf((bounds
.left
+ bounds
.right
) / 2 + 0.5) + 0.5;
170 fCenterY
= floorf((bounds
.top
+ bounds
.bottom
) / 2 + 0.5) + 0.5;
171 fRadius
= floorf((MIN(bounds
.Width(), bounds
.Height()) / 2.0)) - 5.5;
176 TAnalogClock::MaxSize()
178 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
179 BSize(B_SIZE_UNLIMITED
, B_SIZE_UNLIMITED
));
184 TAnalogClock::MinSize()
186 return BSize(64.f
, 64.f
);
191 TAnalogClock::PreferredSize()
193 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
194 BSize(B_SIZE_UNLIMITED
, B_SIZE_UNLIMITED
));
199 TAnalogClock::SetTime(int32 hour
, int32 minute
, int32 second
)
201 // don't set the time if the hands are in a drag action
202 if (fHourDragging
|| fMinuteDragging
|| fTimeChangeIsOngoing
)
205 if (fHours
== hour
&& fMinutes
== minute
&& fSeconds
== second
)
214 BWindow
* window
= Window();
215 if (window
&& window
->Lock()) {
223 TAnalogClock::IsChangingTime()
225 return fTimeChangeIsOngoing
;
230 TAnalogClock::ChangeTimeFinished()
232 fTimeChangeIsOngoing
= false;
237 TAnalogClock::GetTime(int32
* hour
, int32
* minute
, int32
* second
)
246 TAnalogClock::DrawClock()
251 BRect bounds
= Bounds();
253 rgb_color background
= ui_color(B_PANEL_BACKGROUND_COLOR
);
254 SetHighColor(background
);
257 bounds
.Set(fCenterX
- fRadius
, fCenterY
- fRadius
,
258 fCenterX
+ fRadius
, fCenterY
+ fRadius
);
262 SetHighColor(tint_color(background
, B_DARKEN_1_TINT
));
263 StrokeEllipse(bounds
.OffsetByCopy(-1, -1));
265 SetHighColor(tint_color(background
, B_LIGHTEN_2_TINT
));
266 StrokeEllipse(bounds
.OffsetByCopy(1, 1));
268 SetHighColor(tint_color(background
, B_DARKEN_3_TINT
));
269 StrokeEllipse(bounds
);
271 SetLowColor(255, 255, 255);
272 FillEllipse(bounds
, B_SOLID_LOW
);
274 SetHighColor(tint_color(HighColor(), B_DARKEN_2_TINT
));
278 SetLineMode(B_BUTT_CAP
, B_MITER_JOIN
);
279 for (int32 minute
= 1; minute
< 60; minute
++) {
282 float x1
= fCenterX
+ sinf(minute
* M_PI
/ 30.0) * fRadius
;
283 float y1
= fCenterY
+ cosf(minute
* M_PI
/ 30.0) * fRadius
;
284 float x2
= fCenterX
+ sinf(minute
* M_PI
/ 30.0) * (fRadius
* 0.95);
285 float y2
= fCenterY
+ cosf(minute
* M_PI
/ 30.0) * (fRadius
* 0.95);
286 StrokeLine(BPoint(x1
, y1
), BPoint(x2
, y2
));
289 SetHighColor(tint_color(HighColor(), B_DARKEN_1_TINT
));
293 SetLineMode(B_ROUND_CAP
, B_MITER_JOIN
);
294 for (int32 hour
= 0; hour
< 12; hour
++) {
295 float x1
= fCenterX
+ sinf(hour
* M_PI
/ 6.0) * fRadius
;
296 float y1
= fCenterY
+ cosf(hour
* M_PI
/ 6.0) * fRadius
;
297 float x2
= fCenterX
+ sinf(hour
* M_PI
/ 6.0) * (fRadius
* 0.9);
298 float y2
= fCenterY
+ cosf(hour
* M_PI
/ 6.0) * (fRadius
* 0.9);
299 StrokeLine(BPoint(x1
, y1
), BPoint(x2
, y2
));
302 rgb_color knobColor
= tint_color(HighColor(), B_DARKEN_2_TINT
);;
305 hourColor
= (rgb_color
){ 0, 0, 255, 255 };
307 hourColor
= tint_color(HighColor(), B_DARKEN_2_TINT
);
309 rgb_color minuteColor
;
311 minuteColor
= (rgb_color
){ 0, 0, 255, 255 };
313 minuteColor
= tint_color(HighColor(), B_DARKEN_2_TINT
);
315 rgb_color secondsColor
= (rgb_color
){ 255, 0, 0, 255 };
316 rgb_color shadowColor
= tint_color(LowColor(),
317 (B_DARKEN_1_TINT
+ B_DARKEN_2_TINT
) / 2);
319 _DrawHands(fCenterX
+ 1.5, fCenterY
+ 1.5, fRadius
,
320 shadowColor
, shadowColor
, shadowColor
, shadowColor
);
321 _DrawHands(fCenterX
, fCenterY
, fRadius
,
322 hourColor
, minuteColor
, secondsColor
, knobColor
);
331 TAnalogClock::InHourHand(BPoint point
)
333 int32 ticks
= fHours
;
337 ticks
+= int32(5. * fMinutes
/ 60.0);
340 return _InHand(point
, ticks
, fRadius
* 0.7);
345 TAnalogClock::InMinuteHand(BPoint point
)
347 return _InHand(point
, fMinutes
, fRadius
* 0.9);
352 TAnalogClock::SetHourHand(BPoint point
)
357 float pointPhi
= _GetPhi(point
);
358 float hoursExact
= 6.0 * pointPhi
/ M_PI
;
363 fHours
+= int32(hoursExact
);
365 SetTime(fHours
, fMinutes
, fSeconds
);
370 TAnalogClock::SetMinuteHand(BPoint point
)
375 float pointPhi
= _GetPhi(point
);
376 float minutesExact
= 30.0 * pointPhi
/ M_PI
;
377 fMinutes
= int32(ceilf(minutesExact
));
379 SetTime(fHours
, fMinutes
, fSeconds
);
384 TAnalogClock::_GetPhi(BPoint point
)
386 if (point
.x
== 0 && point
.y
< 0)
388 if (point
.x
== 0 && point
.y
> 0)
390 if (point
.y
== 0 && point
.x
< 0)
392 if (point
.y
== 0 && point
.x
> 0)
395 float pointPhi
= atanf(-1. * point
.y
/ point
.x
);
396 if (point
.y
< 0. && point
.x
> 0.) // right upper corner
397 pointPhi
= M_PI
/ 2. - pointPhi
;
398 if (point
.y
> 0. && point
.x
> 0.) // right lower corner
399 pointPhi
= M_PI
/ 2 - pointPhi
;
400 if (point
.y
> 0. && point
.x
< 0.) // left lower corner
401 pointPhi
= (M_PI
* 3. / 2. - pointPhi
);
402 if (point
.y
< 0. && point
.x
< 0.) // left upper corner
403 pointPhi
= 3. / 2. * M_PI
- pointPhi
;
409 TAnalogClock::_InHand(BPoint point
, int32 ticks
, float radius
)
414 float pRadius
= sqrt(pow(point
.x
, 2) + pow(point
.y
, 2));
416 if (radius
< pRadius
)
419 float pointPhi
= _GetPhi(point
);
420 float handPhi
= M_PI
/ 30.0 * ticks
;
421 float delta
= pointPhi
- handPhi
;
422 if (fabs(delta
) > DRAG_DELTA_PHI
)
430 TAnalogClock::_DrawHands(float x
, float y
, float radius
,
431 rgb_color hourColor
, rgb_color minuteColor
,
432 rgb_color secondsColor
, rgb_color knobColor
)
437 // calc, draw hour hand
438 SetHighColor(hourColor
);
440 float hours
= fHours
+ float(fMinutes
) / 60.0;
441 offsetX
= (radius
* 0.7) * sinf((hours
* M_PI
) / 6.0);
442 offsetY
= (radius
* 0.7) * cosf((hours
* M_PI
) / 6.0);
443 StrokeLine(BPoint(x
, y
), BPoint(x
+ offsetX
, y
- offsetY
));
445 // calc, draw minute hand
446 SetHighColor(minuteColor
);
448 float minutes
= fMinutes
+ float(fSeconds
) / 60.0;
449 offsetX
= (radius
* 0.9) * sinf((minutes
* M_PI
) / 30.0);
450 offsetY
= (radius
* 0.9) * cosf((minutes
* M_PI
) / 30.0);
451 StrokeLine(BPoint(x
, y
), BPoint(x
+ offsetX
, y
- offsetY
));
453 if (fDrawSecondHand
) {
454 // calc, draw second hand
455 SetHighColor(secondsColor
);
457 offsetX
= (radius
* 0.95) * sinf((fSeconds
* M_PI
) / 30.0);
458 offsetY
= (radius
* 0.95) * cosf((fSeconds
* M_PI
) / 30.0);
459 StrokeLine(BPoint(x
, y
), BPoint(x
+ offsetX
, y
- offsetY
));
462 // draw the center knob
463 SetHighColor(knobColor
);
464 FillEllipse(BPoint(x
, y
), radius
* 0.06, radius
* 0.06);