<> <> <> DIRECTORY Controls, ControlsPrivate, Convert, Draw2d, Imager, ImagerOps, Real, RealFns, ViewerClasses, ViewerOps, ViewerTools; ControlsSliderDialImpl: CEDAR MONITOR IMPORTS ControlsPrivate, Convert, Draw2d, Imager, ImagerOps, Real, RealFns, ViewerOps, ViewerTools EXPORTS Controls, ControlsPrivate ~ BEGIN OPEN Controls; <> SliderDial: TYPE ~ REF SliderDialRec; SliderDialRec: TYPE ~ RECORD [ t: REAL _ 0.0, -- parametric pos'n (0~min, 1~max) tPrev: REAL _ 0.0, -- (private) previous t detents: DetentList _ NIL, -- list of detents detented: BOOL _ FALSE, -- (private) true if control detented detentedPrev: BOOL _ FALSE, -- (private) true if was detented taper: SliderTaper _ lin, -- lin, log, or exponential slider cx, cy, rad: REAL _ 0.0 -- center and radius of dial ]; <> NewSliderDial: PUBLIC PROC [control: Control] ~ { sliderDial: SliderDial _ NEW[SliderDialRec _ [detents: control.detents, taper: control.taper]]; control.sliderDialRef _ sliderDial; FOR d: DetentList _ sliderDial.detents, d.rest WHILE d # NIL DO d.first.t _ (d.first.value-control.min)/(control.max-control.min); ENDLOOP; IF control.max # control.min THEN { sliderDial.t _(control.init-control.min)/(control.max-control.min); IF control.taper = log THEN sliderDial.t _ sliderDial.t*sliderDial.t; IF control.taper = exp THEN sliderDial.t _ Real.SqRt[sliderDial.t]; }; control.value _ control.init; sliderDial.rad _ MIN[control.w, control.h]/2-1; sliderDial.cx _ control.w/2; sliderDial.cy _ sliderDial.cx-1; }; <> NotifySliderDial: PUBLIC PROC [control: Control] ~ { IF ControlsPrivate.ControlProcBusy[control] THEN RETURN; ComputeControlTandValue[control, control.mouse.x, control.mouse.y]; ViewerOps.PaintViewer[control.viewer, client, FALSE, control]; ControlsPrivate.ForkControlProc[control]; }; <> SetSliderDialValue: PUBLIC PROC [control: Control, value: REAL, repaint: BOOL _ TRUE] ~ { sliderDial: SliderDial _ NARROW[control.sliderDialRef]; sliderDial.detentedPrev _ sliderDial.detented; sliderDial.detented _ FALSE; control.valuePrev _ control.value; control.value _ value; sliderDial.tPrev _ sliderDial.t; sliderDial.t _ (control.value-control.min)/(control.max-control.min); SELECT sliderDial.taper FROM log => sliderDial.t _ sliderDial.t*sliderDial.t; exp => sliderDial.t _ Real.SqRt[sliderDial.t]; ENDCASE; IF repaint THEN ViewerOps.PaintViewer[control.viewer, client, FALSE, control]; }; GetSliderDialValue: PUBLIC PROC [control: Control] RETURNS [REAL] ~ { RETURN[control.value]; }; <> PaintSliderDial: PUBLIC ViewerClasses.PaintProc ~ { control: Control _ NARROW[self.data]; sliderDial: SliderDial _ NARROW[control.sliderDialRef]; Action: PROC ~ { IF control.w = 0 THEN control.w _ control.viewer.cw; IF control.h = 0 THEN control.h _ control.viewer.ch; IF whatChanged = NIL AND control.type = dial THEN Draw2d.Circle[context, [sliderDial.cx, sliderDial.cy], sliderDial.rad, FALSE]; UnTick[context, control]; FOR detents: DetentList _ sliderDial.detents, detents.rest WHILE detents # NIL DO Tick[context, control, detents.first.t, FALSE, 1]; ENDLOOP; Tick[context, control, sliderDial.t, sliderDial.detented, 2]; sliderDial.detentedPrev _ sliderDial.detented; IF control.status # NIL THEN { ViewerTools.SetContents[control.status, Convert.FtoRope[control.value, IF control.truncate THEN 0 ELSE 3]]; ViewerOps.PaintViewer[control.status, client]; }; }; IF sliderDial # NIL THEN ImagerOps.DoWithBuffer[context, Action, 0, 0, self.ww, self.wh]; }; UnTick: PUBLIC PROC[context: Context, control: Control] ~ { sliderDial: SliderDial _ NARROW[control.sliderDialRef]; Imager.SetColor[context, Imager.white]; Tick[context, control, sliderDial.tPrev, sliderDial.detentedPrev, 2]; Imager.SetColor[context, Imager.black]; }; Tick: PUBLIC PROC [ context: Context, control: Control, t: REAL, detent: BOOL _ FALSE, width: NAT _ 1] ~ { SELECT control.type FROM vSlider => { y: REAL _ control.h*t; Imager.MaskRectangle[context, [0, y-width/2, control.w, width]]; IF detent THEN Draw2d.Square[context, [control.w/2, y], 3.0]; }; hSlider => { x: REAL _ control.w*t; Imager.MaskRectangle[context, [x-width/2, 0, width, control.h]]; IF detent THEN Draw2d.Square[context, [x+1, control.h/2-1], 3.0]; }; dial => { sliderDial: SliderDial _ NARROW[control.sliderDialRef]; rad: REAL _ sliderDial.rad-3; deg: REAL _ 360.0*t; Imager.SetStrokeWidth[context, width]; Imager.MaskVector[context, [sliderDial.cx+rad*RealFns.CosDeg[deg], sliderDial.cy+rad*RealFns.SinDeg[deg]], [sliderDial.cx, sliderDial.cy]]; IF detent THEN Draw2d.Circle[context, [sliderDial.cx, sliderDial.cy], 3.0, TRUE]; }; ENDCASE => NULL; }; ComputeControlTandValue: PROC [control: Control, x, y: INTEGER] ~ { ComputeControlT[control, x, y]; ComputeControlValue[control]; }; ComputeControlT: PROC [control: Control, x, y: INTEGER] ~ { sliderDial: SliderDial _ NARROW[control.sliderDialRef]; sliderDial.tPrev _ sliderDial.t; SELECT control.type FROM vSlider => sliderDial.t _ REAL[y]/REAL[control.h-3]; hSlider => sliderDial.t _ REAL[x]/REAL[control.w-3]; dial => { sliderDial.t _ RealFns.ArcTan[y-sliderDial.cy, x-sliderDial.cx]/(2.0*3.1415926535); IF y-sliderDial.cy < 0.0 THEN sliderDial.t _ sliderDial.t+1.0; }; ENDCASE => NULL; sliderDial.t _ MIN[1.0, MAX[0.0, sliderDial.t]]; }; ComputeControlValue: PROC [control: Control] ~ { sliderDial: SliderDial _ NARROW[control.sliderDialRef]; control.valuePrev _ control.value; sliderDial.detented _ FALSE; IF sliderDial.detents = NIL THEN { tt: REAL _ SELECT sliderDial.taper FROM log => Real.SqRt[sliderDial.t], exp => sliderDial.t*sliderDial.t, ENDCASE => sliderDial.t; control.value _ (1.0-tt)*control.min+tt*control.max; } ELSE { grain: REAL ~ 0.02; t0, t1, value0, value1: REAL _ 0.0; FOR d: DetentList _ sliderDial.detents, d.rest WHILE d # NIL DO t: REAL _ d.first.t; IF ABS[sliderDial.t-t] < grain THEN { sliderDial.t _ t; control.value _ d.first.value; sliderDial.detented _ TRUE; EXIT; }; IF t > sliderDial.t THEN EXIT; ENDLOOP; IF NOT sliderDial.detented THEN control.value _ (1.0-sliderDial.t)*control.min+sliderDial.t*control.max; }; IF control.truncate THEN control.value _ Real.Round[control.value]; }; END.