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],
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;
};
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.