<> <> <<>> <> DIRECTORY Atom USING [GetPName], Containers USING [Container, Create, ChildXBound, ChildYBound], Graphics USING [Context, Mark, Save, white, GetBounds, SetColor, DrawBox, Restore, Translate], InputFocus USING [ReleaseButtons], Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc], PEViewer, Process USING [Detach], Real USING [FixI], Rope USING [ROPE], TIPUser USING [InstantiateNewTIPTable,TIPScreenCoords], ViewerClasses USING [Viewer, ViewerClass, ViewerClassRec, NotifyProc, PaintProc, DestroyProc, ScrollProc], ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass], WindowManager USING [RestoreCursor]; PEViewerImpl: CEDAR MONITOR IMPORTS Atom, Containers, Graphics, InputFocus, Menus, Process, Real, TIPUser, ViewerOps, WindowManager EXPORTS PEViewer = BEGIN OPEN PEViewer; entryHeight: CARDINAL = 12; -- height of a line of items in a menu entryVSpace: CARDINAL = 3; -- vertical leading between lines entryHSpace: CARDINAL = 8; -- horizontal space between items on a line PathViewerData: TYPE = REF PathViewerDataRec; PathViewerDataRec: TYPE = RECORD [ outer: Containers.Container _ NIL, viewer: ViewerClasses.Viewer, xTranslation, yTranslation: REAL _ 0., xLeft, xRight, yBottom, yTop: REAL _ 0., buttonPasserState: ButtonPasserState _ alive, inputEvent: ATOM, newEventArrival: CONDITION, gotANewHit: BOOLEAN _ FALSE, controlPointX, controlPointY: REAL _ 0., redrawProc: RedrawProc, -- name of procedure for redrawing on window resizing, etc. buttonProc: ButtonProc, -- name of procedure for acting on button pushes quitProc: QuitProc, -- name of procedure for cleaning up on exit clientData: REF ANY ]; ButtonPasserState: TYPE = {alive, dying, dead}; MenuData: TYPE = REF MenuDataRec; MenuDataRec: TYPE = RECORD [ menuButton: ATOM, pathViewerData: PathViewerData ]; BuildViewer: PUBLIC PROCEDURE [name: Rope.ROPE, menuLabels: LIST OF MenuLabelRec, clientData: REF ANY, redrawProc: RedrawProc, quitProc: QuitProc, buttonProc: ButtonProc] RETURNS [pathViewer: ViewerClasses.Viewer] = { <> menu: Menus.Menu _ Menus.CreateMenu[]; pathViewerData: PathViewerData _ NEW[PathViewerDataRec _ [redrawProc: redrawProc, buttonProc: buttonProc, quitProc: quitProc, clientData: clientData]]; Menus.AppendMenuEntry[ -- enter "erase" button menu: menu, entry: Menus.CreateEntry[ name: "Erase", proc: Erase, clientData: pathViewerData, documentation: "Erase the viewer" ] ]; Menus.AppendMenuEntry[ -- enter "<" button menu: menu, entry: Menus.CreateEntry[ name: "<", proc: RollLeft, clientData: pathViewerData, documentation: "Roll image to left" ] ]; Menus.AppendMenuEntry[ -- enter ">" button menu: menu, entry: Menus.CreateEntry[ name: ">", proc: RollRight, clientData: pathViewerData, documentation: "Roll image to right" ] ]; FOR menuLabels _ menuLabels, menuLabels.rest UNTIL menuLabels = NIL DO Menus.AppendMenuEntry[ -- enter menu buttons menu: menu, entry: Menus.CreateEntry[ name: Atom.GetPName[menuLabels.first.label], proc: MenuHit, clientData: NEW[MenuDataRec _ [ menuButton: menuLabels.first.label, pathViewerData: pathViewerData ] ], documentation: "", guarded: menuLabels.first.guarded ] ]; ENDLOOP; pathViewerData.outer _ Containers.Create[ [ name: name, menu: menu, iconic: TRUE, column: left, scrollable: FALSE ] ]; pathViewerData.viewer _ ViewerOps.CreateViewer[ flavor: $PEViewer, info: [ parent: pathViewerData.outer, wx: 0, wy: 0, -- position WRT parent ww: pathViewerData.outer.ww, -- CHildXBound below wh: pathViewerData.outer.wh, -- CHildXBound below data: pathViewerData, -- describes the current scene scrollable: TRUE ] ]; <> Containers.ChildXBound[pathViewerData.outer, pathViewerData.viewer]; Containers.ChildYBound[pathViewerData.outer, pathViewerData.viewer]; ViewerOps.PaintViewer[pathViewerData.outer, all]; pathViewerData.xTranslation _ pathViewerData.yTranslation _ 0.; pathViewerData.xLeft _ pathViewerData.yBottom _ 0.; pathViewerData.xRight _ pathViewerData.viewer.ww; pathViewerData.yTop _ pathViewerData.viewer.wh; TRUSTED {Process.Detach[FORK ButtonPasser[pathViewerData]];}; RETURN [pathViewerData.viewer]; }; DrawInViewer: PUBLIC PROCEDURE [pathViewer: ViewerClasses.Viewer, drawProc: DrawProc] = { <> doDrawProc: REF DrawProc _ NIL; IF pathViewer # NIL THEN { TRUSTED {doDrawProc _ NEW[DrawProc _ drawProc];}; ViewerOps.PaintViewer[ viewer: pathViewer, hint: client, whatChanged: doDrawProc, clearClient: FALSE ]; }; }; PaintProc: ViewerClasses.PaintProc = { <> pathViewerData: PathViewerData _ NARROW[self.data]; x,y: REAL; x _ pathViewerData.xTranslation; y _ pathViewerData.yTranslation; IF whatChanged = NIL THEN { Graphics.Translate[context, x, y]; pathViewerData.redrawProc[pathViewerData.clientData]; } ELSE { Graphics.Translate[context, x, y]; NARROW[whatChanged, REF DrawProc]^[context]; }; }; Erase: Menus.MenuProc = { <> DoErase: PROC [context: Graphics.Context] = { mark: Graphics.Mark _ Graphics.Save[context]; Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, Graphics.GetBounds[context]]; Graphics.Restore[context,mark]; }; pathViewerData: PathViewerData _ NARROW[clientData]; DrawInViewer[pathViewerData.viewer, DoErase]; }; RollLeft: Menus.MenuProc = { <> pathViewerData: PathViewerData _ NARROW[clientData]; pathViewerData.xTranslation _ pathViewerData.xTranslation - 64; pathViewerData.redrawProc[clientData: pathViewerData.clientData]; }; RollRight: Menus.MenuProc = { <> pathViewerData: PathViewerData _ NARROW[clientData]; pathViewerData.xTranslation _ pathViewerData.xTranslation + 64; pathViewerData.redrawProc[clientData: pathViewerData.clientData]; }; MenuHit: Menus.MenuProc = { <> menuData: MenuData _ NARROW[clientData]; ButtonMonitor[menuData.pathViewerData, menuData.menuButton]; }; ButtonMonitor: ENTRY PROCEDURE [pathViewerData: PathViewerData, event: ATOM] = { <> pathViewerData.inputEvent _ event; pathViewerData.gotANewHit _ TRUE; NOTIFY pathViewerData.newEventArrival; }; ButtonPasser: PROCEDURE [pathViewerData: PathViewerData] = { <> WHILE pathViewerData.buttonPasserState = alive DO IF GotAButtonHit[pathViewerData] THEN pathViewerData.buttonProc[pathViewerData.clientData, pathViewerData.inputEvent, pathViewerData.controlPointX, pathViewerData.controlPointY]; ENDLOOP; pathViewerData.buttonPasserState _ dead; }; GotAButtonHit: ENTRY PROCEDURE [pathViewerData: PathViewerData] RETURNS [gotAHit: BOOLEAN] = { <> IF ~pathViewerData.gotANewHit THEN WAIT pathViewerData.newEventArrival; gotAHit _ pathViewerData.gotANewHit; pathViewerData.gotANewHit _ FALSE; }; NotifyProc: ViewerClasses.NotifyProc = { <> ENABLE UNWIND => { InputFocus.ReleaseButtons[]; WindowManager.RestoreCursor[]; }; pathViewerData: PathViewerData _ NARROW[self.data]; FOR list: LIST OF REF ANY _ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM x: ATOM => ButtonMonitor[pathViewerData, x]; z: TIPUser.TIPScreenCoords => { OPEN pathViewerData; controlPointX _ z.mouseX - xTranslation; controlPointY _ z.mouseY - yTranslation; <> IF controlPointX > xRight THEN xRight _ controlPointX ELSE IF controlPointX < xLeft THEN xLeft _ controlPointX; IF controlPointY > yTop THEN yTop _ controlPointY ELSE IF controlPointY < yBottom THEN yBottom _ controlPointY; }; ENDCASE => ERROR; ENDLOOP; }; DestroyProc: ViewerClasses.DestroyProc = { <> pathViewerData: PathViewerData _ NARROW[self.data]; DestroyButtonPasser[pathViewerData]; IF pathViewerData.quitProc # NIL THEN pathViewerData.quitProc[pathViewerData.clientData]; pathViewerData.viewer _ NIL; pathViewerData.outer _ NIL; }; DestroyButtonPasser: ENTRY PROCEDURE [pathViewerData: PathViewerData] = { <> pathViewerData.buttonPasserState _ dying; NOTIFY pathViewerData.newEventArrival; }; ScrollProc: ViewerClasses.ScrollProc = { <> pathViewerData: PathViewerData _ NARROW[self.data]; SELECT op FROM up => { pathViewerData.yTranslation _ pathViewerData.yTranslation + amount; pathViewerData.redrawProc[clientData: pathViewerData.clientData]; }; down => { pathViewerData.yTranslation _ pathViewerData.yTranslation - amount; pathViewerData.redrawProc[clientData: pathViewerData.clientData]; }; thumb => { pathViewerData.yTranslation _ - (pathViewerData.yBottom + (1. - amount/100.) * (pathViewerData.yTop - pathViewerData.yBottom)); pathViewerData.redrawProc[clientData: pathViewerData.clientData]; }; query => { OPEN pathViewerData; RETURN [Real.FixI[100. - (-yTranslation + self.ch - yBottom) * 100. / (yTop - yBottom)], Real.FixI[100. - (-yTranslation - yBottom) * 100. / (yTop - yBottom)]]; }; ENDCASE; }; Init: PROCEDURE [] = { <> pathViewerClass: ViewerClasses.ViewerClass; pathViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: PaintProc, notify: NotifyProc, destroy: DestroyProc, scroll: ScrollProc, tipTable: TIPUser.InstantiateNewTIPTable["PEViewer.TIP"], icon: document ] ]; ViewerOps.RegisterViewerClass[$PEViewer, pathViewerClass]; }; Init[]; END.