<> <> <> <<>> <> <> DIRECTORY Atom USING [GetPName], Buttons USING [Create], Containers USING [Container, Create, ChildXBound, ChildYBound], <> Convert USING [RealFromRope], Graphics USING [Context, Mark, Save, white, GetBounds, SetColor, DrawBox, Restore, Translate], InputFocus USING [ReleaseButtons], Menus USING [AppendMenuEntry, ClickProc, CreateEntry, CreateMenu, Menu, MenuProc], PathEditor USING [SetGlobal, SetLocal, SetSimplify, StoreBetas], 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], ViewerTools USING [GetContents, MakeNewTextViewer, SetContents, SetSelection], WindowManager USING [RestoreCursor]; PEViewerImpl: CEDAR MONITOR IMPORTS Atom, Buttons, Containers, Convert, Graphics, InputFocus, Menus, PathEditor, Process, Real, TIPUser, ViewerOps, ViewerTools, 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, biasviewer: ViewerClasses.Viewer, tensionviewer: ViewerClasses.Viewer, deltaviewer: ViewerClasses.Viewer, showbiasviewer: ViewerClasses.Viewer, showtensionviewer: ViewerClasses.Viewer, doitviewer: ViewerClasses.Viewer, showdeltaviewer: 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, biasViewer: ViewerClasses.Viewer, tensionViewer: ViewerClasses.Viewer] = { <> menu: Menus.Menu _ Menus.CreateMenu[lines: 2]; 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" ] ]; Menus.AppendMenuEntry[ -- enter "simplify" button menu: menu, entry: Menus.CreateEntry[ name: "Simplify", proc: CallSimplify, clientData: pathViewerData, documentation: "Reduces number of control points by one" ], line: 1 ]; Menus.AppendMenuEntry[ -- enter "local" button menu: menu, entry: Menus.CreateEntry[ name: "Local", proc: MakeLocal, clientData: pathViewerData, documentation: "Set bias and tension locally" ], line: 1 ]; Menus.AppendMenuEntry[ -- enter "global" button menu: menu, entry: Menus.CreateEntry[ name: "Global", proc: MakeGlobal, clientData: pathViewerData, documentation: "Set bias and tension globally" ], line: 1 ]; 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: entryHeight + entryVSpace, -- position WRT parent ww: pathViewerData.outer.ww, -- CHildXBound below wh: pathViewerData.outer.wh, -- CHildYBound below data: pathViewerData, -- describes the current scene scrollable: TRUE ] ]; pathViewerData.biasviewer _ Buttons.Create[ info: [ parent: pathViewerData.outer, wx: 10, wy: 0, <> ww: 35, wh: entryHeight + entryVSpace, data: pathViewerData, -- describes the current scene scrollable: FALSE, border: FALSE, name: "Bias: " ], proc: ClickBias, clientData: pathViewerData ]; pathViewerData.showbiasviewer _ ViewerTools.MakeNewTextViewer[ info: [ parent: pathViewerData.outer, wx: 45, wy: 0, ww: 55, wh: entryHeight + entryVSpace, scrollable: FALSE, border: FALSE ] ]; pathViewerData.tensionviewer _ Buttons.Create[ info: [ parent: pathViewerData.outer, <> <> wx: 100, wy: 0, ww: 60, wh: entryHeight + entryVSpace, data: pathViewerData, -- describes the current scene scrollable: FALSE, border: FALSE, name: "Tension: " ], proc: ClickTension, clientData: pathViewerData ]; pathViewerData.showtensionviewer _ ViewerTools.MakeNewTextViewer[ info: [ parent: pathViewerData.outer, <> <> wx: 160, wy: 0, ww: 55, wh: entryHeight + entryVSpace, scrollable: FALSE, border: FALSE ] ]; pathViewerData.doitviewer _ Buttons.Create[ info: [ parent: pathViewerData.outer, <> <> wx: 215, wy: 0, ww: 100, -- CHildXBound below wh: entryHeight + entryVSpace, data: pathViewerData, -- describes the current scene scrollable: FALSE, border: FALSE, name: "Doit" ], proc: ClickDoit, clientData: pathViewerData ]; pathViewerData.deltaviewer _ Buttons.Create[ info: [ parent: pathViewerData.outer, wx: 315, wy: 0, ww: 60, wh: entryHeight + entryVSpace, data: pathViewerData, -- describes the current scene scrollable: FALSE, border: FALSE, name: "|| Delta: " ], proc: ClickDelta, clientData: pathViewerData ]; pathViewerData.showdeltaviewer _ ViewerTools.MakeNewTextViewer[ info: [ parent: pathViewerData.outer, wx: 375, wy: 0, ww: 55, wh: entryHeight + entryVSpace, scrollable: FALSE, border: FALSE ] ]; <> Containers.ChildXBound[pathViewerData.outer, pathViewerData.viewer]; Containers.ChildYBound[pathViewerData.outer, pathViewerData.viewer]; Containers.ChildXBound[pathViewerData.outer, pathViewerData.showdeltaviewer]; ViewerTools.SetContents[pathViewerData.showdeltaviewer, "1.0"]; <<>> 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, pathViewerData.showbiasviewer, pathViewerData.showtensionviewer]; }; 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 ]; }; }; ClickBias: Menus.ClickProc = { <> pathViewerData: PathViewerData _ NARROW[clientData]; IF mouseButton = blue THEN ViewerTools.SetContents[pathViewerData.showbiasviewer, ""]; ViewerTools.SetSelection[pathViewerData.showbiasviewer]; }; ClickTension: Menus.ClickProc = { <> pathViewerData: PathViewerData _ NARROW[clientData]; IF mouseButton = blue THEN ViewerTools.SetContents[pathViewerData.showtensionviewer, ""]; ViewerTools.SetSelection[pathViewerData.showtensionviewer]; }; ClickDoit: Menus.ClickProc = { <> bias, tension: REAL; pathViewerData: PathViewerData _ NARROW[clientData]; bias _ Convert.RealFromRope[ViewerTools.GetContents[pathViewerData.showbiasviewer]]; tension _ Convert.RealFromRope[ViewerTools.GetContents[pathViewerData.showtensionviewer]]; < {RETURN;}];>> < {RETURN;}];>> PathEditor.StoreBetas[NARROW[clientData, PathViewerData].clientData, bias, tension]; }; ClickDelta: Menus.ClickProc = { <> pathViewerData: PathViewerData _ NARROW[clientData]; IF mouseButton = blue THEN ViewerTools.SetContents[pathViewerData.showdeltaviewer, ""]; ViewerTools.SetSelection[pathViewerData.showdeltaviewer]; }; 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]; }; }; MakeGlobal: Menus.MenuProc = { <> PathEditor.SetGlobal[NARROW[clientData, PathViewerData].clientData]; }; MakeLocal: Menus.MenuProc = { <> PathEditor.SetLocal[NARROW[clientData, PathViewerData].clientData]; }; CallSimplify: Menus.MenuProc = { <> delta: REAL; pathViewerData: PathViewerData _ NARROW[clientData]; delta _ Convert.RealFromRope[ViewerTools.GetContents[pathViewerData.showdeltaviewer]]; PathEditor.SetSimplify[NARROW[clientData, PathViewerData].clientData, delta]; }; 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.