StyleToolGraphicsImpl.mesa
Written by Linda Gass September 10, 1982 4:34 pm
Last Edit by Linda Gass November 4, 1982 3:44 pm
-- Last Edited by: Plass, March 28, 1983 3:03 pm

DIRECTORY
ChoiceButtons USING [GetSelectedButton, PromptDataRef],
Containers USING [ChildXBound, ChildYBound, Create],
Graphics USING [black, Box, Color, Context, DrawBox, DrawArea, DrawStroke, DrawRope,
FontRef, GetBounds, LineTo, MoveTo, NewPath, PaintMode, Path, Rectangle, RopeBox, SetColor, SetCP, SetPaintMode, white],
GraphicsColor USING [RGBToColor],
Icons USING [NewIconFromFile],
IO USING [int, PutFToRope],
MessageWindow USING [Clear],
Real USING [FixI],
Rope USING [Equal, ROPE],
StyleToolBuilders USING [DisplayRealInViewer],
StyleToolConvert USING [GetRealFromViewer],
StyleToolDefs USING [MarginType, PageBoundaryType, StyleToolHandle, ValueAndUnitsRef],
StyleToolGraphics,
StyleToolGraphicsMisc USING [CreateNumbersContainer, defaultInterval, HitAMargin, HitAPage, InsidePage, ScreenToLeftMargin, ScreenToRightMargin, ScreenToBindingMargin, ScreenToTopMargin, ScreenToBottomMargin, ScreenToPageWidth, ScreenToPageLength, SetUpNumbers],
TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords],
VFonts USING [EstablishFont, Font, GraphicsFont],
ViewerClasses USING [NotifyProc, PaintProc, Viewer, ViewerClass, ViewerClassRec],
ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass];

StyleToolGraphicsImpl: CEDAR PROGRAM
IMPORTS ChoiceButtons, Containers, Graphics, GraphicsColor, Icons, IO, MessageWindow, Real, Rope, StyleToolBuilders, StyleToolConvert, StyleToolGraphicsMisc, TIPUser, VFonts, ViewerOps
EXPORTS StyleToolGraphics =
BEGIN OPEN StyleToolDefs, StyleToolGraphicsMisc;
PaintInfoRef: TYPE = REF PaintInfoRec;
PaintInfoRec: TYPE = RECORD [
 whatToChange: Changeables,  -- part of layout to change
 x,y: REAL       -- Mouse coordinates
 ];
Changeables: TYPE = {all, margin, page};

gray: Graphics.Color = GraphicsColor.RGBToColor[0.5, 0.5, 0.5];

gridThickness: REAL = 1.0;
pageOutlineThickness: REAL = 2.0; 
marginThickness: REAL = 1.0;

