StopWatchImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Andrew Glassner May 28, 1991 4:34 pm PDT
Buchanan, August 26, 1992 2:04 pm PDT
StopWatchImpl:
CEDAR
PROGRAM
LOCKS wd USING wd: WatchData
IMPORTS Args, Commander, Imager, ImagerBackdoor, ImagerPath, Process, Real, RealFns, ViewerOps
EXPORTS StopWatch
Type Declarations
CommandProc: TYPE ~ Commander.CommandProc;
Rectangle:
TYPE ~ Imager.Rectangle;
-- RECORD [x, y, w, h: REAL]
Trajectory: TYPE ~ Imager.Trajectory;
StopWatch Command
StopWatchCmd: Commander.CommandProc ~ {
hoursA, minutesA, secondsA, frameA, sleepA: Args.Arg;
[hoursA, minutesA, secondsA, frameA, sleepA] ¬ Args.ArgsGet[cmd, "%iiiii"];
viewer ¬ CreateStopWatch[hoursA.int, minutesA.int, secondsA.int, frameA.int, sleepA.int];
};
ChangeRateCmd: Commander.CommandProc ~ {
newSleepA: Args.Arg ¬ Args.ArgInt[cmd];
wd: WatchData ¬ NARROW[viewer.data];
wd.rate ¬ newSleepA.int;
};
StopWatch Proc
CreateStopWatch:
PROC [hours, minutes, seconds:
INT, frames:
INT, sleep:
INT]
RETURNS [v: Viewer] ~ {
-- Show a clock animation. Starting time in minutes is given by 'start'. Show ABS[frames] frames, if 'frames'<0 then the clock goes backwards. Sleep 'sleep' mSec between frames.
wd: WatchData ¬ NIL;
v ¬ GetViewer["StopWatch"];
wd ¬ NARROW[v.data];
wd.rate ¬ sleep;
Process.Detach[FORK ShowStopWatch[v, hours, minutes, seconds, frames] ];
};
ShowStopWatch:
PUBLIC
PROC [v: Viewer, hours, minutes, seconds:
INT, frames:
INT] ~ {
-- Show a clock animation. Starting time in minutes is given by 'start'. Show ABS[frames] frames, if 'frames'<0 then the clock goes backwards. Sleep 'sleep' mSec between frames.
wd: WatchData ¬ NARROW[v.data];
BEGIN
FOR i:
INT
IN [0 ..
ABS[frames])
DO
CheckForPause[wd];
IF wd.done THEN RETURN;
wd.hourHand.angle ¬ halfPi + (-twoPi * ((hours/12.0)+(minutes/720.0)));
wd.minuteHand.angle ¬ halfPi + (-twoPi * ((minutes/60.0)+(seconds/3600.0)));
wd.secondHand.angle ¬ halfPi + (-twoPi * (seconds/60.0));
ViewerOps.PaintViewer[v, client, FALSE, $upDate];
wd.oldPosValid ¬ TRUE;
IF frames>0
THEN seconds ¬ seconds+1
ELSE seconds ¬ seconds-1;
IF seconds < 0
THEN {
seconds ¬ 59;
minutes ¬ minutes-1;
IF minutes < 0
THEN {
minutes ¬ 59;
hours ¬ hours-1;
IF hours < 0 THEN hours ¬ 11;
}
};
IF seconds > 59
THEN {
seconds ¬ 0;
minutes ¬ minutes+1;
IF minutes > 59
THEN {
minutes ¬ 0;
hours ¬ hours+1;
IF hours > 11 THEN hours ¬ 0;
}
};
IF wd.done THEN RETURN;
CheckForPause[wd];
Process.PauseMsec[wd.rate];
Process.CheckForAbort[];
ENDLOOP;
END;
};
CheckForPause:
ENTRY
PROC [wd: WatchData] ~ {
WHILE wd.paused
DO
TRUSTED { Process.SetTimeout[@wd.resumed, Process.MsecToTicks[10000] ]; };
WAIT wd.resumed;
ENDLOOP;
};
ChangeRate:
PUBLIC ENTRY
PROC [wd: WatchData, newRate: Milliseconds ¬
STOPWATCHSLEEP] ~ {
wd.rate ¬ newRate;
};
Pause:
PUBLIC ENTRY
PROC [wd: WatchData] ~ {
IF wd.paused THEN RETURN;
wd.paused ¬ TRUE;
WHILE wd.paused
DO
TRUSTED { Process.SetTimeout[@wd.resumed, Process.MsecToTicks[100000] ]; };
WAIT wd.resumed;
ENDLOOP;
};
Resume:
PUBLIC ENTRY
PROC [wd: WatchData] ~ {
IF NOT wd.paused THEN RETURN;
wd.paused ¬ FALSE;
NOTIFY wd.resumed;
};
End:
PUBLIC ENTRY
PROC [wd: WatchData] ~ {
IF wd.done THEN RETURN;
wd.done ¬ TRUE;
NOTIFY wd.resumed;
};
StopWatchPaint: ViewerClasses.PaintProc ~ {
VectorHead:
PROC [len, angle:
REAL]
RETURNS [
VEC] ~ {
RETURN[[
Real.Floor[center.x + (len * RealFns.Cos[angle])],
Real.Floor[center.y + (len * RealFns.Sin[angle])]]]
};
DrawClockFace:
PROC ~ {
innerRadius: INT ¬ Real.Floor[0.9 * radius];
trajectory: Trajectory ¬ NIL;
Imager.SetColor[context, Imager.white];
Imager.MaskRectangle[context, rectangle]; -- clear the viewer
Imager.SetColor[context, Imager.black];
Imager.SetStrokeWidth[context, 1.0];
FOR i:
INT
IN [0 .. 12)
DO
angle: REAL ¬ halfPi+((twoPi * i)/12.0);
inner: VEC ¬ VectorHead[innerRadius, angle];
outer: VEC ¬ VectorHead[radius, angle];
Imager.MaskVector[context, inner, outer];
IF i=0
THEN trajectory ¬ ImagerPath.MoveTo[outer]
ELSE trajectory ¬ ImagerPath.LineTo[trajectory, outer];
ENDLOOP;
trajectory ¬ ImagerPath.LineTo[trajectory, VectorHead[radius, halfPi]];
Imager.MaskStrokeTrajectory[context, trajectory];
};
PaintHands:
PROC [update:
BOOL] ~ {
PaintHand:
PROC [hand: HandData, update:
BOOL] ~ {
Imager.SetStrokeWidth[context, hand.width];
IF update THEN hand.oldPos ¬ VectorHead[radius*hand.length, hand.angle];
Imager.MaskVector[context, center, hand.oldPos];
};
IF NOT update THEN Imager.SetColor[context, Imager.white];
IF wd.secondHand # NIL THEN PaintHand[wd.secondHand, update];
IF wd.minuteHand # NIL THEN PaintHand[wd.minuteHand, update];
IF wd.hourHand # NIL THEN PaintHand[wd.hourHand, update];
IF NOT update THEN Imager.SetColor[context, Imager.black];
};
wd: WatchData ¬ NARROW[self.data];
rectangle: Rectangle ¬ ImagerBackdoor.GetBounds[context];
delta: VEC ¬ [Real.Floor[rectangle.w/2.0], Real.Floor[rectangle.h/2.0]];
center: VEC ¬ [Real.Floor[rectangle.x+delta.x], Real.Floor[rectangle.y+delta.y]];
radius: INT ¬ Real.Floor[MIN[delta.x, delta.y] - 10];
IF whatChanged=
NIL
OR
NOT wd.oldPosValid
THEN DrawClockFace[] ELSE PaintHands[FALSE];
PaintHands[TRUE];
};
MakeWatchData:
PROC []
RETURNS [wd: WatchData] ~ {
wd ¬ NEW[WatchDataRep ¬ []];
wd.secondHand ¬ NEW[HandDataRep ¬ [width: 1, length: 0.8]];
wd.minuteHand ¬ NEW[HandDataRep ¬ [width: 5, length: 0.6]];
wd.hourHand ¬ NEW[HandDataRep ¬ [width: 9, length: 0.4]];
};
GetViewer:
PROC [vName:
ROPE]
RETURNS [viewer: Viewer] ~ {
viewer ¬ ViewerOps.FindViewer[vName];
IF viewer = NIL THEN viewer ¬ CreateViewer[vName];
ViewerOps.OpenIcon[viewer];
};
CreateViewer:
PUBLIC
PROC [vName:
ROPE]
RETURNS [viewer: Viewer] ~ {
ViewerOps.RegisterViewerClass[
$StopWatchViewer, NEW[ViewerClasses.ViewerClassRec ¬ [paint: StopWatchPaint]]];
viewer ¬ ViewerOps.CreateViewer[
flavor: $StopWatchViewer, info: [name: vName, column: right, data: MakeWatchData[]]];
};
Start Code
Commander.Register["StopWatch", StopWatchCmd, "StopWatch startingTime(hours minutes seconds) numberFrames sleepInMilliSeconds"];
Commander.Register["ChangeRate", ChangeRateCmd, "ChangeRate newSleepInMilliSeconds"];