1/*
2 * Copyright 2010, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "RadioView.h"
8
9#include <stdio.h>
10
11#include <MessageRunner.h>
12
13
14const uint32 kMsgPulse = 'puls';
15
16const bigtime_t kMinPulseInterval = 100000;
17const bigtime_t kMaxPulseInterval = 300000;
18const float kMinStep = 3.f;
19
20
21RadioView::RadioView(BRect frame, const char* name, int32 resizingMode)
22	:
23	BView(frame, name, resizingMode,
24		B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW | B_FRAME_EVENTS),
25	fPercent(0),
26	fPulse(NULL),
27	fPhase(0),
28	fMax(DefaultMax())
29{
30}
31
32
33RadioView::~RadioView()
34{
35}
36
37
38void
39RadioView::SetPercent(int32 percent)
40{
41	if (percent < 0)
42		percent = 0;
43	if (percent > 100)
44		percent = 100;
45
46	if (percent == fPercent)
47		return;
48
49	fPercent = percent;
50	Invalidate();
51}
52
53
54void
55RadioView::SetMax(int32 max)
56{
57	if (max < 0)
58		max = 0;
59	if (max > 100)
60		max = 100;
61	if (max == fMax)
62		return;
63
64	fMax = max;
65	Invalidate();
66}
67
68
69void
70RadioView::StartPulsing()
71{
72	fPhase = 0;
73	_RestartPulsing();
74}
75
76
77void
78RadioView::StopPulsing()
79{
80	if (!IsPulsing())
81		return;
82
83	delete fPulse;
84	fPulse = NULL;
85	fPhase = 0;
86	Invalidate();
87}
88
89
90/*static*/ void
91RadioView::Draw(BView* view, BRect rect, int32 percent, int32 maxCount)
92{
93	view->PushState();
94
95	BPoint center;
96	int32 count;
97	float step;
98	_Compute(rect, center, count, maxCount, step);
99
100	for (int32 i = 0; i < count; i++) {
101		_SetColor(view, percent, 0, i, count);
102		_DrawBow(view, i, center, count, step);
103	}
104	view->PopState();
105}
106
107
108/*static*/ int32
109RadioView::DefaultMax()
110{
111	return 7;
112}
113
114
115void
116RadioView::AttachedToWindow()
117{
118	if (Parent() != NULL)
119		SetViewColor(Parent()->ViewColor());
120	else
121		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
122}
123
124
125void
126RadioView::DetachedFromWindow()
127{
128	StopPulsing();
129}
130
131
132void
133RadioView::MessageReceived(BMessage* message)
134{
135	switch (message->what) {
136		case kMsgPulse:
137			fPhase++;
138			Invalidate();
139			break;
140
141		default:
142			BView::MessageReceived(message);
143			break;
144	}
145}
146
147
148void
149RadioView::Draw(BRect updateRect)
150{
151	SetLowColor(ViewColor());
152
153	BPoint center;
154	int32 count;
155	float step;
156	_Compute(Bounds(), center, count, fMax, step);
157
158	for (int32 i = 0; i < count; i++) {
159		_SetColor(this, fPercent, fPhase, i, count);
160		if (step == kMinStep && _IsDisabled(fPercent, i, count))
161			continue;
162
163		_DrawBow(this, i, center, count, step);
164	}
165}
166
167
168void
169RadioView::FrameResized(float /*width*/, float /*height*/)
170{
171	if (IsPulsing())
172		_RestartPulsing();
173}
174
175
176void
177RadioView::_RestartPulsing()
178{
179	delete fPulse;
180
181	// The pulse speed depends on the size of the view
182	BPoint center;
183	int32 count;
184	float step;
185	_Compute(Bounds(), center, count, fMax, step);
186
187	BMessage message(kMsgPulse);
188	fPulse = new BMessageRunner(this, &message, (bigtime_t)(kMinPulseInterval
189			+ (kMaxPulseInterval - kMinPulseInterval) / step), -1);
190}
191
192
193/*static*/ void
194RadioView::_Compute(const BRect& bounds, BPoint& center, int32& count,
195	int32 max, float& step)
196{
197	center.Set(roundf(bounds.Width() / 2), bounds.bottom);
198	float size = min_c(center.x * 3 / 2, center.y);
199	step = floorf(size / max);
200	if (step < kMinStep) {
201		count = (int32)(size / kMinStep);
202		step = kMinStep;
203	} else
204		count = max;
205
206	center.x += bounds.left;
207}
208
209
210/*static*/ void
211RadioView::_DrawBow(BView* view, int32 index, const BPoint& center,
212	int32 count, float step)
213{
214	float radius = step * index + 1;
215
216	if (step < 4)
217		view->SetPenSize(step / 2);
218	else
219		view->SetPenSize(step * 2 / 3);
220
221	view->SetLineMode(B_ROUND_CAP, B_ROUND_JOIN);
222	view->StrokeArc(center, radius, radius, 50, 80);
223}
224
225
226/*static*/ void
227RadioView::_SetColor(BView* view, int32 percent, int32 phase, int32 index,
228	int32 count)
229{
230	if (_IsDisabled(percent, index, count)) {
231		// disabled
232		view->SetHighColor(tint_color(view->LowColor(), B_DARKEN_1_TINT));
233	} else if (phase == 0 || phase % count != index) {
234		// enabled
235		view->SetHighColor(tint_color(view->LowColor(), B_DARKEN_3_TINT));
236	} else {
237		// pulsing
238		view->SetHighColor(tint_color(view->LowColor(), B_DARKEN_2_TINT));
239	}
240}
241
242
243/*static*/ bool
244RadioView::_IsDisabled(int32 percent, int32 index, int32 count)
245{
246	return percent < 100 * index / count;
247}
248