CreateLayoutContainer: PUBLIC PROCEDURE [handle: StyleToolHandle] = {
nexty: INTEGER;
handle.layoutGraphics.container ← Containers.Create[info: [name: "PAGE LAYOUT", scrollable: FALSE, iconic: FALSE, column: left, icon: Icons.NewIconFromFile["StyleTool.icons", 3]]];

nexty ← CreateNumbersContainer[handle];
handle.layoutGraphics.interval ← defaultInterval;
SetUpNumbers[handle];
CreateGraphicsViewer[handle, nexty];
ViewerOps.PaintViewer[handle.layoutGraphics.container, all];
};
DestroyProc: Menus.MenuProc = { (Currently not used)
handle: StyleToolHandle ← NARROW[clientData];
IF ~MessageWindow.Confirm["Confirm discarding all edits . . ."] THEN RETURN;
ViewerOps.DestroyViewer[handle.layoutGraphics.container];
handle.layoutGraphics.container ← NIL;
handle.layoutGraphics.exists ← FALSE;
};
CreateGraphicsViewer: PROCEDURE [handle: StyleToolHandle, starty: INTEGER] = {
layoutViewerClass: ViewerClasses.ViewerClass ← NEW[ViewerClasses.ViewerClassRec ← [
 paint: LayoutPainter,
 notify: NotifyEvents,
 coordSys: bottom,
 tipTable: TIPUser.InstantiateNewTIPTable["StyleToolGraphics.TIP"]
 ]];
ViewerOps.RegisterViewerClass[$LayoutViewer, layoutViewerClass];
handle.layoutGraphics.viewer ← ViewerOps.CreateViewer[flavor: $LayoutViewer, info: [
 parent: handle.layoutGraphics.container, wx: 0, wy: starty, scrollable: FALSE, border: FALSE,
 data: handle]];
Since we have created our own viewer class, we can attach anything we want to the data field of the viewer rec. We put the handle there so that in future calls to the paint proc, we can have handle on the StyleTool
Containers.ChildXBound[handle.layoutGraphics.container, handle.layoutGraphics.viewer];
Containers.ChildYBound[handle.layoutGraphics.container, handle.layoutGraphics.viewer];
};
UpdateValue: PROCEDURE[displayInfo: ChoiceButtons.PromptDataRef, newValue: REAL] = INLINE {
StyleToolBuilders.DisplayRealInViewer[newValue, displayInfo.textViewer];
};
LayoutPainter: ViewerClasses.PaintProc = {
handle: StyleToolHandle ← NARROW[self.data];
MessageWindow.Clear[];
IF whatChanged = NIL THEN {
bounds: Graphics.Box ← Graphics.GetBounds[context];
DrawGrid[handle, context, clear, bounds];
DrawPage[handle, context];
DrawNumbers[handle, context, bounds];
DrawUnits[handle, context];
DrawMargins[handle, context];
}
ELSE {
paintInfo: PaintInfoRef ← NARROW[whatChanged];
SELECT paintInfo.whatToChange FROM
 margin => ChangeMargin[handle, context, paintInfo.x, paintInfo.y];
 page  => ChangePage[handle, context, paintInfo.x, paintInfo.y];
 all   => {DrawPage[handle, context];
     DrawMargins[handle, context];
     };
ENDCASE => ERROR;
};
};
NotifyEvents: ViewerClasses.NotifyProc = {
handle: StyleToolHandle ← NARROW[self.data];
iMouse, jMouse: REAL;
InterpretAtom: PROCEDURE [atom: ATOM] = {
paintInfo: PaintInfoRef;
SELECT atom FROM
$StartMarginMode => IF InsidePage[handle, iMouse, jMouse] THEN {
       IF (handle.layoutGraphics.currentMargin ←
        HitAMargin[handle, iMouse, jMouse]) # none THEN {
        paintInfo ← NEW [PaintInfoRec ← [margin, iMouse, jMouse]];
        ViewerOps.PaintViewer[viewer: self, hint: client,
         whatChanged: paintInfo, clearClient: FALSE];
        };
       };
$Margin => IF InsidePage[handle, iMouse, jMouse] THEN {
     IF handle.layoutGraphics.currentMargin = none THEN {
      IF (handle.layoutGraphics.currentMargin ←
       HitAMargin[handle, iMouse, jMouse]) # none THEN {
       paintInfo ← NEW [PaintInfoRec ← [margin, iMouse, jMouse]];
       ViewerOps.PaintViewer[viewer: self, hint: client,
         whatChanged: paintInfo, clearClient: FALSE];
       };
      }
     ELSE {paintInfo ← NEW [PaintInfoRec ← [margin, iMouse, jMouse]];
       ViewerOps.PaintViewer[viewer: self, hint: client,
         whatChanged: paintInfo, clearClient: FALSE];
       };
     };
$EndMarginMode => IF InsidePage[handle, iMouse, jMouse] THEN {
       IF handle.layoutGraphics.currentMargin # none THEN {
        paintInfo ← NEW [PaintInfoRec ← [margin, iMouse, jMouse]];
        ViewerOps.PaintViewer[viewer: self, hint: client,
         whatChanged: paintInfo, clearClient: FALSE];
        };
       };
$StartPageMode => IF (handle.layoutGraphics.currentPage ←
        HitAPage[handle, iMouse, jMouse]) # none THEN {
        paintInfo ← NEW [PaintInfoRec ← [page, iMouse, jMouse]];
        ViewerOps.PaintViewer[viewer: self, hint: client,
         whatChanged: paintInfo, clearClient: FALSE];
        };
$Page =>  IF handle.layoutGraphics.currentPage = none THEN {
    IF (handle.layoutGraphics.currentPage ←
     HitAPage[handle, iMouse, jMouse]) # none THEN {
     paintInfo ← NEW [PaintInfoRec ← [page, iMouse, jMouse]];
     ViewerOps.PaintViewer[viewer: self, hint: client,
      whatChanged: paintInfo, clearClient: FALSE];
     };
    }
   ELSE {paintInfo ← NEW [PaintInfoRec ← [page, iMouse, jMouse]];
    ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: paintInfo,
     clearClient: FALSE];
    };
