-- DoradoBoardImpl.mesa
-- last modified by C. Thacker November 8, 1982 2:00 pm

DIRECTORY
 Buttons USING [Button, ButtonProc, Create, SetDisplayStyle],
 ColorWorld USING[HasMode],
 Containers USING [ChildXBound, ChildYBound, Container, Create],
 DoradoBoard,
 Graphics USING [Context, NewContext, Map, Translate],
 InputFocus USING [SetInputFocus],
IO USING [STREAM, Close],
 MessageWindow USING [Append, Blink],
 Real USING [RoundI],
 Rope USING [ROPE, Length],
 Rules USING [Create],
 TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords, TIPTable],
 UserExec USING [CommandProc, RegisterCommand],
 VFonts USING [CharWidth],
 ViewerClasses USING [NotifyProc, PaintProc, DestroyProc,
  Viewer, ViewerClass, ViewerClassRec],
 ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass,
  OpenIcon],
 ViewerTools USING [MakeNewTextViewer, GetContents, SetContents, SetSelection],
 WindowManager USING [StartColorViewers, colorDisplayOn];

DoradoBoardImpl: CEDAR MONITOR

IMPORTS Buttons, ColorWorld, Containers, DoradoBoard, Graphics,
 InputFocus, IO, MessageWindow, Real,
 Rope, Rules, TIPUser, UserExec, VFonts, ViewerOps, ViewerTools, WindowManager
EXPORTS DoradoBoard =

TRUSTED BEGIN
OPEN DoradoBoard;

------ All exported values ------

boardHeight: PUBLIC Mils ← 13200;
boardWidth: PUBLIC Mils ← 13060;
asiOffset: PUBLIC Point ← [x: -3240, y: -100];
-- translate ASI coordinates to position on board
board: PUBLIC Board ← NIL;

------ global module values ------

entryHeight: CARDINAL = 15; -- how tall to make each line of items
entryVSpace: CARDINAL = 8; -- vertical leading space between lines
entryHSpace: CARDINAL = 4; -- horizontal space between items in a line
SP: CHAR = ' ;

---- ---- ---- ---- ---- ---- ---- ----

---- ---- ---- ---- ---- ---- ---- ----

DisplayBoard: UserExec.CommandProc =
TRUSTED BEGIN

NewButtonLine: PROC [] =
  CHECKED BEGIN
  state.height ← state.height + entryHeight + entryVSpace; -- interline spacing
  nextX ← entryHSpace;
  END;


NextButton: PROC [label: Rope.ROPE, proc: Buttons.ButtonProc, border: BOOLTRUE,
  extraSpaces: INTEGER ← 1] =
  TRUSTED BEGIN
  button: Buttons.Button = Buttons.Create[
   info: [
    name: label,
    wx: nextX+extraSpaces*entryHSpace,
    wy: state.height,
    -- default the width so that it will be computed for us
    wh: entryHeight, -- specify rather than defaulting so line is uniform
    parent: state.outer, 
    border: border
    ],
   clientData: state, -- this will be passed to our button proc
   proc: proc,
   fork: TRUE
   ];
  nextX ← button.wx + button.ww + entryHSpace;
  END;
  
 openHeight: INTEGER ← 150;
 initialData: Rope.ROPE;
 initialBoard: Rope.ROPE ← "memc-apcrev-be";
 nextX: INTEGER ← entryHSpace;
  
 state: Board = NEW[BoardRec ← [
  center: [x: boardWidth/2, y: boardHeight/2],
  pads: NEW[Pads]]];

 board ← state;

