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
14 const uint32 kMsgPulse = 'puls';
15
16 const bigtime_t kMinPulseInterval = 100000;
17 const bigtime_t kMaxPulseInterval = 300000;
18 const float kMinStep = 3.f;
19
20
RadioView(BRect frame,const char * name,int32 resizingMode)21 RadioView::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
~RadioView()33 RadioView::~RadioView()
34 {
35 }
36
37
38 void
SetPercent(int32 percent)39 RadioView::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
54 void
SetMax(int32 max)55 RadioView::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
69 void
StartPulsing()70 RadioView::StartPulsing()
71 {
72 fPhase = 0;
73 _RestartPulsing();
74 }
75
76
77 void
StopPulsing()78 RadioView::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
Draw(BView * view,BRect rect,int32 percent,int32 maxCount)91 RadioView::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
DefaultMax()109 RadioView::DefaultMax()
110 {
111 return 7;
112 }
113
114
115 void
AttachedToWindow()116 RadioView::AttachedToWindow()
117 {
118 if (Parent() != NULL)
119 SetViewColor(Parent()->ViewColor());
120 else
121 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
122 }
123
124
125 void
DetachedFromWindow()126 RadioView::DetachedFromWindow()
127 {
128 StopPulsing();
129 }
130
131
132 void
MessageReceived(BMessage * message)133 RadioView::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
148 void
Draw(BRect updateRect)149 RadioView::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
168 void
FrameResized(float,float)169 RadioView::FrameResized(float /*width*/, float /*height*/)
170 {
171 if (IsPulsing())
172 _RestartPulsing();
173 }
174
175
176 void
_RestartPulsing()177 RadioView::_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
_Compute(const BRect & bounds,BPoint & center,int32 & count,int32 max,float & step)194 RadioView::_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
_DrawBow(BView * view,int32 index,const BPoint & center,int32 count,float step)211 RadioView::_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
_SetColor(BView * view,int32 percent,int32 phase,int32 index,int32 count)227 RadioView::_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
_IsDisabled(int32 percent,int32 index,int32 count)244 RadioView::_IsDisabled(int32 percent, int32 index, int32 count)
245 {
246 return percent < 100 * index / count;
247 }
248