$EndPageMode => IF handle.layoutGraphics.currentPage # none THEN {
      paintInfo ← NEW [PaintInfoRec ← [all, iMouse, jMouse]];
      ViewerOps.PaintViewer[viewer: self, hint: client,
       whatChanged: paintInfo, clearClient: FALSE];
      };
ENDCASE;
};
FOR items: LIST OF REF ANY ← input, items.rest UNTIL items = NIL DO
WITH items.first SELECT FROM
  x: TIPUser.TIPScreenCoords => { iMouse ← x.mouseX;
            jMouse ← x.mouseY;
            };
  x: ATOM       => InterpretAtom[x];
  ENDCASE;
ENDLOOP;
};
DrawGrid: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, clear: BOOLEAN, bounds: Graphics.Box] = {
xpos, ypos, rem: INTEGER;
path: Graphics.Path;
start on an even interval boundary
xpos ← Real.FixI[bounds.xmin];
rem ← (xpos MOD handle.layoutGraphics.interval);
IF rem # 0 THEN
 xpos ← xpos+ handle.layoutGraphics.interval - rem;
ypos ← Real.FixI[bounds.ymin];
rem ← (ypos MOD handle.layoutGraphics.interval);
IF rem # 0 THEN
 ypos ← ypos + handle.layoutGraphics.interval - rem;
first clear the viewer if necessary
IF ~clear THEN {
 Graphics.SetColor[context, Graphics.white];
 Graphics.DrawBox[context, bounds];
 };
now draw the grid
Graphics.SetColor[context, gray];
path ← Graphics.NewPath[];

WHILE xpos <= bounds.xmax OR ypos <= bounds.ymax DO
vertical lines
Graphics.MoveTo[path, xpos, bounds.ymin, TRUE]; -- flush = true to flush out previous path
Graphics.LineTo[path, xpos, bounds.ymax];
Graphics.DrawStroke[context, path, gridThickness];
horizontal lines
Graphics.MoveTo[path, bounds.xmin, ypos, TRUE];
Graphics.LineTo[path, bounds.xmax, ypos];
Graphics.DrawStroke[context, path, gridThickness];

xpos ← xpos + handle.layoutGraphics.interval;
ypos ← ypos + handle.layoutGraphics.interval;
ENDLOOP;
};
DrawPage: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context] = {
start the proper amount of units in
OPEN handle.layoutGraphics; -- for screenPage, interval, indent, pageWidth, pageLength
pageVal: REAL;
path: Graphics.Path;
DetermineIndent[handle];
DetermineSkipAndIncrement[handle];
screenPage.xmin ← indent*interval;
pageVal ← StyleToolConvert.GetRealFromViewer[pageWidth.textViewer];
screenPage.xmax ← ((pageVal / (increment/skip)) + indent) * interval;
screenPage.ymin ← indent*interval;
pageVal ← StyleToolConvert.GetRealFromViewer[pageLength.textViewer];
screenPage.ymax ← ((pageVal / (increment/skip)) + indent) * interval;
clear the area which is the page
Graphics.SetColor[context, Graphics.white];
Graphics.DrawBox[context, handle.layoutGraphics.screenPage];
now draw the outline of the page
Graphics.SetColor[context, Graphics.black];
path ← Graphics.NewPath[];
Graphics.MoveTo[path, screenPage.xmin, screenPage.ymin];
Graphics.LineTo[path, screenPage.xmax, screenPage.ymin];
Graphics.LineTo[path, screenPage.xmax, screenPage.ymax];
Graphics.LineTo[path, screenPage.xmin, screenPage.ymax];
Graphics.LineTo[path, screenPage.xmin, screenPage.ymin];
Graphics.DrawStroke[context, path, pageOutlineThickness];
};
DetermineIndent: PROCEDURE [handle: StyleToolHandle] = {
units: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.layoutGraphics.units.ref];
SELECT TRUE FROM
Rope.Equal[units, "points", FALSE]    => handle.layoutGraphics.indent ← 4; 
Rope.Equal[units, "picas", FALSE]   => handle.layoutGraphics.indent ← 4;
Rope.Equal[units, "inches", FALSE]   => handle.layoutGraphics.indent ← 2;
Rope.Equal[units, "centimeters", FALSE]  => handle.layoutGraphics.indent ← 4;
Rope.Equal[units, "millimeters", FALSE]  => handle.layoutGraphics.indent ← 4;
Rope.Equal[units, "didot points", FALSE]  => handle.layoutGraphics.indent ← 4;
ENDCASE          => handle.layoutGraphics.indent ← 2;
};
DrawNumbers: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, bounds: Graphics.Box] = {
OPEN handle.layoutGraphics; -- for interval
font: Graphics.FontRef ← VFonts.GraphicsFont[VFonts.EstablishFont[
 family: "Helvetica", size: 10, bold: TRUE, italic: FALSE, defaultOnFailure: TRUE]];
xline, yline: REAL ← GetNumberLine[handle] * interval;
xpos, ypos: REAL ← indent*interval;
rxmin, rxmax, rymin, rymax: REAL;
x0, y0, x1, y1: REAL;
i: CARDINAL ← 0;
numberRope: Rope.ROPE;
path: Graphics.Path ← Graphics.NewPath[];
numbers accross x axis
WHILE xpos <= bounds.xmax DO
 numberRope ← IO.PutFToRope["%g", IO.int[i]];
 [rxmin, rymin, rxmax, rymax] ← Graphics.RopeBox[font, numberRope];
 x0 ← xpos - (rxmax - rxmin)/2 - 2; -- bottom right corner
 y0 ← yline - (rymax - rymin)/2;
 x1 ← xpos + (rxmax - rxmin)/2 + 2; -- top left corner plus some pixels
 y1 ← yline + (rymax - rymin)/2;
 Graphics.SetColor[context, Graphics.white];
 Graphics.MoveTo[path, x0, y0, TRUE];
 Graphics.Rectangle[path, x0, y0, x1, y1];
 Graphics.DrawArea[context, path];
 Graphics.SetColor[context, Graphics.black];
 Graphics.SetCP[context, x0 + 2, y0 + 2];
 Graphics.DrawRope[self: context, rope: numberRope, font: font];
 xpos ← xpos + skip * interval;
 i ← i + increment;
ENDLOOP;
numbers accross the y axis
i ← 0;
WHILE ypos <= bounds.ymax DO
 numberRope ← IO.PutFToRope["%g", IO.int[i]];
 [rxmin, rymin, rxmax, rymax] ← Graphics.RopeBox[font, numberRope];
 x0 ← xline - (rxmax - rxmin)/2 - 2; -- bottom right corner
 y0 ← ypos - (rymax - rymin)/2;
 x1 ← xline + (rxmax - rxmin)/2 + 2; -- top left corner plus some pixels
 y1 ← ypos + (rymax - rymin)/2;
 Graphics.SetColor[context, Graphics.white];
 Graphics.MoveTo[path, x0, y0, TRUE];
 Graphics.Rectangle[path, x0, y0, x1, y1];
 Graphics.DrawArea[context, path];
 Graphics.SetColor[context, Graphics.black];
 Graphics.SetCP[context, x0 + 2, y0 + 2];
 Graphics.DrawRope[self: context, rope: numberRope, font: font];
 ypos ← ypos + skip * interval;
 i ← i + increment;
ENDLOOP;
};
GetNumberLine: PROCEDURE [handle: StyleToolHandle] RETURNS [INTEGER] = {
units: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.layoutGraphics.units.ref];
SELECT TRUE FROM
Rope.Equal[units, "points", FALSE]    => RETURN[2]; 
Rope.Equal[units, "picas", FALSE]   => RETURN[2];
Rope.Equal[units, "inches", FALSE]   => RETURN[1];
Rope.Equal[units, "centimeters", FALSE]  => RETURN[2];
Rope.Equal[units, "millimeters", FALSE]  => RETURN[2];
Rope.Equal[units, "didot points", FALSE]  => RETURN[2];
ENDCASE          => RETURN[2];
};
DetermineSkipAndIncrement: PROCEDURE [handle: StyleToolHandle] = {
units: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.layoutGraphics.units.ref];
SELECT TRUE FROM
Rope.Equal[units, "points", FALSE]    => {handle.layoutGraphics.skip ← 2;
              handle.layoutGraphics.increment ← 72};
