126c008b7SPhil Greenway/*
2323ba9b7SRyan Leavengood * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3cbc6a45aSAxel Dörfler * Distributed under the terms of the MIT License.
4cbc6a45aSAxel Dörfler *
5cbc6a45aSAxel Dörfler * Authors:
600826781SKarsten Heimrich *		Mike Berg <mike@berg-net.us>
753d9356eSStephan Aßmus *		Julun <host.haiku@gmx.de>
853d9356eSStephan Aßmus *		Stephan A��mus <superstippi@gmx.de>
93e58fe9eSStephan Aßmus *		Clemens <mail@Clemens-Zeidler.de>
10323ba9b7SRyan Leavengood *		Hamish Morrison <hamish@lavabit.com>
11cbc6a45aSAxel Dörfler */
12cbc6a45aSAxel Dörfler
13323ba9b7SRyan Leavengood
14cbc6a45aSAxel Dörfler#include "AnalogClock.h"
157f5bbbdcSAxel Dörfler
167f5bbbdcSAxel Dörfler#include <math.h>
177f5bbbdcSAxel Dörfler#include <stdio.h>
1826c008b7SPhil Greenway
19323ba9b7SRyan Leavengood#include <LayoutUtils.h>
2053d9356eSStephan Aßmus#include <Message.h>
213e58fe9eSStephan Aßmus#include <Window.h>
223e58fe9eSStephan Aßmus
237f5bbbdcSAxel Dörfler#include "TimeMessages.h"
247f5bbbdcSAxel Dörfler
253e58fe9eSStephan Aßmus
263e58fe9eSStephan Aßmus#define DRAG_DELTA_PHI 0.2
27a94fb8e2SPhil Greenway
28cbc6a45aSAxel Dörfler
29323ba9b7SRyan LeavengoodTAnalogClock::TAnalogClock(const char* name, bool drawSecondHand,
30323ba9b7SRyan Leavengood	bool interactive)
31323ba9b7SRyan Leavengood	:
32323ba9b7SRyan Leavengood	BView(name, B_WILL_DRAW | B_DRAW_ON_CHILDREN | B_FRAME_EVENTS),
33323ba9b7SRyan Leavengood	fHours(0),
34323ba9b7SRyan Leavengood	fMinutes(0),
35323ba9b7SRyan Leavengood	fSeconds(0),
36323ba9b7SRyan Leavengood	fDirty(true),
37323ba9b7SRyan Leavengood	fCenterX(0.0),
38323ba9b7SRyan Leavengood	fCenterY(0.0),
39323ba9b7SRyan Leavengood	fRadius(0.0),
40323ba9b7SRyan Leavengood	fHourDragging(false),
41323ba9b7SRyan Leavengood	fMinuteDragging(false),
42323ba9b7SRyan Leavengood	fDrawSecondHand(drawSecondHand),
43323ba9b7SRyan Leavengood	fInteractive(interactive),
44323ba9b7SRyan Leavengood	fTimeChangeIsOngoing(false)
45323ba9b7SRyan Leavengood{
46323ba9b7SRyan Leavengood	SetFlags(Flags() | B_SUBPIXEL_PRECISE);
47323ba9b7SRyan Leavengood}
487f5bbbdcSAxel Dörfler
497f5bbbdcSAxel Dörfler
50323ba9b7SRyan LeavengoodTAnalogClock::~TAnalogClock()
51323ba9b7SRyan Leavengood{
52323ba9b7SRyan Leavengood}
537f5bbbdcSAxel Dörfler
547f5bbbdcSAxel Dörfler
55323ba9b7SRyan Leavengoodvoid
56323ba9b7SRyan LeavengoodTAnalogClock::Draw(BRect /*updateRect*/)
57323ba9b7SRyan Leavengood{
58323ba9b7SRyan Leavengood	if (fDirty)
59323ba9b7SRyan Leavengood		DrawClock();
60323ba9b7SRyan Leavengood}
617f5bbbdcSAxel Dörfler
627f5bbbdcSAxel Dörfler
63323ba9b7SRyan Leavengoodvoid
64323ba9b7SRyan LeavengoodTAnalogClock::MessageReceived(BMessage* message)
65323ba9b7SRyan Leavengood{
66323ba9b7SRyan Leavengood	int32 change;
67323ba9b7SRyan Leavengood	switch (message->what) {
68323ba9b7SRyan Leavengood		case B_OBSERVER_NOTICE_CHANGE:
69323ba9b7SRyan Leavengood			message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change);
70323ba9b7SRyan Leavengood			switch (change) {
71323ba9b7SRyan Leavengood				case H_TM_CHANGED:
72323ba9b7SRyan Leavengood				{
73323ba9b7SRyan Leavengood					int32 hour = 0;
74323ba9b7SRyan Leavengood					int32 minute = 0;
75323ba9b7SRyan Leavengood					int32 second = 0;
76323ba9b7SRyan Leavengood					if (message->FindInt32("hour", &hour) == B_OK
77323ba9b7SRyan Leavengood					 && message->FindInt32("minute", &minute) == B_OK
78323ba9b7SRyan Leavengood					 && message->FindInt32("second", &second) == B_OK)
79323ba9b7SRyan Leavengood						SetTime(hour, minute, second);
80323ba9b7SRyan Leavengood					break;
81323ba9b7SRyan Leavengood				}
82323ba9b7SRyan Leavengood				default:
83323ba9b7SRyan Leavengood					BView::MessageReceived(message);
84323ba9b7SRyan Leavengood					break;
85323ba9b7SRyan Leavengood			}
86323ba9b7SRyan Leavengood		break;
87323ba9b7SRyan Leavengood		default:
88323ba9b7SRyan Leavengood			BView::MessageReceived(message);
89323ba9b7SRyan Leavengood			break;
90323ba9b7SRyan Leavengood	}
91323ba9b7SRyan Leavengood}
927f5bbbdcSAxel Dörfler
937f5bbbdcSAxel Dörfler
94323ba9b7SRyan Leavengoodvoid
95323ba9b7SRyan LeavengoodTAnalogClock::MouseDown(BPoint point)
96323ba9b7SRyan Leavengood{
97323ba9b7SRyan Leavengood	if (!fInteractive) {
98323ba9b7SRyan Leavengood		BView::MouseDown(point);
99323ba9b7SRyan Leavengood		return;
100323ba9b7SRyan Leavengood	}
1014e7d39f0SAxel Dörfler
102323ba9b7SRyan Leavengood	if (InMinuteHand(point)) {
103323ba9b7SRyan Leavengood		fMinuteDragging = true;
104323ba9b7SRyan Leavengood		fDirty = true;
105323ba9b7SRyan Leavengood		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
106323ba9b7SRyan Leavengood		Invalidate();
107323ba9b7SRyan Leavengood		return;
108323ba9b7SRyan Leavengood	}
109a94fb8e2SPhil Greenway
110323ba9b7SRyan Leavengood	if (InHourHand(point)) {
111323ba9b7SRyan Leavengood		fHourDragging = true;
112323ba9b7SRyan Leavengood		fDirty = true;
113323ba9b7SRyan Leavengood		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
114323ba9b7SRyan Leavengood		Invalidate();
115323ba9b7SRyan Leavengood		return;
116323ba9b7SRyan Leavengood	}
117323ba9b7SRyan Leavengood}
118ae2555efSPhil Greenway
119323ba9b7SRyan Leavengood
120323ba9b7SRyan Leavengoodvoid
121323ba9b7SRyan LeavengoodTAnalogClock::MouseUp(BPoint point)
122a94fb8e2SPhil Greenway{
123323ba9b7SRyan Leavengood	if (!fInteractive) {
124323ba9b7SRyan Leavengood		BView::MouseUp(point);
125323ba9b7SRyan Leavengood		return;
126323ba9b7SRyan Leavengood	}
1274e7d39f0SAxel Dörfler
128323ba9b7SRyan Leavengood	if (fHourDragging || fMinuteDragging) {
129323ba9b7SRyan Leavengood		int32 hour, minute, second;
130323ba9b7SRyan Leavengood		GetTime(&hour, &minute, &second);
131323ba9b7SRyan Leavengood		BMessage message(H_USER_CHANGE);
132323ba9b7SRyan Leavengood		message.AddBool("time", true);
133323ba9b7SRyan Leavengood		message.AddInt32("hour", hour);
134323ba9b7SRyan Leavengood		message.AddInt32("minute", minute);
135323ba9b7SRyan Leavengood		Window()->PostMessage(&message);
136323ba9b7SRyan Leavengood		fTimeChangeIsOngoing = true;
137323ba9b7SRyan Leavengood	}
138323ba9b7SRyan Leavengood	fHourDragging = false;
139323ba9b7SRyan Leavengood	fDirty = true;
140323ba9b7SRyan Leavengood	fMinuteDragging = false;
141323ba9b7SRyan Leavengood	fDirty = true;
142323ba9b7SRyan Leavengood}
143323ba9b7SRyan Leavengood
144323ba9b7SRyan Leavengood
145323ba9b7SRyan Leavengoodvoid
146323ba9b7SRyan LeavengoodTAnalogClock::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
147323ba9b7SRyan Leavengood{
148323ba9b7SRyan Leavengood	if (!fInteractive) {
149323ba9b7SRyan Leavengood		BView::MouseMoved(point, transit, message);
150323ba9b7SRyan Leavengood		return;
151323ba9b7SRyan Leavengood	}
1523e58fe9eSStephan Aßmus
153323ba9b7SRyan Leavengood	if (fMinuteDragging)
154323ba9b7SRyan Leavengood		SetMinuteHand(point);
155323ba9b7SRyan Leavengood	if (fHourDragging)
156323ba9b7SRyan Leavengood		SetHourHand(point);
157323ba9b7SRyan Leavengood
158323ba9b7SRyan Leavengood	Invalidate();
159323ba9b7SRyan Leavengood}
160323ba9b7SRyan Leavengood
161323ba9b7SRyan Leavengood
162323ba9b7SRyan Leavengoodvoid
1634e7d39f0SAxel DörflerTAnalogClock::DoLayout()
164323ba9b7SRyan Leavengood{
1653e58fe9eSStephan Aßmus	BRect bounds = Bounds();
166323ba9b7SRyan Leavengood
167323ba9b7SRyan Leavengood	// + 0.5 is for the offset to pixel centers
168323ba9b7SRyan Leavengood	// (important when drawing with B_SUBPIXEL_PRECISE)
1693e58fe9eSStephan Aßmus	fCenterX = floorf((bounds.left + bounds.right) / 2 + 0.5) + 0.5;
1703e58fe9eSStephan Aßmus	fCenterY = floorf((bounds.top + bounds.bottom) / 2 + 0.5) + 0.5;
1714e7d39f0SAxel Dörfler	fRadius = floorf((MIN(bounds.Width(), bounds.Height()) / 2.0)) - 5.5;
172a94fb8e2SPhil Greenway}
173a94fb8e2SPhil Greenway
174ae2555efSPhil Greenway
175323ba9b7SRyan LeavengoodBSize
176323ba9b7SRyan LeavengoodTAnalogClock::MaxSize()
177323ba9b7SRyan Leavengood{
178323ba9b7SRyan Leavengood	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
179323ba9b7SRyan Leavengood		BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
180323ba9b7SRyan Leavengood}
181323ba9b7SRyan Leavengood
182323ba9b7SRyan Leavengood
183323ba9b7SRyan LeavengoodBSize
184323ba9b7SRyan LeavengoodTAnalogClock::MinSize()
185323ba9b7SRyan Leavengood{
186c2f3ee3bSAdrien Destugues	return BSize(64.f, 64.f);
187323ba9b7SRyan Leavengood}
188323ba9b7SRyan Leavengood
189323ba9b7SRyan Leavengood
190323ba9b7SRyan LeavengoodBSize
191323ba9b7SRyan LeavengoodTAnalogClock::PreferredSize()
19253d9356eSStephan Aßmus{
193323ba9b7SRyan Leavengood	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
194323ba9b7SRyan Leavengood		BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
195a94fb8e2SPhil Greenway}
196a94fb8e2SPhil Greenway
197a94fb8e2SPhil Greenway
198a94fb8e2SPhil Greenwayvoid
199323ba9b7SRyan LeavengoodTAnalogClock::SetTime(int32 hour, int32 minute, int32 second)
200a94fb8e2SPhil Greenway{
201323ba9b7SRyan Leavengood	// don't set the time if the hands are in a drag action
202323ba9b7SRyan Leavengood	if (fHourDragging || fMinuteDragging || fTimeChangeIsOngoing)
203323ba9b7SRyan Leavengood		return;
2044e7d39f0SAxel Dörfler
20553d9356eSStephan Aßmus	if (fHours == hour && fMinutes == minute && fSeconds == second)
20653d9356eSStephan Aßmus		return;
20753d9356eSStephan Aßmus
20853d9356eSStephan Aßmus	fHours = hour;
20953d9356eSStephan Aßmus	fMinutes = minute;
21053d9356eSStephan Aßmus	fSeconds = second;
21153d9356eSStephan Aßmus
21253d9356eSStephan Aßmus	fDirty = true;
213323ba9b7SRyan Leavengood
214323ba9b7SRyan Leavengood	BWindow* window = Window();
215323ba9b7SRyan Leavengood	if (window && window->Lock()) {
216323ba9b7SRyan Leavengood		Invalidate();
217323ba9b7SRyan Leavengood		Window()->Unlock();
218323ba9b7SRyan Leavengood	}
219323ba9b7SRyan Leavengood}
220323ba9b7SRyan Leavengood
221323ba9b7SRyan Leavengood
222323ba9b7SRyan Leavengoodbool
223323ba9b7SRyan LeavengoodTAnalogClock::IsChangingTime()
224323ba9b7SRyan Leavengood{
225323ba9b7SRyan Leavengood	return fTimeChangeIsOngoing;
226323ba9b7SRyan Leavengood}
227323ba9b7SRyan Leavengood
228323ba9b7SRyan Leavengood
229323ba9b7SRyan Leavengoodvoid
230323ba9b7SRyan LeavengoodTAnalogClock::ChangeTimeFinished()
231323ba9b7SRyan Leavengood{
232323ba9b7SRyan Leavengood	fTimeChangeIsOngoing = false;
233a94fb8e2SPhil Greenway}
234a94fb8e2SPhil Greenway
235ae2555efSPhil Greenway
2363e58fe9eSStephan Aßmusvoid
237323ba9b7SRyan LeavengoodTAnalogClock::GetTime(int32* hour, int32* minute, int32* second)
2383e58fe9eSStephan Aßmus{
2393e58fe9eSStephan Aßmus	*hour = fHours;
2403e58fe9eSStephan Aßmus	*minute = fMinutes;
2413e58fe9eSStephan Aßmus	*second = fSeconds;
2423e58fe9eSStephan Aßmus}
2433e58fe9eSStephan Aßmus
2443e58fe9eSStephan Aßmus
245cbc6a45aSAxel Dörflervoid
246323ba9b7SRyan LeavengoodTAnalogClock::DrawClock()
247cbc6a45aSAxel Dörfler{
24853d9356eSStephan Aßmus	if (!LockLooper())
24953d9356eSStephan Aßmus		return;
25053d9356eSStephan Aßmus
25153d9356eSStephan Aßmus	BRect bounds = Bounds();
25253d9356eSStephan Aßmus	// clear background
253f0650dc9Slooncraz	rgb_color background = ViewColor();
25453d9356eSStephan Aßmus	SetHighColor(background);
25553d9356eSStephan Aßmus	FillRect(bounds);
2564e7d39f0SAxel Dörfler
2573e58fe9eSStephan Aßmus	bounds.Set(fCenterX - fRadius, fCenterY - fRadius,
2587f5bbbdcSAxel Dörfler		fCenterX + fRadius, fCenterY + fRadius);
25953d9356eSStephan Aßmus
26053d9356eSStephan Aßmus	SetPenSize(2.0);
26153d9356eSStephan Aßmus
26253d9356eSStephan Aßmus	SetHighColor(tint_color(background, B_DARKEN_1_TINT));
26353d9356eSStephan Aßmus	StrokeEllipse(bounds.OffsetByCopy(-1, -1));
26453d9356eSStephan Aßmus
26553d9356eSStephan Aßmus	SetHighColor(tint_color(background, B_LIGHTEN_2_TINT));
26653d9356eSStephan Aßmus	StrokeEllipse(bounds.OffsetByCopy(1, 1));
26753d9356eSStephan Aßmus
26853d9356eSStephan Aßmus	SetHighColor(tint_color(background, B_DARKEN_3_TINT));
26953d9356eSStephan Aßmus	StrokeEllipse(bounds);
27053d9356eSStephan Aßmus
27153d9356eSStephan Aßmus	SetLowColor(255, 255, 255);
27253d9356eSStephan Aßmus	FillEllipse(bounds, B_SOLID_LOW);
2737f5bbbdcSAxel Dörfler
27453d9356eSStephan Aßmus	SetHighColor(tint_color(HighColor(), B_DARKEN_2_TINT));
27553d9356eSStephan Aßmus
27653d9356eSStephan Aßmus	// minutes
27753d9356eSStephan Aßmus	SetPenSize(1.0);
27853d9356eSStephan Aßmus	SetLineMode(B_BUTT_CAP, B_MITER_JOIN);
27953d9356eSStephan Aßmus	for (int32 minute = 1; minute < 60; minute++) {
28053d9356eSStephan Aßmus		if (minute % 5 == 0)
28153d9356eSStephan Aßmus			continue;
2827f5bbbdcSAxel Dörfler		float x1 = fCenterX + sinf(minute * M_PI / 30.0) * fRadius;
2837f5bbbdcSAxel Dörfler		float y1 = fCenterY + cosf(minute * M_PI / 30.0) * fRadius;
2847f5bbbdcSAxel Dörfler		float x2 = fCenterX + sinf(minute * M_PI / 30.0) * (fRadius * 0.95);
2857f5bbbdcSAxel Dörfler		float y2 = fCenterY + cosf(minute * M_PI / 30.0) * (fRadius * 0.95);
28653d9356eSStephan Aßmus		StrokeLine(BPoint(x1, y1), BPoint(x2, y2));
28753d9356eSStephan Aßmus	}
288cbc6a45aSAxel Dörfler
28953d9356eSStephan Aßmus	SetHighColor(tint_color(HighColor(), B_DARKEN_1_TINT));
29053d9356eSStephan Aßmus
29153d9356eSStephan Aßmus	// hours
29253d9356eSStephan Aßmus	SetPenSize(2.0);
29353d9356eSStephan Aßmus	SetLineMode(B_ROUND_CAP, B_MITER_JOIN);
29453d9356eSStephan Aßmus	for (int32 hour = 0; hour < 12; hour++) {
2957f5bbbdcSAxel Dörfler		float x1 = fCenterX + sinf(hour * M_PI / 6.0) * fRadius;
2967f5bbbdcSAxel Dörfler		float y1 = fCenterY + cosf(hour * M_PI / 6.0) * fRadius;
2977f5bbbdcSAxel Dörfler		float x2 = fCenterX + sinf(hour * M_PI / 6.0) * (fRadius * 0.9);
2987f5bbbdcSAxel Dörfler		float y2 = fCenterY + cosf(hour * M_PI / 6.0) * (fRadius * 0.9);
29953d9356eSStephan Aßmus		StrokeLine(BPoint(x1, y1), BPoint(x2, y2));
30053d9356eSStephan Aßmus	}
301cbc6a45aSAxel Dörfler
3023e58fe9eSStephan Aßmus	rgb_color knobColor = tint_color(HighColor(), B_DARKEN_2_TINT);;
3033e58fe9eSStephan Aßmus	rgb_color hourColor;
3043e58fe9eSStephan Aßmus	if (fHourDragging)
3053e58fe9eSStephan Aßmus		hourColor = (rgb_color){ 0, 0, 255, 255 };
3063e58fe9eSStephan Aßmus	else
3073e58fe9eSStephan Aßmus	 	hourColor = tint_color(HighColor(), B_DARKEN_2_TINT);
3087f5bbbdcSAxel Dörfler
3093e58fe9eSStephan Aßmus	rgb_color minuteColor;
3103e58fe9eSStephan Aßmus	if (fMinuteDragging)
3113e58fe9eSStephan Aßmus		minuteColor = (rgb_color){ 0, 0, 255, 255 };
3123e58fe9eSStephan Aßmus	else
3133e58fe9eSStephan Aßmus	 	minuteColor = tint_color(HighColor(), B_DARKEN_2_TINT);
3147f5bbbdcSAxel Dörfler
31553d9356eSStephan Aßmus	rgb_color secondsColor = (rgb_color){ 255, 0, 0, 255 };
31653d9356eSStephan Aßmus	rgb_color shadowColor = tint_color(LowColor(),
31753d9356eSStephan Aßmus		(B_DARKEN_1_TINT + B_DARKEN_2_TINT) / 2);
318cbc6a45aSAxel Dörfler
3193e58fe9eSStephan Aßmus	_DrawHands(fCenterX + 1.5, fCenterY + 1.5, fRadius,
3207f5bbbdcSAxel Dörfler		shadowColor, shadowColor, shadowColor, shadowColor);
3213e58fe9eSStephan Aßmus	_DrawHands(fCenterX, fCenterY, fRadius,
3227f5bbbdcSAxel Dörfler		hourColor, minuteColor, secondsColor, knobColor);
323cbc6a45aSAxel Dörfler
32453d9356eSStephan Aßmus	Sync();
325cbc6a45aSAxel Dörfler
32653d9356eSStephan Aßmus	UnlockLooper();
32753d9356eSStephan Aßmus}
328cbc6a45aSAxel Dörfler
329ae2555efSPhil Greenway
3303e58fe9eSStephan Aßmusbool
331323ba9b7SRyan LeavengoodTAnalogClock::InHourHand(BPoint point)
3323e58fe9eSStephan Aßmus{
3333e58fe9eSStephan Aßmus	int32 ticks = fHours;
3343e58fe9eSStephan Aßmus	if (ticks > 12)
3353e58fe9eSStephan Aßmus		ticks -= 12;
3363e58fe9eSStephan Aßmus	ticks *= 5;
3373e58fe9eSStephan Aßmus	ticks += int32(5. * fMinutes / 60.0);
3383e58fe9eSStephan Aßmus	if (ticks > 60)
3393e58fe9eSStephan Aßmus		ticks -= 60;
3403e58fe9eSStephan Aßmus	return _InHand(point, ticks, fRadius * 0.7);
3413e58fe9eSStephan Aßmus}
3423e58fe9eSStephan Aßmus
3433e58fe9eSStephan Aßmus
3443e58fe9eSStephan Aßmusbool
345323ba9b7SRyan LeavengoodTAnalogClock::InMinuteHand(BPoint point)
3463e58fe9eSStephan Aßmus{
3473e58fe9eSStephan Aßmus	return _InHand(point, fMinutes, fRadius * 0.9);
3483e58fe9eSStephan Aßmus}
3493e58fe9eSStephan Aßmus
3503e58fe9eSStephan Aßmus
35153d9356eSStephan Aßmusvoid
352323ba9b7SRyan LeavengoodTAnalogClock::SetHourHand(BPoint point)
35353d9356eSStephan Aßmus{
3543e58fe9eSStephan Aßmus	point.x -= fCenterX;
3553e58fe9eSStephan Aßmus	point.y -= fCenterY;
3563e58fe9eSStephan Aßmus
3573e58fe9eSStephan Aßmus	float pointPhi = _GetPhi(point);
3587f5bbbdcSAxel Dörfler	float hoursExact = 6.0 * pointPhi / M_PI;
3593e58fe9eSStephan Aßmus	if (fHours >= 12)
3603e58fe9eSStephan Aßmus		fHours = 12;
3613e58fe9eSStephan Aßmus	else
3623e58fe9eSStephan Aßmus		fHours = 0;
3633e58fe9eSStephan Aßmus	fHours += int32(hoursExact);
3643e58fe9eSStephan Aßmus
3653e58fe9eSStephan Aßmus	SetTime(fHours, fMinutes, fSeconds);
3663e58fe9eSStephan Aßmus}
3673e58fe9eSStephan Aßmus
3683e58fe9eSStephan Aßmus
3693e58fe9eSStephan Aßmusvoid
370323ba9b7SRyan LeavengoodTAnalogClock::SetMinuteHand(BPoint point)
3713e58fe9eSStephan Aßmus{
3723e58fe9eSStephan Aßmus	point.x -= fCenterX;
3733e58fe9eSStephan Aßmus	point.y -= fCenterY;
3743e58fe9eSStephan Aßmus
3753e58fe9eSStephan Aßmus	float pointPhi = _GetPhi(point);
3767f5bbbdcSAxel Dörfler	float minutesExact = 30.0 * pointPhi / M_PI;
3773e58fe9eSStephan Aßmus	fMinutes = int32(ceilf(minutesExact));
37853d9356eSStephan Aßmus
3793e58fe9eSStephan Aßmus	SetTime(fHours, fMinutes, fSeconds);
3803e58fe9eSStephan Aßmus}
3813e58fe9eSStephan Aßmus
3823e58fe9eSStephan Aßmus
3833e58fe9eSStephan Aßmusfloat
384323ba9b7SRyan LeavengoodTAnalogClock::_GetPhi(BPoint point)
3853e58fe9eSStephan Aßmus{
3863e58fe9eSStephan Aßmus	if (point.x == 0 && point.y < 0)
3877f5bbbdcSAxel Dörfler		return 2 * M_PI;
3883e58fe9eSStephan Aßmus	if (point.x == 0 && point.y > 0)
3897f5bbbdcSAxel Dörfler		return M_PI;
3903e58fe9eSStephan Aßmus	if (point.y == 0 && point.x < 0)
3917f5bbbdcSAxel Dörfler		return M_PI * 3 / 2;
3923e58fe9eSStephan Aßmus	if (point.y == 0 && point.x > 0)
3937f5bbbdcSAxel Dörfler		return M_PI / 2;
3943e58fe9eSStephan Aßmus
3953e58fe9eSStephan Aßmus	float pointPhi = atanf(-1. * point.y / point.x);
3963e58fe9eSStephan Aßmus	if (point.y < 0. && point.x > 0.)	// right upper corner
3977f5bbbdcSAxel Dörfler		pointPhi = M_PI / 2. - pointPhi;
3983e58fe9eSStephan Aßmus	if (point.y > 0. && point.x > 0.)	// right lower corner
3997f5bbbdcSAxel Dörfler		pointPhi = M_PI / 2 - pointPhi;
4003e58fe9eSStephan Aßmus	if (point.y > 0. && point.x < 0.)	// left lower corner
4017f5bbbdcSAxel Dörfler		pointPhi = (M_PI * 3. / 2. - pointPhi);
4023e58fe9eSStephan Aßmus	if (point.y < 0. && point.x < 0.)	// left upper corner
4037f5bbbdcSAxel Dörfler		pointPhi = 3. / 2. * M_PI - pointPhi;
4043e58fe9eSStephan Aßmus	return pointPhi;
4053e58fe9eSStephan Aßmus}
4063e58fe9eSStephan Aßmus
4077f5bbbdcSAxel Dörfler
4083e58fe9eSStephan Aßmusbool
409323ba9b7SRyan LeavengoodTAnalogClock::_InHand(BPoint point, int32 ticks, float radius)
4103e58fe9eSStephan Aßmus{
4113e58fe9eSStephan Aßmus	point.x -= fCenterX;
4123e58fe9eSStephan Aßmus	point.y -= fCenterY;
4137f5bbbdcSAxel Dörfler
4143e58fe9eSStephan Aßmus	float pRadius = sqrt(pow(point.x, 2) + pow(point.y, 2));
4153e58fe9eSStephan Aßmus
4163e58fe9eSStephan Aßmus	if (radius < pRadius)
4173e58fe9eSStephan Aßmus		return false;
4187f5bbbdcSAxel Dörfler
4193e58fe9eSStephan Aßmus	float pointPhi = _GetPhi(point);
4207f5bbbdcSAxel Dörfler	float handPhi = M_PI / 30.0 * ticks;
4213e58fe9eSStephan Aßmus	float delta = pointPhi - handPhi;
422645285b0SFrançois Revol	if (fabs(delta) > DRAG_DELTA_PHI)
4233e58fe9eSStephan Aßmus		return false;
4243e58fe9eSStephan Aßmus
4253e58fe9eSStephan Aßmus	return true;
4263e58fe9eSStephan Aßmus}
4273e58fe9eSStephan Aßmus
4283e58fe9eSStephan Aßmus
4293e58fe9eSStephan Aßmusvoid
430323ba9b7SRyan LeavengoodTAnalogClock::_DrawHands(float x, float y, float radius,
431323ba9b7SRyan Leavengood	rgb_color hourColor, rgb_color minuteColor,
432323ba9b7SRyan Leavengood	rgb_color secondsColor, rgb_color knobColor)
4333e58fe9eSStephan Aßmus{
43453d9356eSStephan Aßmus	float offsetX;
43553d9356eSStephan Aßmus	float offsetY;
43653d9356eSStephan Aßmus
43753d9356eSStephan Aßmus	// calc, draw hour hand
4383e58fe9eSStephan Aßmus	SetHighColor(hourColor);
43953d9356eSStephan Aßmus	SetPenSize(4.0);
44053d9356eSStephan Aßmus	float hours = fHours + float(fMinutes) / 60.0;
4417f5bbbdcSAxel Dörfler	offsetX = (radius * 0.7) * sinf((hours * M_PI) / 6.0);
4427f5bbbdcSAxel Dörfler	offsetY = (radius * 0.7) * cosf((hours * M_PI) / 6.0);
44353d9356eSStephan Aßmus	StrokeLine(BPoint(x, y), BPoint(x + offsetX, y - offsetY));
44453d9356eSStephan Aßmus
44553d9356eSStephan Aßmus	// calc, draw minute hand
4463e58fe9eSStephan Aßmus	SetHighColor(minuteColor);
44753d9356eSStephan Aßmus	SetPenSize(3.0);
44853d9356eSStephan Aßmus	float minutes = fMinutes + float(fSeconds) / 60.0;
4497f5bbbdcSAxel Dörfler	offsetX = (radius * 0.9) * sinf((minutes * M_PI) / 30.0);
4507f5bbbdcSAxel Dörfler	offsetY = (radius * 0.9) * cosf((minutes * M_PI) / 30.0);
45153d9356eSStephan Aßmus	StrokeLine(BPoint(x, y), BPoint(x + offsetX, y - offsetY));
45253d9356eSStephan Aßmus
453e55a3c6aSJonas Sundström	if (fDrawSecondHand) {
454e55a3c6aSJonas Sundström		// calc, draw second hand
455e55a3c6aSJonas Sundström		SetHighColor(secondsColor);
456e55a3c6aSJonas Sundström		SetPenSize(1.0);
457e55a3c6aSJonas Sundström		offsetX = (radius * 0.95) * sinf((fSeconds * M_PI) / 30.0);
458e55a3c6aSJonas Sundström		offsetY = (radius * 0.95) * cosf((fSeconds * M_PI) / 30.0);
459e55a3c6aSJonas Sundström		StrokeLine(BPoint(x, y), BPoint(x + offsetX, y - offsetY));
460e55a3c6aSJonas Sundström	}
4614e7d39f0SAxel Dörfler
46253d9356eSStephan Aßmus	// draw the center knob
4633e58fe9eSStephan Aßmus	SetHighColor(knobColor);
46453d9356eSStephan Aßmus	FillEllipse(BPoint(x, y), radius * 0.06, radius * 0.06);
465cbc6a45aSAxel Dörfler}
466