vfs: check userland buffers before reading them.
[haiku.git] / src / preferences / time / AnalogClock.cpp
blob5b6d34eef906e5d5336cc8c9d1a2524d782171e3
1 /*
2 * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
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"
16 #include <math.h>
17 #include <stdio.h>
19 #include <LayoutUtils.h>
20 #include <Message.h>
21 #include <Window.h>
23 #include "TimeMessages.h"
26 #define DRAG_DELTA_PHI 0.2
29 TAnalogClock::TAnalogClock(const char* name, bool drawSecondHand,
30 bool interactive)
32 BView(name, B_WILL_DRAW | B_DRAW_ON_CHILDREN | B_FRAME_EVENTS),
33 fHours(0),
34 fMinutes(0),
35 fSeconds(0),
36 fDirty(true),
37 fCenterX(0.0),
38 fCenterY(0.0),
39 fRadius(0.0),
40 fHourDragging(false),
41 fMinuteDragging(false),
42 fDrawSecondHand(drawSecondHand),
43 fInteractive(interactive),
44 fTimeChangeIsOngoing(false)
46 SetFlags(Flags() | B_SUBPIXEL_PRECISE);
50 TAnalogClock::~TAnalogClock()
55 void
56 TAnalogClock::Draw(BRect /*updateRect*/)
58 if (fDirty)
59 DrawClock();
63 void
64 TAnalogClock::MessageReceived(BMessage* message)
66 int32 change;
67 switch (message->what) {
68 case B_OBSERVER_NOTICE_CHANGE:
69 message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change);
70 switch (change) {
71 case H_TM_CHANGED:
73 int32 hour = 0;
74 int32 minute = 0;
75 int32 second = 0;
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);
80 break;
82 default:
83 BView::MessageReceived(message);
84 break;
86 break;
87 default:
88 BView::MessageReceived(message);
89 break;
94 void
95 TAnalogClock::MouseDown(BPoint point)
97 if (!fInteractive) {
98 BView::MouseDown(point);
99 return;
102 if (InMinuteHand(point)) {
103 fMinuteDragging = true;
104 fDirty = true;
105 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
106 Invalidate();
107 return;
110 if (InHourHand(point)) {
111 fHourDragging = true;
112 fDirty = true;
113 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
114 Invalidate();
115 return;
120 void
121 TAnalogClock::MouseUp(BPoint point)
123 if (!fInteractive) {
124 BView::MouseUp(point);
125 return;
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;
139 fDirty = true;
140 fMinuteDragging = false;
141 fDirty = true;
145 void
146 TAnalogClock::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
148 if (!fInteractive) {
149 BView::MouseMoved(point, transit, message);
150 return;
153 if (fMinuteDragging)
154 SetMinuteHand(point);
155 if (fHourDragging)
156 SetHourHand(point);
158 Invalidate();
162 void
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;
175 BSize
176 TAnalogClock::MaxSize()
178 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
179 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
183 BSize
184 TAnalogClock::MinSize()
186 return BSize(64.f, 64.f);
190 BSize
191 TAnalogClock::PreferredSize()
193 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
194 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
198 void
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)
203 return;
205 if (fHours == hour && fMinutes == minute && fSeconds == second)
206 return;
208 fHours = hour;
209 fMinutes = minute;
210 fSeconds = second;
212 fDirty = true;
214 BWindow* window = Window();
215 if (window && window->Lock()) {
216 Invalidate();
217 Window()->Unlock();
222 bool
223 TAnalogClock::IsChangingTime()
225 return fTimeChangeIsOngoing;
229 void
230 TAnalogClock::ChangeTimeFinished()
232 fTimeChangeIsOngoing = false;
236 void
237 TAnalogClock::GetTime(int32* hour, int32* minute, int32* second)
239 *hour = fHours;
240 *minute = fMinutes;
241 *second = fSeconds;
245 void
246 TAnalogClock::DrawClock()
248 if (!LockLooper())
249 return;
251 BRect bounds = Bounds();
252 // clear background
253 rgb_color background = ViewColor();
254 SetHighColor(background);
255 FillRect(bounds);
257 bounds.Set(fCenterX - fRadius, fCenterY - fRadius,
258 fCenterX + fRadius, fCenterY + fRadius);
260 SetPenSize(2.0);
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));
276 // minutes
277 SetPenSize(1.0);
278 SetLineMode(B_BUTT_CAP, B_MITER_JOIN);
279 for (int32 minute = 1; minute < 60; minute++) {
280 if (minute % 5 == 0)
281 continue;
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));
291 // hours
292 SetPenSize(2.0);
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);;
303 rgb_color hourColor;
304 if (fHourDragging)
305 hourColor = (rgb_color){ 0, 0, 255, 255 };
306 else
307 hourColor = tint_color(HighColor(), B_DARKEN_2_TINT);
309 rgb_color minuteColor;
310 if (fMinuteDragging)
311 minuteColor = (rgb_color){ 0, 0, 255, 255 };
312 else
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);
324 Sync();
326 UnlockLooper();
330 bool
331 TAnalogClock::InHourHand(BPoint point)
333 int32 ticks = fHours;
334 if (ticks > 12)
335 ticks -= 12;
336 ticks *= 5;
337 ticks += int32(5. * fMinutes / 60.0);
338 if (ticks > 60)
339 ticks -= 60;
340 return _InHand(point, ticks, fRadius * 0.7);
344 bool
345 TAnalogClock::InMinuteHand(BPoint point)
347 return _InHand(point, fMinutes, fRadius * 0.9);
351 void
352 TAnalogClock::SetHourHand(BPoint point)
354 point.x -= fCenterX;
355 point.y -= fCenterY;
357 float pointPhi = _GetPhi(point);
358 float hoursExact = 6.0 * pointPhi / M_PI;
359 if (fHours >= 12)
360 fHours = 12;
361 else
362 fHours = 0;
363 fHours += int32(hoursExact);
365 SetTime(fHours, fMinutes, fSeconds);
369 void
370 TAnalogClock::SetMinuteHand(BPoint point)
372 point.x -= fCenterX;
373 point.y -= fCenterY;
375 float pointPhi = _GetPhi(point);
376 float minutesExact = 30.0 * pointPhi / M_PI;
377 fMinutes = int32(ceilf(minutesExact));
379 SetTime(fHours, fMinutes, fSeconds);
383 float
384 TAnalogClock::_GetPhi(BPoint point)
386 if (point.x == 0 && point.y < 0)
387 return 2 * M_PI;
388 if (point.x == 0 && point.y > 0)
389 return M_PI;
390 if (point.y == 0 && point.x < 0)
391 return M_PI * 3 / 2;
392 if (point.y == 0 && point.x > 0)
393 return M_PI / 2;
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;
404 return pointPhi;
408 bool
409 TAnalogClock::_InHand(BPoint point, int32 ticks, float radius)
411 point.x -= fCenterX;
412 point.y -= fCenterY;
414 float pRadius = sqrt(pow(point.x, 2) + pow(point.y, 2));
416 if (radius < pRadius)
417 return false;
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)
423 return false;
425 return true;
429 void
430 TAnalogClock::_DrawHands(float x, float y, float radius,
431 rgb_color hourColor, rgb_color minuteColor,
432 rgb_color secondsColor, rgb_color knobColor)
434 float offsetX;
435 float offsetY;
437 // calc, draw hour hand
438 SetHighColor(hourColor);
439 SetPenSize(4.0);
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);
447 SetPenSize(3.0);
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);
456 SetPenSize(1.0);
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);