Rope.Equal[units, "picas", FALSE]   => {handle.layoutGraphics.skip ← 1;
              handle.layoutGraphics.increment ← 3};
Rope.Equal[units, "inches", FALSE]   => {handle.layoutGraphics.skip ← 1;
              handle.layoutGraphics.increment ← 1};
Rope.Equal[units, "centimeters", FALSE]  => {handle.layoutGraphics.skip ← 2;
              handle.layoutGraphics.increment ← 2};
Rope.Equal[units, "millimeters", FALSE]  => {handle.layoutGraphics.skip ← 2;
              handle.layoutGraphics.increment ← 20};
Rope.Equal[units, "didot points", FALSE]  => {handle.layoutGraphics.skip ← 2;
              handle.layoutGraphics.increment ← 50};
ENDCASE          => {handle.layoutGraphics.skip ← 1;
              handle.layoutGraphics.increment ← 1};
};
DrawUnits: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context] = {
unitsRope: Rope.ROPE;
font: Graphics.FontRef ← VFonts.GraphicsFont[VFonts.EstablishFont[
 family: "Helvetica", size: 8, bold: TRUE, italic: FALSE, defaultOnFailure: TRUE]];
xline, yline: REAL ← GetNumberLine[handle] * handle.layoutGraphics.interval;
rxmin, rxmax, rymin, rymax: REAL;
x0, y0, x1, y1: REAL;
path: Graphics.Path ← Graphics.NewPath[];
unitsRope ← ChoiceButtons.GetSelectedButton[handle.layoutGraphics.units.ref];

[rxmin, rymin, rxmax, rymax] ← Graphics.RopeBox[font, unitsRope];
x0 ← xline - (rxmax - rxmin)/2 - 2; -- bottom right corner
y0 ← yline - (rymax - rymin)/2;
x1 ← xline + (rxmax - rxmin)/2 + 2; -- top left corner plus some pixels
y1 ← yline + (rymax - rymin)/2;

Graphics.SetColor[context, Graphics.white];
Graphics.MoveTo[path, x0, y0];
Graphics.Rectangle[path, x0, y0, x1, y1];
Graphics.DrawArea[context, path];
Graphics.SetColor[context, Graphics.black];
Graphics.SetCP[context, x0 + 2, y0 + 2];
Graphics.DrawRope[self: context, rope: unitsRope, font: font];
};
DrawMargins: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context] = {
Graphics.SetColor[context, Graphics.black];
DrawLeftMargin[handle, context];
DrawRightMargin[handle, context];
DrawTopMargin[handle, context];
DrawBottomMargin[handle, context];
DrawBindingMargin[handle, context];
};
DrawLeftMargin: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context] = {
OPEN handle.layoutGraphics; -- for leftMargin, screenLeftMargin, screenPage and interval
margin: REAL ← StyleToolConvert.GetRealFromViewer[leftMargin.textViewer];
path: Graphics.Path ← Graphics.NewPath[];
screenLeftMargin ← screenPage.xmin + ((margin / (increment/skip)) * interval);
IF screenLeftMargin < screenPage.xmax THEN {
-- Dont bother drawing a margin which lies outside of the page
 Graphics.MoveTo[path, screenLeftMargin, screenPage.ymin];
 Graphics.LineTo[path, screenLeftMargin, screenPage.ymax];
 Graphics.DrawStroke[context, path, marginThickness];
 };
};
DrawRightMargin: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context] = {
OPEN handle.layoutGraphics; -- for rightMargin, screenRightMargin, screenPage and interval
margin: REAL ← StyleToolConvert.GetRealFromViewer[rightMargin.textViewer];
path: Graphics.Path ← Graphics.NewPath[];
screenRightMargin ← screenPage.xmax - ((margin / (increment/skip)) * interval);
IF screenRightMargin > screenPage.xmin THEN {
 Graphics.MoveTo[path, screenRightMargin, screenPage.ymin];
 Graphics.LineTo[path, screenRightMargin, screenPage.ymax];
 Graphics.DrawStroke[context, path, marginThickness];
 };
};
DrawTopMargin: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context] = {
OPEN handle.layoutGraphics; -- for topMargin, screenTopMargin, screenPage and interval
margin: REAL ← StyleToolConvert.GetRealFromViewer[topMargin.textViewer];
path: Graphics.Path ← Graphics.NewPath[];
screenTopMargin ← screenPage.ymax - ((margin / (increment/skip)) * interval);
IF screenTopMargin > screenPage.ymin THEN {
 Graphics.MoveTo[path, screenPage.xmin, screenTopMargin];
 Graphics.LineTo[path, screenPage.xmax, screenTopMargin];
 Graphics.DrawStroke[context, path, marginThickness];
 };
};
DrawBottomMargin: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context] = {
OPEN handle.layoutGraphics; -- for bottomMargin, screenBottomMargin, screenPage and interval
margin: REAL ← StyleToolConvert.GetRealFromViewer[bottomMargin.textViewer];
path: Graphics.Path ← Graphics.NewPath[];
screenBottomMargin ← screenPage.ymin + ((margin / (increment/skip)) * interval);
IF screenBottomMargin < screenPage.ymax THEN {
 Graphics.MoveTo[path, screenPage.xmin, screenBottomMargin];
 Graphics.LineTo[path, screenPage.xmax, screenBottomMargin];
 Graphics.DrawStroke[context, path, marginThickness];
 };
};
DrawBindingMargin: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context] = {
OPEN handle.layoutGraphics; -- for bindingMargin, screenBindingMargin, screenPage and interval
offset: REAL ← StyleToolConvert.GetRealFromViewer[bindingMargin.textViewer];
path: Graphics.Path ← Graphics.NewPath[];
screenBindingMargin ← screenLeftMargin + ((offset / (increment/skip)) * interval);
IF screenBindingMargin < screenPage.xmax THEN {
 Graphics.MoveTo[path, screenBindingMargin, screenPage.ymin];
 Graphics.LineTo[path, screenBindingMargin, screenPage.ymax];
 Graphics.DrawStroke[context, path, marginThickness];
 };
};
ChangeMargin: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, x, y: REAL] = {
for each different margin we do the same procedure. First check that the new position will not cause the margin to overlap with its "opposite" margin. This means that if we are changing the right margin, we first want to check that the new right margin will not overlap the left margin, this would not make sense and it would drive the typesetter crazy. If everything is OK, then change the position of the margin
OPEN handle.layoutGraphics; -- for currentMargin, screenRightMargin, screenLeftMargin, rightMargin, leftMargin, etc
neverLand: REAL ← 5.0;
SELECT currentMargin FROM
 left => IF (x + neverLand) < screenRightMargin THEN {
     diff: REAL ← x - screenLeftMargin;
     DrawVerticalMargin[handle, context, screenLeftMargin];
     screenLeftMargin ← x;
     DrawVerticalMargin[handle, context, screenLeftMargin];
     UpdateValue[leftMargin, ScreenToLeftMargin[handle]];
     -- also redraw the binding margin accordingly
     DrawVerticalMargin[handle, context, screenBindingMargin];
     screenBindingMargin ← screenBindingMargin + diff;
     DrawVerticalMargin[handle, context, screenBindingMargin];
     -- no need to update the value of the binding margin since it's an offset and
     -- that hasn't changed
     };
 right  => IF (x - neverLand) > screenLeftMargin THEN {
     DrawVerticalMargin[handle, context, screenRightMargin];
     screenRightMargin ← x;
     DrawVerticalMargin[handle, context, screenRightMargin];
     UpdateValue[rightMargin, ScreenToRightMargin[handle]];
     };
 binding => IF x > screenLeftMargin THEN {
     DrawVerticalMargin[handle, context, screenBindingMargin];
     screenBindingMargin ← x;
     DrawVerticalMargin[handle, context, screenBindingMargin];
     UpdateValue[bindingMargin, ScreenToBindingMargin[handle]];
     };
 top => IF (y - neverLand) > screenBottomMargin THEN {
     DrawHorizontalMargin[handle, context, screenTopMargin];
     screenTopMargin ← y;
     DrawHorizontalMargin[handle, context, screenTopMargin];
     UpdateValue[topMargin, ScreenToTopMargin[handle]];
     };
 bottom => IF (y + neverLand) < screenTopMargin THEN {
     DrawHorizontalMargin[handle, context, screenBottomMargin];
     screenBottomMargin ← y;
     DrawHorizontalMargin[handle, context, screenBottomMargin];
     UpdateValue[bottomMargin, ScreenToBottomMargin[handle]];
     };
ENDCASE;
};
DrawVerticalMargin: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, x: REAL] = {
oldPaintMode: Graphics.PaintMode ← Graphics.SetPaintMode[context, invert];
path: Graphics.Path ← Graphics.NewPath[];
Graphics.MoveTo[path, x, handle.layoutGraphics.screenPage.ymin];
Graphics.LineTo[path, x, handle.layoutGraphics.screenPage.ymax];
Graphics.DrawStroke[context, path, marginThickness];
[] ← Graphics.SetPaintMode[context, oldPaintMode];
};
DrawHorizontalMargin: PROCEDURE[handle: StyleToolHandle, context: Graphics.Context, y: REAL]={
oldPaintMode: Graphics.PaintMode ← Graphics.SetPaintMode[context, invert];
path: Graphics.Path ← Graphics.NewPath[];
Graphics.MoveTo[path, handle.layoutGraphics.screenPage.xmin, y];
Graphics.LineTo[path, handle.layoutGraphics.screenPage.xmax, y];
Graphics.DrawStroke[context, path, marginThickness];
[] ← Graphics.SetPaintMode[context, oldPaintMode];
};
ChangePage: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, x, y: REAL] = {
for the different page boundaries we do the same procedure. First check that the new position will not cause the boundary to overlap with its "opposite" boundary. This means that if we are changing the right boundary, we first want to check that the new right boundary will not overlap the left boundary, this would not make sense and it would drive the typesetter crazy. If everything is OK, then change the position of the page boundary.
neverLand: REAL ← 5.0;
SELECT handle.layoutGraphics.currentPage FROM
 side  => IF (x - neverLand) > handle.layoutGraphics.screenPage.xmin THEN {
     IF x < handle.layoutGraphics.screenPage.xmax THEN
      -- indicates that we are making the page smaller so need to redraw the grid.
      DrawSmallerPageFromRight[handle, context, x]
     ELSE DrawLargerPageFromRight[handle, context, x];
     handle.layoutGraphics.screenPage.xmax ← x;
     UpdateValue[handle.layoutGraphics.pageWidth, ScreenToPageWidth[handle]];
     };
 top => IF (y - neverLand) > handle.layoutGraphics.screenPage.ymin THEN {
     IF y < handle.layoutGraphics.screenPage.ymax THEN
      -- indicates that we are making the page smaller so need to redraw the grid
      DrawSmallerPageFromTop[handle, context, y]
     ELSE DrawLargerPageFromTop[handle, context, y];
     handle.layoutGraphics.screenPage.ymax ← y;
     UpdateValue[handle.layoutGraphics.pageLength, ScreenToPageLength[handle]];
     };
ENDCASE;
};
DrawSmallerPageFromRight: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, x: REAL] = {
OPEN handle.layoutGraphics; -- for screenPage, screenRightMargin
tempBox, gridBox: Graphics.Box;
path: Graphics.Path ← Graphics.NewPath[];
tempBox.xmin ← x;
tempBox.xmax ← screenPage.xmax;
tempBox.ymin ← screenPage.ymin;
tempBox.ymax ← screenPage.ymax;
remove the old outline
Graphics.SetColor[context, Graphics.white];
DrawThreePageSides[context, tempBox];
white out the area to be redrawn and redraw the grid
gridBox ← tempBox;
gridBox.xmax ← tempBox.xmax + 1; -- since the page outline is 2 units wide
gridBox.ymax ← tempBox.ymax + 1;
DrawGrid[handle, context, FALSE, gridBox];
now redraw the right page boundary
Graphics.SetColor[context, Graphics.black];
Graphics.MoveTo[path, tempBox.xmin, tempBox.ymin];
Graphics.LineTo[path, tempBox.xmin, tempBox.ymax];
Graphics.DrawStroke[context, path, pageOutlineThickness];
now redraw the margin
IF (screenRightMargin - (tempBox.xmax - tempBox.xmin)) > screenPage.xmin THEN {
 DrawVerticalMargin[handle, context, screenRightMargin];-- erase old one
 screenRightMargin ← screenRightMargin - (tempBox.xmax - tempBox.xmin);
 DrawVerticalMargin[handle, context, screenRightMargin];
 };
};
DrawSmallerPageFromTop: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, y: REAL] = {
OPEN handle.layoutGraphics; -- for screenPage, screenTopMargin
tempBox, gridBox: Graphics.Box;
path: Graphics.Path ← Graphics.NewPath[];
tempBox.xmin ← screenPage.xmin;
tempBox.xmax ← screenPage.xmax;
tempBox.ymin ← y;
tempBox.ymax ← screenPage.ymax;
remove the old outline
Graphics.SetColor[context, Graphics.white];
DrawThreePageSidesFromTop[context, tempBox];
white out the area to be redrawn and redraw the grid
gridBox ← tempBox;
gridBox.xmax ← tempBox.xmax + 1; -- since the page outline is 2 units wide
gridBox.ymax ← tempBox.ymax + 1;
DrawGrid[handle, context, FALSE, gridBox];
now redraw the top page boundary
Graphics.SetColor[context, Graphics.black];
Graphics.MoveTo[path, tempBox.xmin, tempBox.ymin];
Graphics.LineTo[path, tempBox.xmax, tempBox.ymin];
Graphics.DrawStroke[context, path, pageOutlineThickness];
now redraw the margin
IF (screenTopMargin - (tempBox.ymax - tempBox.ymin)) > screenPage.ymin THEN {
 DrawHorizontalMargin[handle, context, screenTopMargin];-- erase old one
 screenTopMargin ← screenTopMargin - (tempBox.ymax - tempBox.ymin);
 DrawHorizontalMargin[handle, context, screenTopMargin];
 };
};
DrawLargerPageFromRight: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, x: REAL] = {
OPEN handle.layoutGraphics; -- for screenPage,screenTopMargin,screenBottomMargin,screenRightMargin
tempBox: Graphics.Box;
path: Graphics.Path ← Graphics.NewPath[];
tempBox.xmin ← screenPage.xmax;
tempBox.xmax ← x;
tempBox.ymin ← screenPage.ymin;
tempBox.ymax ← screenPage.ymax;
-- erase the old grid and old page boundary
Graphics.SetColor[context, Graphics.white];
Graphics.DrawBox[context, tempBox];
extend old top and bottom page boundaries and draw new right boundary
Graphics.SetColor[context, Graphics.black];
DrawThreePageSides[context, tempBox];
now redraw the right margin and extend others.
Graphics.MoveTo[path, tempBox.xmin, screenTopMargin];
Graphics.LineTo[path, tempBox.xmax, screenTopMargin];
Graphics.DrawStroke[context, path, marginThickness];
Graphics.MoveTo[path, tempBox.xmin, screenBottomMargin, TRUE];
Graphics.LineTo[path, tempBox.xmax, screenBottomMargin];
Graphics.DrawStroke[context, path, marginThickness];
DrawVerticalMargin[handle, context, screenRightMargin];-- erase old one
screenRightMargin ← screenRightMargin + (tempBox.xmax - tempBox.xmin);
DrawVerticalMargin[handle, context, handle.layoutGraphics.screenRightMargin];
};
DrawLargerPageFromTop: PROCEDURE [handle: StyleToolHandle, context: Graphics.Context, y: REAL] = {
OPEN handle.layoutGraphics; -- for screenPage, screenRightMargin, screenLeftMargin, screenBindingMargin
tempBox: Graphics.Box;
path: Graphics.Path ← Graphics.NewPath[];
tempBox.xmin ← screenPage.xmin;
tempBox.xmax ← screenPage.xmax;
tempBox.ymin ← screenPage.ymax-1;
tempBox.ymax ← y;
erase the old grid and old page boundary
Graphics.SetColor[context, Graphics.white];
Graphics.DrawBox[context, tempBox];
extend old left and right page boundaries and draw new top boundary
Graphics.SetColor[context, Graphics.black];
DrawThreePageSidesFromTop[context, tempBox];
now redraw the top margin and extend others.
Graphics.MoveTo[path, screenRightMargin, tempBox.ymin];
Graphics.LineTo[path, screenRightMargin, tempBox.ymax+1];
Graphics.DrawStroke[context, path, marginThickness];
Graphics.MoveTo[path, screenLeftMargin, tempBox.ymin, TRUE];
Graphics.LineTo[path, screenLeftMargin, tempBox.ymax+1];
Graphics.DrawStroke[context, path, marginThickness];
Graphics.MoveTo[path, screenBindingMargin, tempBox.ymin, TRUE];
Graphics.LineTo[path, screenBindingMargin, tempBox.ymax+1];
Graphics.DrawStroke[context, path, marginThickness];
DrawHorizontalMargin[handle,context, screenTopMargin];-- erase old one
screenTopMargin ← screenTopMargin + (tempBox.ymax - tempBox.ymin - 1);
DrawHorizontalMargin[handle, context, screenTopMargin];
};
DrawThreePageSides: PROCEDURE [context: Graphics.Context, box: Graphics.Box] = {
path: Graphics.Path ← Graphics.NewPath[];
Graphics.MoveTo[path, box.xmin, box.ymin];
Graphics.LineTo[path, box.xmax, box.ymin];
Graphics.LineTo[path, box.xmax, box.ymax];
Graphics.LineTo[path, box.xmin, box.ymax];
Graphics.DrawStroke[context, path, pageOutlineThickness];
};
DrawThreePageSidesFromTop: PROCEDURE [context: Graphics.Context, box: Graphics.Box] = {
path: Graphics.Path ← Graphics.NewPath[];
Graphics.MoveTo[path, box.xmin, box.ymin];
Graphics.LineTo[path, box.xmin, box.ymax];
Graphics.LineTo[path, box.xmax, box.ymax];
Graphics.LineTo[path, box.xmax, box.ymin];
Graphics.DrawStroke[context, path, pageOutlineThickness];
};
END.