SilKernelImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Tracy Larrabee: March 28, 1984 7:38:21 pm PST
Last Edited by Ken Pier, August 22, 1985 4:17:15 pm PDT
This file contains the top level procedures for the functioning of Sil. There are global initialization procedures, an init procedure that happens once per sil instance, some procedures which manage sil carets, and SilMain - the procedure that keeps a sil instance painting and accepting commands.
DIRECTORY
BiScrollers USING
[BiScroller, ClientDataOf, QuaBiScroller, QuaViewer],
Commander USING
[CommandProc, Register],
CommandTool USING
[ArgumentVector, Failed, Parse, StarExpansion],
Cursors USING
[CursorArray, CursorType, NewCursor],
FileNames USING
[CurrentWorkingDirectory, ResolveRelativePath, Directory],
Geom2D USING [Rect, Vec, ExtremaOfRect, Transform, id],
ImagerTransformation USING
[PostTranslate],
Process USING
[Detach, MsecToTicks, SetTimeout],
Real USING
[FixI],
Rope USING
[Concat, Length, ROPE],
ViewerOps USING
[PaintViewer],
TIPUser USING
[InstantiateNewTIPTable, TIPTable],
ViewerClasses USING
[DestroyProc, Viewer],
SilBiScrollers,
SilFile,
SilDisplay,
SilDisplayUtils,
SilKernel,
SilUserInput
;
SilKernelImpl: CEDAR MONITOR
IMPORTS BiScrollers, Cursors, Commander, CommandTool, FileNames, Geom2D, ImagerTransformation, Process, SilFile, ViewerOps, Real, Rope, SilBiScrollers, SilDisplay, SilKernel, SilUserInput, SilDisplayUtils, TIPUser
EXPORTS SilKernel = BEGIN
SilData: TYPE = SilKernel.SilData;
maxInstances: NAT ← 0; --keep non-decreasing count for viewer naming
numberOfSilInstances: NAT ← 0;
keep the number of instances so we know when to fire up a cursor blinking process (see StartSilInstance, below).
caretTimeout: CONDITION;
A global condition variable for the blinking of the carets
SilDataList: TYPE = LIST OF SilData;
silDatas: SilDataList ← NIL; --list of all sil's in the world
SilError: PUBLIC ERROR [explain: SilKernel.SilErrorType] = CODE;
InitSil: PROC [] = {
Initialize background datastructures which will be used by all Sil instances.
Prepare for the eventual creation of Sil viewers:
MakeSilClass[];
Initialize All the Sil Library data structures:
SilFile.InitSil[];
SilDisplay.InitSil[];
};
StartSilInstance: PUBLIC ENTRY PROC [fileName: Rope.ROPE] = {
Initialize everything that will be needed to start a bare Sil open on fileName (the fileName may be an empty rope).
data: SilData ← NEW[SilKernel.SilDataObject];
viewer: ViewerClasses.Viewer ← SilDisplay.InitSilDisplayInstance[data, fileName, maxInstances ! SilKernel.SilError => GOTO NotFound];
ViewerOps.PaintViewer[viewer, all]; -- InitSilDisplayInstance paints viewer immediately before
silDatas ← CONS[data, silDatas]; -- save models for repainting if fonts change
TRUSTED {Process.Detach [FORK SilMain[data, viewer] ]};
IF numberOfSilInstances = 0 THEN
TRUSTED {Process.Detach [FORK Carets[] ]};
numberOfSilInstances ← numberOfSilInstances + 1;
maxInstances ← maxInstances + 1;
EXITS
NotFound => NULL;
};
RepaintSilViewers: PUBLIC ENTRY PROC = {
This routine is called after swapping fonts between display and printing fonts. Since there is only one font set for all Sil viewers, all viewers are erased, their string bounding boxes recalculated, and then they are repainted with the new fonts.
fudge: REAL = 10.0; -- fudge factor for EraseArea needed to catch all descenders in fonts
xMin, yMin, xMax, yMax: INTEGER ← 0;
rect: Geom2D.Rect ← [0.0, 0.0, 0.0, 0.0];
FOR d: SilDataList ← silDatas, d.rest UNTIL d=NIL DO
IF d.first.mainProcessShouldStop THEN LOOP; -- already deleted Sil viewer
rect ← SilDisplayUtils.BoundingBoxOfModel[dData: d.first.displayData, model: d.first.model, mapArea: FALSE]; -- Returns RECORD [x, y, w, h: REAL]
Next loop recalculates string bounding boxes with new font metrics
FOR s: SilFile.SilObject ← SilFile.GetObjectList[d.first.model, fgnd], s.rest UNTIL s=NIL DO
IF s.first.font IN SilFile.InternalPresetFonts THEN {
[xMin: , yMin: , xMax: xMax, yMax: yMax] ← SilDisplayUtils.BoundingBoxOfObject[d.first.model, s.first];
s.first.xMax ← s.first.xMin+xMax;
s.first.yMax ← s.first.yMin+yMax;
};
ENDLOOP;
IF NOT SilUserInput.GetBiScroller[d.first.uiData].QuaViewer[].parent.iconic THEN {
SilUserInput.Enque[[EraseArea[Real.FixI[rect.x-fudge], Real.FixI[rect.y-fudge], Real.FixI[rect.x+rect.w+fudge], Real.FixI[rect.y+rect.h+fudge]]], d.first.uiData];
rect ← SilDisplayUtils.BoundingBoxOfModel[dData: d.first.displayData, model: d.first.model, mapArea: FALSE]; --get NEW bounding box, which has changed due to new fonts
SilUserInput.Enque[[MergeArea[Real.FixI[rect.x-fudge], Real.FixI[rect.y-fudge], Real.FixI[rect.x+rect.w+fudge], Real.FixI[rect.y+rect.h+fudge]]], d.first.uiData];
};
ENDLOOP;
now fix up the selection bounds as well
SilFile.EvaluateSelection[];
};
SilMain: PROC [data: SilData, viewer: ViewerClasses.Viewer] = {
the PaintProc actually processes the user input !! viewer is the ButtonedContainer which contains the BiScroller; viewer.parent is the Abutter which contains everything
WHILE NOT data.mainProcessShouldStop DO
IF SilUserInput.InputAvailable[data.uiData] THEN
IF NOT viewer.parent.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: client, clearClient: FALSE, whatChanged: $UserInput] ELSE NULL
ELSE IF SilUserInput.ShouldChangeCaret[data.uiData] THEN {
IF NOT viewer.parent.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: client, clearClient: FALSE, whatChanged: $Carets];
SilUserInput.CaretHasChanged[data.uiData];
}
ELSE IF SilDisplay.NeedRebuild[data.displayData] THEN
IF NOT viewer.parent.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: client, clearClient: FALSE, whatChanged: $Rebuild] ELSE SilDisplay.CancelRebuild[data.displayData]
ELSE
SilUserInput.AwaitUserInput[data.uiData];
ENDLOOP;
};
Carets: PROC [] = { --Carets is forked so is not an ENTRY Proc
Set information which will cause the carets (the mark and the origin) to blink appropriately until the destroy proc tells us that we neednt do it anymore.
quarterSec: INTEGER = 250; --(msecs)
SetCaretBlinkInterval[quarterSec];
WHILE numberOfSilInstances > 0 DO
RestCaret[];
SilDisplay.CaretBlink[];
ENDLOOP;
};
SetCaretBlinkInterval: ENTRY PROC [msecs: INTEGER] = {
Make it so the carets will blink every msecs milliseconds.
ENABLE UNWIND => NULL;
TRUSTED {Process.SetTimeout[@caretTimeout, Process.MsecToTicks[msecs]];};
};
RestCaret: ENTRY PROC [] = {
Pause for the appropriate time so that the carets will blink pleasingly.
ENABLE UNWIND => NULL;
WAIT caretTimeout;
};
SilDestroy: ViewerClasses.DestroyProc = {
[self: Viewer]
SilEntryDestroy[self: self];
};
SilEntryDestroy: ENTRY PROC [self: ViewerClasses.Viewer] = {
Perform the correct actions upon destruction of a Sil instance. Called when viewer
is destroyed.
data: SilData ← NARROW[BiScrollers.QuaBiScroller[self].ClientDataOf[]];
data.mainProcessShouldStop ← TRUE;
SilUserInput.DestroyUserInput[data.uiData];
numberOfSilInstances ← numberOfSilInstances - 1;
};
SilExtremaProc: PROC [clientData: REF ANY, direction: Geom2D.Vec] RETURNS [min, max: Geom2D.Vec] --BiScrollers.ExtremaProc-- = {
This proc is required by BiScrollers to return the extremes of the displayed data in this model
data: SilData ← NARROW[clientData];
area: Geom2D.Rect;
area ← SilDisplayUtils.BoundingBoxOfModel[dData: data.displayData, model: data.model, mapArea: TRUE];
[min, max] ← Geom2D.ExtremaOfRect[r: area, n: direction];
};
SilBasicTransformProc: PROC [bs: BiScrollers.BiScroller] RETURNS [t: Geom2D.Transform] --BiScrollers.TransformGenerator-- = {
iv: ViewerClasses.Viewer ← bs.style.QuaViewer[bs: bs, inner: TRUE];
t ← Geom2D.id.PostTranslate[[0, iv.ch]];
Make client <0,0> appear at the upper left corner of viewer.
};
MakeSilClass: PROC [] = {
Set up things so Viewers will know which procedures to call after interesting event.
myCursorBits: Cursors.CursorArray = [177700B, 177600B, 177400B, 177000B, 177400B, 177600B, 177700B, 167740B, 143760B, 101770B, 774B, 376B, 177B, 76B, 34B, 10B];
myCursor: Cursors.CursorType ← Cursors.NewCursor[myCursorBits];
tipTable: TIPUser.TIPTable ← TIPUser.InstantiateNewTIPTable["Sil.tip"];
SilUserInput.InitTipTable[tipTable];
SilBiScrollers.silClass ←
SilBiScrollers.bsStyle.NewBiScrollerClass
[[flavor: $Sil,
extrema: SilExtremaProc,
notify: SilUserInput.SilNotify,
paint: SilDisplay.SilPaintProc, --call SilPaint when viewer needs to be repainted
modify: SilUserInput.SilModifyInputFocus, --Call when input focus is taken
destroy: SilDestroy,
finish: LIST[$Exit],
save: SilDisplay.SilSaveProc,
tipTable: tipTable,
cursor: myCursor,
mayStretch: FALSE,
offsetsMustBeIntegers: TRUE,
preferIntegerCoefficients: TRUE, -- integer coefficients in transform
vanilla: SilBasicTransformProc, --proc which provides the basic transform for BiScrollers
preserve: [X: 0, Y: 1] --this specifies point that stays fixed when viewer size changes
]];
};
StartSilCommand: Commander.CommandProc = {
[cmd: Handle]
argv: CommandTool.ArgumentVector;
name, dir: Rope.ROPE;
CommandTool.StarExpansion[cmd];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => CONTINUE; ];
IF argv = NIL THEN RETURN[$Failure, "Sil initialization procedure is confused"];
IF argv.argc < 2 THEN { --Sil called with no file name given.
StartSilInstance[fileName: ""];
RETURN;
};
FOR i: NAT IN [1..argv.argc) DO --open a sil window on each file given
name ← FileNames.ResolveRelativePath[argv[i]];
dir ← FileNames.Directory[name];
IF Rope.Length[dir] = 0 THEN name ← Rope.Concat[FileNames.CurrentWorkingDirectory[], name];
StartSilInstance[fileName: name];
ENDLOOP;
};
When Sil is run, the library data structures will be initialized and the sil command will be registered with the system.
InitSil[];
Commander.Register[key: "Sil", proc: StartSilCommand,
doc: "Create a Window Sil Instance" ];
END.