-- construct the outer container
 state.outer ← Containers.Create[[
  name: "DoradoBoard",  -- name displayed in the caption
  iconic: TRUE,  -- so tool will be iconic (small) when first created
  column: left,  -- initially in the left column
  scrollable: FALSE  -- inhibit user from scrolling contents
  ]];

 state.height ← state.height + entryVSpace; -- space down from the top of the viewer
 NextButton[label: "Repaint", proc: Repaint];
 NextButton[label: "Mirror", proc: Mirror, extraSpaces: 2];
 NextButton[label: "Rot-90", proc: Rotate];
 NextButton[label: "Setup board", proc: ReadInputFile, extraSpaces: 2];
 NextButton[label: "Board name:", proc: BoardNamePrompt, border: FALSE];
 state.boardName ← ViewerTools.MakeNewTextViewer[[
  parent: state.outer,
  wx: nextX,
  wy: state.height+2, -- (hack: add 2 to align text with button baseline)
  ww: 25*VFonts.CharWidth['0], -- 25 digits worth of width
  wh: entryHeight,
  data: initialBoard, -- initial contents
  scrollable: FALSE,
  border: FALSE
  ]];
 NewButtonLine[];

 NextButton[label: "Labels", proc: ShowLabels];
 NextButton[label: "Pads", proc: ShowPads];
 NextButton[label: "WiresOff", proc: Clear];
 NextButton[label: "+Wire", proc: AddWire];
 NextButton[label: "-Wire", proc: DelWire];
 NextButton[label: "Signal:", proc: SigPrompt, border: FALSE];
 state.signal ← ViewerTools.MakeNewTextViewer[[
  parent: state.outer,
  wx: nextX,
  wy: state.height+2, -- (hack: add 2 to align text with button baseline)
  ww: 25*VFonts.CharWidth['0], -- 25 digits worth of width
  wh: entryHeight,
  data: initialData,
  scrollable: FALSE,
  border: FALSE
  ]];
 NewButtonLine[];

 NextButton[label: "Outer component", proc: Layer1];
 NextButton[label: "Inner component", proc: Layer0];
 NextButton[label: "Inner solder", proc: Layer2];
 NextButton[label: "Outer solder", proc: Layer3];
 NewButtonLine[];

 Containers.ChildXBound[state.outer,
  Rules.Create[
   info: [
    parent: state.outer,
    wx: 0, wy: state.height,
    ww: state.outer.cw, wh:3
  ]]]; --constrain bar to be width of parent

 state.boardPicture ← ViewerOps.CreateViewer[
  flavor: $DBoard,
  info: [
   parent: state.outer,
   wx: 0, wy: state.height+3,
   scrollable: FALSE,
   border: FALSE,
   data: state
   ],
  paint: TRUE
  ];
 Containers.ChildXBound[container: state.outer, child: state.boardPicture];
 Containers.ChildYBound[container: state.outer, child: state.boardPicture];

 ViewerOps.OpenIcon[icon: state.outer];
IF ColorWorld.HasMode[4] AND NOT WindowManager.colorDisplayOn THEN
  WindowManager.StartColorViewers[screenPos: left, bitsPerPixel: 4];
END; -- of DisplayBoard --


Destroy: ViewerClasses.DestroyProc =
BEGIN
 state: Board = NARROW[self.data];
IF state.asiFile # NIL THEN {state.asiFile.Close[]; state.asiFile ← NIL};
IF state.errorInStream # NIL THEN {state.errorInStream.Close[]; state.errorInStream ← NIL};
IF state.errorOutStream # NIL THEN {state.errorOutStream.Close[]; state.errorOutStream ← NIL};
END; -- of Destroy



----
---- ---- ---- ---- ---- ---- ----

-- Button-driven procedures


SigPrompt: ENTRY Buttons.ButtonProc =
-- force the selection into the user input field
TRUSTED BEGIN
 state: Board = NARROW[clientData]; -- get our data
 ViewerTools.SetSelection[state.signal]; -- force the selection
END; -- of SigPrompt --


BoardNamePrompt: ENTRY Buttons.ButtonProc =
-- force the selection into the user input field
TRUSTED BEGIN
 state: Board = NARROW[clientData]; -- get our data
 ViewerTools.SetSelection[state.boardName]; -- force the selection
END; -- of BoardNamePrompt --


Layer0: ENTRY Buttons.ButtonProc = {Layer[0, parent, clientData]};
Layer1: ENTRY Buttons.ButtonProc = {Layer[1, parent, clientData]};
Layer2: ENTRY Buttons.ButtonProc = {Layer[2, parent, clientData]};
Layer3: ENTRY Buttons.ButtonProc = {Layer[3, parent, clientData]};

Layer: PROC [layer: WiringLayer, parent, clientData: REF ANY] =
TRUSTED BEGIN
 viewer: ViewerClasses.Viewer = NARROW[parent];
 state: Board = NARROW[clientData]; -- get our data
 state.plotLayer[layer] ← NOT state.plotLayer[layer];
 Buttons.SetDisplayStyle[viewer,
IF state.plotLayer[layer] THEN $BlackOnWhite ELSE $WhiteOnBlack];
END; -- of Layer --


ShowLabels: ENTRY Buttons.ButtonProc =
-- Turn on labels inside IC's.
TRUSTED BEGIN
 state: Board = NARROW[clientData];
 state.showLabels ← NOT state.showLabels;
 ViewerOps.PaintViewer[
  viewer: state.boardPicture,
  hint: client,
  clearClient: TRUE
  ];
END; -- of ShowLabels --


ShowPads: ENTRY Buttons.ButtonProc =
-- Turn on pads inside IC's.
TRUSTED BEGIN
 state: Board = NARROW[clientData];
 state.showPads ← NOT state.showPads;
 ViewerOps.PaintViewer[
  viewer: state.boardPicture,
  hint: client,
  clearClient: TRUE
  ];
END; -- of ShowLabels --


AddWire: ENTRY Buttons.ButtonProc =
-- Add a signal to the list 'signalPads' to be painted.
TRUSTED BEGIN
 viewer: ViewerClasses.Viewer = NARROW[parent];
 state: Board = NARROW[clientData];
 signal: Rope.ROPE ← ViewerTools.GetContents[state.signal];
IF signal = NIL THEN RETURN;
 Buttons.SetDisplayStyle[viewer, $BlackOnGrey];
-- grey button to indicate activity to user
IF AddSignal[state, signal] THEN {
  ViewerOps.PaintViewer[
   viewer: state.boardPicture,
   hint: client,
   clearClient: FALSE
   ]}
ELSE BEGIN
  MessageWindow.Append[
   message: "Signal not found!",
   clearFirst: TRUE
   ];
  MessageWindow.Blink[];
  END;
 Buttons.SetDisplayStyle[viewer, $BlackOnWhite];
END; -- of Add --


DelWire: ENTRY Buttons.ButtonProc =
-- Remove a signal from the list 'signalPads'.
TRUSTED BEGIN
 viewer: ViewerClasses.Viewer = NARROW[parent];
 state: Board = NARROW[clientData];
 signal: Rope.ROPE ← ViewerTools.GetContents[state.signal];
IF signal = NIL THEN RETURN;
 Buttons.SetDisplayStyle[viewer, $BlackOnGrey];
-- grey button to indicate activity to user
IF RemoveSignal[state, signal] THEN {
  ViewerOps.PaintViewer[
   viewer: state.boardPicture,
   hint: client,
   clearClient: TRUE
   ]};
 Buttons.SetDisplayStyle[viewer, $BlackOnWhite];
  END; 

Mirror: ENTRY Buttons.ButtonProc =
TRUSTED BEGIN
 viewer: ViewerClasses.Viewer = NARROW[parent];
 state: Board = NARROW[clientData];
 state.mirFact ← -state.mirFact;
 Buttons.SetDisplayStyle[viewer,
  IF state.mirFact>0 THEN $BlackOnWhite ELSE $WhiteOnBlack];
 ViewerOps.PaintViewer[
  viewer: state.boardPicture,
  hint: client,
  clearClient: TRUE
  ];
END; -- of Mirror --


Rotate: ENTRY Buttons.ButtonProc =
-- Rotation occurs around the point at the center of the viewer. RotAngle increments
-- and decrements by 90 degrees.
TRUSTED BEGIN
 state: Board = NARROW[clientData];
 state.rotAngle ← IF mouseButton = red THEN (state.rotAngle + 90) MOD 360
  ELSE (state.rotAngle - 90) MOD 360;
 ViewerOps.PaintViewer[
  viewer: state.boardPicture,
  hint: client,
  clearClient: TRUE
  ];
END; -- of Rotate --


Clear: ENTRY Buttons.ButtonProc =
-- Reset signal list
TRUSTED BEGIN
 state: Board = NARROW[clientData]; -- get our data
 state.signalPads ← NIL;
 ViewerOps.PaintViewer[
  viewer: state.boardPicture,
  hint: client,
  clearClient: TRUE
  ];
END; -- of Clear --


Repaint: ENTRY Buttons.ButtonProc =
TRUSTED BEGIN
 state: Board = NARROW[clientData];
 ViewerOps.PaintViewer[
  viewer: state.boardPicture,
  hint: client,
  clearClient: TRUE
  ];
END; -- of Repaint --


ReadInputFile: ENTRY Buttons.ButtonProc =
-- Read in a set of board data files
TRUSTED BEGIN
 viewer: ViewerClasses.Viewer = NARROW[parent];
 state: Board = NARROW[clientData];
 name: Rope.ROPE ← ViewerTools.GetContents[state.boardName];
 Buttons.SetDisplayStyle[viewer, $BlackOnGrey];
 state.signalPads ← NIL;
IF Rope.Length[name]>0 THEN
  BEGIN
  state.outer.name ← name;
  ViewerOps.PaintViewer[viewer: state.outer, hint: caption];
  ReadBoardData[state, name];
  ViewerOps.PaintViewer[
   viewer: state.boardPicture,
   hint: client,
   clearClient: TRUE
   ];
  END;
 Buttons.SetDisplayStyle[viewer, $BlackOnWhite];
END; -- of ReadInputFile --



---- ---- ---- ---- ---- ---- ---- ----

-- Mouse-Driven Procedures


Notify: ViewerClasses.NotifyProc =
TRUSTED BEGIN
 x, y: INTEGER;
 state: Board = NARROW[self.data];
 InputFocus.SetInputFocus[self: self];
FOR input ← input, input.rest WHILE input # NIL DO
  WITH input.first SELECT FROM
   coords : TIPUser.TIPScreenCoords =>
    {x ← coords.mouseX; y ← coords.mouseY};
   atom: ATOM => SELECT atom FROM
    $QuickGrow => Zoom[5, self];
    $Grow => Zoom[1.5*state.scale, self];
    $Center => Center[x, y, self];
    $SelectPad => SelectPad[x, y, self];
    $Shrink => Zoom[0.6667*state.scale, self];
    $QuickShrink => Zoom[1, self];
    ENDCASE => ERROR;
   ENDCASE;
  ENDLOOP;
END; -- of Notify --


Zoom: PROC [newScale: REAL, viewer: ViewerClasses.Viewer] =
TRUSTED BEGIN
 state: Board = NARROW[viewer.data];
 state.scale ← newScale;
IF state.scale < 1 THEN state.scale ← 1;
 ViewerOps.PaintViewer[
  viewer: viewer,
  hint: client,
  clearClient: TRUE
  ];
END; -- of Zoom --


Center: PROC [x,y: INTEGER, viewer: ViewerClasses.Viewer] =
TRUSTED BEGIN
 state: Board = NARROW[viewer.data];
 context: Graphics.Context = BoardContext[state];
 cx, cy: REAL;
 [dx: cx, dy: cy] ← Graphics.Map[dc: context,
  sc: Graphics.NewContext[], sx: x, sy: y];
 state.center ← [x: Real.RoundI[cx], y: Real.RoundI[cy]];
 ViewerOps.PaintViewer[
  viewer: viewer,
  hint: client,
  clearClient: TRUE
  ];
END; -- of Center --


SelectPad
: PROC [x,y: INTEGER, viewer: ViewerClasses.Viewer] =
TRUSTED BEGIN
 state: Board = NARROW[viewer.data];
 sigName: Rope.ROPE;
 context: Graphics.Context = BoardContext[state];
 cx, cy: REAL;
 Graphics.Translate[context, asiOffset.x, asiOffset.y];
 [dx: cx, dy: cy] ← Graphics.Map[dc: context,
  sc: Graphics.NewContext[], sx: x, sy: y];
 [state.hitPadIndex, sigName] ←
  FindClosestPad[state, [x: Real.RoundI[cx], y: Real.RoundI[cy]]];
 ViewerTools.SetContents[state.signal, sigName];
END; -- of SelectPad --




---- ---- ---- ---- ---- ---- ---- ----

---- ---- ---- ---- ---- ---- ---- ----
-- The following code runs when the module starts.

-- Register the board class of viewer with its TIP table.

boardClass: ViewerClasses.ViewerClass = NEW[ViewerClasses.ViewerClassRec ← [
 flavor: $DBoard,
 paint: PaintBoard,
 notify: Notify,
 destroy: Destroy,
 tipTable: TIPUser.InstantiateNewTIPTable["DoradoBoards.TIP"]
 ]];
ViewerOps.RegisterViewerClass[$DBoard, boardClass];

-- Register a command with the UserExec that can create an instance of our tool.

UserExec.RegisterCommand[
 name: "DisplayBoard",
 proc: DisplayBoard,
 briefDoc: "Create a viewer of a Dorado circuit board"
 ];

END.


CHANGE LOG

Created by Hilton, September 9, 1982 7:05 pm