DIRECTORY Buttons, Imager USING [black, Color, Context, MaskIntRectangle, MaskCharacters, SetColor, SetIntCP, white], InputFocus USING [CaptureButtons, ReleaseButtons], Menus USING [MouseButton], MenusPrivate USING [Document], MessageWindow USING [messageWindow], Process USING [Detach, InitializeMonitor, Milliseconds, MsecToTicks, priorityNormal, SetPriority, SetTimeout], Rope USING [ROPE], TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords], VFonts USING [defaultFont, Font, FontHeight, RopeWidth], ViewerClasses, ViewerOps USING [CreateViewer, MouseInViewer, MoveViewer, PaintViewer, RegisterViewerClass], ViewerSpecs; ButtonsImpl: CEDAR MONITOR LOCKS data USING data: ButtonData IMPORTS Imager, InputFocus, MenusPrivate, MessageWindow, Process, TIPUser, VFonts, ViewerOps EXPORTS Buttons SHARES MessageWindow, ViewerOps = BEGIN OPEN ViewerClasses, Buttons; ButtonData: TYPE = REF ButtonDataRec; ButtonDataRec: TYPE = MONITORED RECORD [ proc: ButtonProc, font: VFonts.Font, clientData: REF ANY, documentation: REF ANY, greyCount: INTEGER, displayStyle: DisplayStyle, inverted: BOOL, fork: BOOL, guarded: BOOL, state: GuardState ]; armingTime: Process.Milliseconds _ 100; -- cover removal time. armedTime: Process.Milliseconds _ 5000; -- unguarded interval. GuardState: TYPE = { guarded, arming, armed }; DisplayStyle: TYPE = {blackOnWhite, whiteOnBlack, blackOnGrey} ; bottomOffset: INTEGER = 3; -- for painting leftOffset: INTEGER = 3; defaultButtonY: INTEGER = ViewerSpecs.screenH-ViewerSpecs.messageWindowHeight; defaultButtonH: INTEGER = ViewerSpecs.messageWindowHeight; Create: PUBLIC PROC [info: ViewerRec _ [], proc: ButtonProc, clientData: REF ANY _ NIL, fork: BOOL _ FALSE, font: VFonts.Font _ VFonts.defaultFont, documentation: REF ANY _ NIL, guarded: BOOL _ FALSE, paint: BOOL _ TRUE] RETURNS [button: Button] = BEGIN data: ButtonData _ NEW[ButtonDataRec _ [, proc, font, clientData, documentation, 0, blackOnWhite, FALSE, fork, guarded, IF guarded THEN guarded ELSE armed]]; TRUSTED {Process.InitializeMonitor[@data.LOCK]}; IF info.ww=0 THEN info.ww _ VFonts.RopeWidth[info.name, font]+leftOffset+leftOffset; IF info.wh=0 THEN info.wh _ VFonts.FontHeight[font]+bottomOffset; IF info.parent=NIL AND info.wx=0 AND info.wy=0 THEN BEGIN OPEN MessageWindow.messageWindow; ViewerOps.MoveViewer[MessageWindow.messageWindow, wx, wy, ww-info.ww, wh, FALSE]; info.wx _ wx + ww; info.wy _ defaultButtonY; info.wh _ defaultButtonH; info.column _ static; END; info.data _ data; IF documentation#NIL THEN WITH documentation SELECT FROM doc: REF TEXT => NULL; doc: REF ButtonProc => NULL; doc: Rope.ROPE => NULL; ENDCASE => ERROR; -- not valid documentation RETURN[ViewerOps.CreateViewer[$Button, info, paint]]; END; myGrey: REF CARDINAL = NEW[CARDINAL _ 001010B]; ButtonsPaint: PRIVATE PaintProc = BEGIN OPEN Imager; data: ButtonData _ NARROW[self.data]; IF data = NIL THEN RETURN; IF ISTYPE[whatChanged, PaintRectangle] THEN whatChanged _ NIL; IF whatChanged=NIL THEN BEGIN -- paint in label (derived from name) borderFudge: INTEGER = IF self.border THEN 0 ELSE 1; IF ~clear OR data.greyCount # 0 OR data.displayStyle=blackOnGrey THEN { SetColor[context, myGrey]; MaskIntRectangle[context, [1, 1, self.cw-2, self.ch-2]]} ELSE IF ~clear OR data.displayStyle=whiteOnBlack THEN { SetColor[context, black]; MaskIntRectangle[context, [1, 1, self.cw-2, self.ch-2]]}; SetColor[context, SELECT data.displayStyle FROM whiteOnBlack => white, ENDCASE => black]; SetIntCP[context, [leftOffset+borderFudge, bottomOffset+borderFudge]]; MaskCharacters[context, data.font, self.name]; IF data.guarded AND data.state#armed THEN BEGIN by: INTEGER ~ bottomOffset+borderFudge+2; MaskIntRectangle[context, [0, by, self.cw, 1]]; END; END; IF whatChanged=$Invert OR data.inverted THEN BEGIN -- invert to indicate highlighting SetColor[context, $XOR]; MaskIntRectangle[context, [0, 0, self.cw, self.ch]]; IF whatChanged=$Invert THEN data.inverted _ ~data.inverted; END; END; ButtonsNotify: NotifyProc = BEGIN data: ButtonData _ NARROW[self.data]; EntryButtonsNotify[self, input, data]; END; EntryButtonsNotify: ENTRY PROC [self: Viewer, input: LIST OF REF ANY, data: ButtonData] = BEGIN ENABLE UNWIND => InputFocus.ReleaseButtons[]; button: Menus.MouseButton _ red; shift, control: BOOL _ FALSE; mouse: TIPUser.TIPScreenCoords; IF data = NIL THEN RETURN; FOR list: LIST OF REF ANY _ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM x: ATOM => SELECT x FROM $Blue => button _ blue; $Control => control _ TRUE; $Documentation => IF data.documentation#NIL THEN MenusPrivate.Document[data.documentation, self, data.clientData, button, shift, control]; $Hit => IF data.inverted THEN SELECT data.state FROM guarded => { data.state_arming; ViewerOps.PaintViewer[self, client, FALSE, $Invert]; InputFocus.ReleaseButtons[]; TRUSTED {Process.Detach[FORK ArmButtonProc[data, self]]}; IF data.documentation#NIL THEN -- post documentation MenusPrivate.Document[data.documentation, self, data.clientData, button, shift, control]; }; arming=> NULL; -- no action armed=> { InputFocus.ReleaseButtons[]; ViewerOps.PaintViewer[self, client, FALSE, $Invert]; IF data.guarded THEN BEGIN data.state_guarded; ViewerOps.PaintViewer[self, client]; END; IF data.fork THEN TRUSTED {Process.Detach[FORK ButtonPusher[self, data, data.proc, data.clientData, button, shift, control, TRUE]]} ELSE ButtonPusher[self, data, data.proc, data.clientData, button, shift, control, FALSE]; }; ENDCASE; $Mark => IF ~data.inverted THEN BEGIN InputFocus.CaptureButtons[ButtonsNotify, buttonsClass.tipTable, self]; ViewerOps.PaintViewer[self, client, FALSE, $Invert]; END ELSE BEGIN v: Viewer; c: BOOL; [v, c] _ ViewerOps.MouseInViewer[mouse]; IF v=self AND c THEN RETURN; ViewerOps.PaintViewer[self, client, FALSE, $Invert]; InputFocus.ReleaseButtons[]; END; $Red => button _ red; $Shift => shift _ TRUE; $Yellow => button _ yellow; ENDCASE => NULL; z: TIPUser.TIPScreenCoords => mouse _ z; ENDCASE => ERROR; ENDLOOP; END; ArmButtonProc: ENTRY PROC [data: ButtonData, button: Buttons.Button] = { IF data = NIL THEN RETURN; ButtonWait[data, armingTime]; IF data.state = arming THEN { data.state_armed; ViewerOps.PaintViewer[button, client]; ButtonWait[data, armedTime]; }; IF data.state#guarded THEN BEGIN data.state_guarded; ViewerOps.PaintViewer[button, client]; END; }; ButtonWait: INTERNAL PROCEDURE[data: ButtonData, ticks: Process.Milliseconds] = TRUSTED { buttonWaitCondition: CONDITION; Process.SetTimeout[LONG[@buttonWaitCondition], Process.MsecToTicks[ticks]]; WAIT buttonWaitCondition; }; ButtonPusher: PROC [button: Button, myData: ButtonData, proc: ButtonProc, data: REF ANY, mouseButton: Menus.MouseButton, shift, control, normalPriority: BOOL] = BEGIN IF myData = NIL THEN RETURN; myData.greyCount _ myData.greyCount + 1; IF myData.displayStyle#blackOnGrey THEN ViewerOps.PaintViewer[button, client]; IF normalPriority THEN TRUSTED {Process.SetPriority[Process.priorityNormal]}; proc[button, data, mouseButton, shift, control ! ABORTED => CONTINUE]; myData.greyCount _ MAX[myData.greyCount - 1, 0]; IF myData.displayStyle#blackOnGrey THEN ViewerOps.PaintViewer[button, client]; END; ButtonGet: PRIVATE GetProc = BEGIN RETURN[self.name]; END; ButtonsSet: PRIVATE SetProc = BEGIN IF op=NIL THEN self.name _ NARROW[data] ELSE BEGIN myData: ButtonData _ NARROW[self.data]; IF myData = NIL THEN RETURN; SELECT op FROM $DisplayStyle => SELECT data FROM $BlackOnWhite => { myData.greyCount _ MAX[myData.greyCount - 1, 0]; IF myData.greyCount <= 0 THEN myData.displayStyle _ blackOnWhite}; $WhiteOnBlack => { myData.greyCount _ MAX[myData.greyCount - 1, 0]; IF myData.greyCount <= 0 THEN myData.displayStyle _ whiteOnBlack}; $BlackOnGrey, $BlackOnGray => { myData.greyCount _ myData.greyCount + 1; myData.displayStyle _ blackOnWhite}; ENDCASE => ERROR; $ClientData => myData.clientData _ data; ENDCASE => ERROR; END; IF finalise THEN ViewerOps.PaintViewer[self, all]; END; buttonsClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: ButtonsPaint, get: ButtonGet, set: ButtonsSet, notify: ButtonsNotify, tipTable: TIPUser.InstantiateNewTIPTable["/Indigo/CedarViewers/Viewers/Button.tip"], cursor: bullseye ]]; ViewerOps.RegisterViewerClass[$Button, buttonsClass]; -- plug in to Viewers END. δButtonsImpl.mesa; Written by S. McGregor Edited by McGregor on August 4, 1983 10:49 am Last Edited by: Maxwell, June 7, 1983 2:25 pm borderFudge is so buttons line up whether they have borders or not assert: state=arming Κ Χ– "cedar" style˜JšΟc(™(Jš-™-J™-J˜šΟk ˜ J˜JšœžœV˜bJšœ žœ"˜2Jšœžœ˜Jšœ žœ ˜Jšœžœ˜$šœžœG˜TJ˜—Jšœžœžœ˜Jšœžœ+˜8Jšœžœ,˜8J˜šœ žœ7˜FJ˜—J˜ J˜—Jšœ ž œžœžœ˜Jšœ(˜>J˜Jšœ žœ˜.J˜Jšœžœ.˜@J˜Jšœžœ˜*Jšœ žœ˜J˜Jšœžœ7˜NJšœžœ#˜:J˜š Οnœžœžœ6žœžœžœ˜WJš œžœžœ9žœžœžœ˜YJš œ žœžœ žœžœžœž˜Kšœžœ=˜SJš œžœžœ žœ žœ ˜I—Jšžœ"žœ˜0Jšžœ žœC˜TJšžœ žœ0˜Aš žœ žœžœ žœ ž˜3Jšžœžœ˜'˜IJšžœ˜—J˜J˜J˜J˜Jšžœ˜—J˜š žœžœžœžœžœž˜8Jšœžœžœžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšžœžœ˜0—Jšžœ/˜5Jšžœ˜J˜—Jš œžœžœžœžœ ˜/J˜šŸ œžœ žœžœ˜4Jšœžœ ˜%Jšžœžœžœžœ˜Jšžœžœžœžœ˜>š žœ žœžœžœ%˜CJšB™BJš œ žœžœ žœžœ˜4šžœžœžœžœ˜GJšœ˜Jšœ8˜8—šžœžœžœ žœ˜7Jšœ˜Jšœ9˜9—šœžœž˜/J˜Jšžœ ˜—J˜FJ˜.šžœžœžœž˜/Jšœžœ˜)Jšœ/˜/Jšžœ˜—Jšžœ˜J˜—š žœžœžœžœ"˜UJ˜Jšœ4˜4Jšžœžœ ˜;Jšžœ˜—Jšžœ˜J˜—šœž˜!Jšœžœ ˜%J˜&Jšžœ˜J˜—šŸœžœžœžœžœžœžœ˜YJšž˜Jšžœžœ ˜-J˜ Jšœžœžœ˜J˜Jšžœžœžœžœ˜šžœžœžœžœžœžœžœž˜@šžœ žœž˜šœžœžœž˜J˜Jšœžœ˜šœžœžœž˜0J˜@J˜—šœžœžœžœ ž˜4˜ J˜Jšœ$žœ ˜4J˜Jšžœžœ˜9šžœžœžœ˜4J˜@J˜—J˜—Jšœ žœ ˜˜ J˜Jšœ$žœ ˜4šžœžœž˜J˜J˜$Jšžœ˜—šžœ žœžœžœ˜AJšœ:žœ˜A—šžœ5˜9Jšœžœ˜"—Jšžœ˜——šœ žœžœž˜%J˜FJšœ$žœ ˜4Jšž˜šžœž˜ J˜ Jšœžœ˜J˜(Jšžœžœžœžœ˜Jšœ$žœ ˜4J˜Jšžœ˜——J˜Jšœžœ˜J˜Jšžœžœ˜—J˜(Jšžœžœ˜—Jšžœ˜—Jšžœ˜J˜—šŸ œžœžœ/˜HJš™Jšžœžœžœžœ˜J˜šžœžœ˜J˜J˜&J˜J˜—šžœžœž˜ J˜J˜&Jšžœ˜J˜—šŸ œžœž œ2žœ˜YJšœž œ˜Jšœžœ4˜KJšžœ˜J˜——šŸ œžœ>žœžœ˜XJšœ@žœž˜MJšžœ žœžœžœ˜Jšœ(˜(Jšžœ!žœ'˜NJšžœžœžœ/˜MJšœ1žœžœ˜FJšœžœ˜0Jšžœ!žœ'˜NJšžœ˜J˜—šœ žœ ž˜"Jšžœ ˜Jšžœ˜J˜—šœ žœ ž˜#Jšžœžœžœ žœ˜'šžœž˜ Jšœžœ ˜'Jšžœ žœžœžœ˜šžœž˜šœ˜šžœž˜˜Jšœžœ˜0Jšžœžœ%˜B—˜Jšœžœ˜0Jšžœžœ%˜B—˜J˜(Jšœ$˜$—Jšžœžœ˜——Jšœ(˜(Jšžœžœ˜—Jšžœ˜—Jšžœ žœ"˜2Jšžœ˜J˜—šœ*žœ!˜NJ˜J˜J˜J˜J˜TJ˜J˜J˜—Jšœ6˜KJ˜Jšžœ˜J˜J˜—…— €+;