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 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 33 RadioView::~RadioView() 34 { 35 } 36 37 38 void 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 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 70 RadioView::StartPulsing() 71 { 72 fPhase = 0; 73 _RestartPulsing(); 74 } 75 76 77 void 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 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 109 RadioView::DefaultMax() 110 { 111 return 7; 112 } 113 114 115 void 116 RadioView::AttachedToWindow() 117 { 118 if (Parent() != NULL) 119 SetViewColor(Parent()->ViewColor()); 120 else 121 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 122 } 123 124 125 void 126 RadioView::DetachedFromWindow() 127 { 128 StopPulsing(); 129 } 130 131 132 void 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 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 169 RadioView::FrameResized(float /*width*/, float /*height*/) 170 { 171 if (IsPulsing()) 172 _RestartPulsing(); 173 } 174 175 176 void 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 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 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 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 244 RadioView::_IsDisabled(int32 percent, int32 index, int32 count) 245 { 246 return percent < 100 * index / count; 247 } 248