ViewerOpsImplB.mesa; Written by Maxwell
Edited by Maxwell, May 18, 1983 9:19 am
Edited by Paul Rovner, June 16, 1983 9:46 am
Edited by Doug Wyatt, December 13, 1983 5:59 pm
DIRECTORY
Carets USING [ResumeCarets, SuspendCarets],
Cursors USING [InvertCursor],
InputFocus USING [GetInputFocus, SetInputFocus],
Menus USING [CopyMenu, Menu],
MessageWindow USING [Append, Blink],
Process USING [GetPriority, Priority, priorityForeground, SetPriority],
RefTab USING [Create, Fetch, Ref, Store],
Rope USING [Compare, ROPE],
TIPUser USING [TIPScreenCoords],
ViewerEvents USING [ProcessEvent],
ViewerOps,
ViewerLocks,
ViewerClasses,
ViewerClassesExtras USING [],
ViewerSpecs,
WindowManager USING [colorDisplayOn],
WindowManagerPrivate;
ViewerOpsImplB: CEDAR PROGRAM
IMPORTS Carets, Cursors, InputFocus, Menus, MessageWindow, Process, RefTab, Rope, ViewerEvents, ViewerLocks, ViewerOps, WindowManager, WindowManagerPrivate
EXPORTS ViewerOps, ViewerClassesExtras
SHARES Menus, ViewerEvents =
BEGIN OPEN ViewerClasses, ViewerSpecs, ViewerOps, WindowManager;
ROPE: TYPE = Rope.ROPE;
classTable: RefTab.Ref ← RefTab.Create[mod:50]; -- hold viewer classes
RegisterViewerClass: PUBLIC PROC [flavor: ViewerFlavor, class: ViewerClass] = {
class.flavor ← flavor;
[] ← RefTab.Store[classTable, flavor, class]};
let the Viewer package know about a new class of viewer.
FetchViewerClass: PUBLIC PROC [flavor: ViewerFlavor] RETURNS [ViewerClass] =
{IF flavor = $TypeScript THEN flavor ← $Typescript;
RETURN[NARROW[RefTab.Fetch[classTable, flavor].val]]};
class information from an existing viewer class.
EnumerateViewers: PUBLIC PROC [enum: EnumProc] = BEGIN
Safe in that caller can move the enumerated viewer in the tree in which case all viewers get seen at least once
FOR c: Column DECREASING IN Column DO-- decreasing so will try static viewers last
v: Viewer ← WindowManagerPrivate.rootViewerTree[c];
next: Viewer;
UNTIL v=NIL DO
next ← v.sibling;
IF ~enum[v] THEN RETURN;
v ← next;
ENDLOOP;
ENDLOOP;
END;
EnumerateChildren: PUBLIC PROC [viewer: Viewer, enum: EnumProc] = BEGIN
v: Viewer ← viewer.child;
next: Viewer;
WHILE v#NIL DO
next ← v.sibling;
EnumerateChildren[v, enum];
IF ~enum[v] THEN RETURN;
v ← next;
ENDLOOP;
END;
FindViewer: PUBLIC PROC [name: Rope.ROPE] RETURNS [viewer: Viewer] = BEGIN
MatchName: EnumProc = BEGIN
IF Rope.Compare[name, v.name, FALSE]=equal THEN BEGIN
viewer ← v;
RETURN[FALSE];
END
ELSE RETURN[TRUE];
END;
EnumerateViewers[MatchName];
END;
SaveAborted: PUBLIC ERROR[error: ROPE] = CODE; -- exported to ViewerClassesExtras
SaveViewer: PUBLIC PROC [viewer: Viewer] = BEGIN
list: LIST OF Viewer;
errorMessage: ROPENIL;
LockedStart: PROC = {
FOR v: Viewer ← viewer, v.link DO
v.saveInProgress ← TRUE;
PaintViewer[v, caption, FALSE];
list ← CONS[v, list];
IF v.link = NIL OR v.link = viewer THEN EXIT;
ENDLOOP};
LockedStop: PROC = {
FOR saved: LIST OF Viewer ← list, saved.rest UNTIL saved = NIL DO
v: Viewer = saved.first;
IF v.destroyed THEN LOOP;
IF errorMessage=NIL THEN v.newVersion ← v.newFile ← FALSE;
v.saveInProgress ← FALSE;
PaintViewer[v, caption, FALSE];
[] ← ViewerEvents.ProcessEvent[save, v, FALSE];
ENDLOOP};
FOR v: Viewer ← viewer, v.link DO
IF ViewerEvents.ProcessEvent[save, v, TRUE].abort THEN RETURN;
IF v.link = NIL OR v.link = viewer THEN EXIT;
ENDLOOP;
Not called under locks so viewers can be saved when the column is locked.
LockedStart[];
IF viewer.class.save=NIL THEN errorMessage ← "no save procedure in viewer class"
ELSE viewer.class.save[viewer ! SaveAborted => { errorMessage ← error; CONTINUE }];
IF errorMessage#NIL THEN {
MessageWindow.Append["Can't SAVE -- ", TRUE];
MessageWindow.Append[errorMessage];
MessageWindow.Blink[];
};
LockedStop[];
END;
RestoreViewer: PUBLIC PROC [viewer: Viewer] = BEGIN
DoOne: PROC [v: Viewer] = INLINE BEGIN
KillInputFocus[v];
IF v.class.init # NIL THEN v.class.init[v];
v.newVersion ← v.newFile ← FALSE;
PaintViewer[v, all];
END;
DoOne[viewer];
IF viewer.link#NIL THEN
FOR v: Viewer ← viewer.link, v.link UNTIL v=viewer DO DoOne[v]; ENDLOOP;
END;
KillInputFocus: PROC [viewer: Viewer] = BEGIN
focus: Viewer ← InputFocus.GetInputFocus[].owner;
WHILE focus # NIL DO
IF focus # viewer THEN focus ← focus.parent
ELSE {InputFocus.SetInputFocus[]; EXIT};
ENDLOOP;
END;
SetMenu: PUBLIC PROC [viewer: Viewer, menu: Menus.Menu, paint: BOOLTRUE] = BEGIN
sameSize: BOOL = (menu#NIL AND viewer.menu#NIL AND
viewer.menu.linesUsed=menu.linesUsed);
IF viewer.iconic THEN paint ← FALSE;
IF viewer.parent#NIL THEN ERROR;
viewer.menu ← IF menu=NIL THEN NIL ELSE Menus.CopyMenu[menu];
IF ~sameSize AND paint THEN
EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh];
IF paint THEN PaintViewer[viewer, IF sameSize THEN menu ELSE all];
END;
IndicateNewVersion: PUBLIC PROC [viewer: Viewer] = BEGIN
link1, link2: Viewer;
LockedNewVersion: PROC = {
FOR v: Viewer ← viewer, v.link DO
v.newVersion ← TRUE;
PaintViewer[v, caption];
[] ← ViewerEvents.ProcessEvent[edit, v, FALSE];
IF v.link = NIL OR v.link = viewer THEN EXIT;
ENDLOOP};
FOR v: Viewer ← viewer, v.link DO
IF ViewerEvents.ProcessEvent[edit, v, TRUE].abort THEN RETURN;
IF v.link = NIL OR v.link = viewer THEN EXIT;
ENDLOOP;
IF viewer # NIL THEN link1 ← viewer.link;
IF link1 # NIL THEN link2 ← link1.link;
ViewerLocks.CallUnderReadLocks[LockedNewVersion, viewer, link1, link2];
END;
SetNewFile: PUBLIC PROC [viewer: Viewer] = BEGIN
DoOne: PROC [v: Viewer] = BEGIN
v.newFile ← TRUE;
PaintViewer[v, caption];
END;
DoOne[viewer];
IF viewer.link#NIL THEN
FOR v: Viewer ← viewer.link, v.link UNTIL v=viewer DO DoOne[v]; ENDLOOP;
END;
SetOpenHeight: PUBLIC PROC [viewer: Viewer, clientHeight: INTEGER] = BEGIN
overhead: INTEGER ← captionHeight + (IF viewer.border THEN windowBorderSize ELSE 0);
IF viewer.menu#NIL THEN overhead ←
overhead+(viewer.menu.linesUsed*menuHeight)+menuBarHeight;
viewer.openHeight ← clientHeight+overhead;
END;
SaveAllEdits: PUBLIC PROC = BEGIN
poor man's crash recovery
old: Process.Priority = Process.GetPriority[];
Save: EnumProc = BEGIN
Cursors.InvertCursor[];
IF (v.newVersion OR v.newFile) AND v.class.save # NIL
THEN v.class.save[v, TRUE ! ANY => CONTINUE];
v.newVersion ← v.newFile ← FALSE;
IF v.icon=dirtyDocument THEN v.icon ← document;
IF v.link#NIL THEN FOR t: Viewer ← v.link, t.link UNTIL t=v DO
t.newVersion ← t.newFile ← FALSE;
ENDLOOP;
Cursors.InvertCursor[];
RETURN[TRUE];
END;
IF old>Process.priorityForeground THEN TRUSTED BEGIN-- called from CoPilot
Process.SetPriority[Process.priorityForeground];
END;
EnumerateViewers[Save];
IF old>Process.priorityForeground THEN TRUSTED BEGIN-- called from CoPilot
Process.SetPriority[old];
END;
END;
PaintEverything: PUBLIC PROC = BEGIN OPEN Process;
ResetPaintCache[];
Carets.SuspendCarets[];
GreyScreen[0, 0, 9999, 9999, FALSE, FALSE];
ComputeColumn[static];
ComputeColumn[left];
ComputeColumn[right];
IF WindowManager.colorDisplayOn THEN ComputeColumn[color];
WaitForPaintingToFinish[];
Carets.ResumeCarets[];
END;
UserToScreenCoords: PUBLIC PROC [self: Viewer, vx, vy: INTEGER ← 0]
RETURNS [sx, sy: INTEGER] = BEGIN
invert: BOOL;
UNTIL self=NIL DO-- compute enclosing viewer offsets
invert ← IF self.parent=NIL THEN self.class.coordSys=top
ELSE self.class.coordSys#self.parent.class.coordSys;
vx ← vx + self.cx;
IF invert THEN vy ← Top2Bottom[self, vy];
vy ← vy + self.cy;
self ← self.parent;
ENDLOOP;
RETURN [vx, vy];
END;
MouseInViewer: PUBLIC PROC [tsc: TIPUser.TIPScreenCoords]
RETURNS [viewer: Viewer, client: BOOL] = BEGIN
ENABLE UNWIND => NULL;
x: INTEGER ← tsc.mouseX;
y: INTEGER ← tsc.mouseY;
invert: BOOL;
TopLevelHit: PROC RETURNS [Viewer] = BEGIN
FOR c: Column DECREASING IN Column DO
FOR v: Viewer ← WindowManagerPrivate.rootViewerTree[c], v.sibling UNTIL v=NIL DO
IF ViewerHit[v] THEN RETURN[v];
ENDLOOP;
REPEAT FINISHED => RETURN[NIL];
ENDLOOP;
END;
ViewerHit: PROC [v: Viewer] RETURNS [BOOL] = INLINE
{RETURN[x IN [v.wx..v.wx+v.ww) AND y IN [v.wy..v.wy+v.wh) AND
(IF tsc.color THEN (v.column=color AND ~v.iconic) ELSE (v.column#color OR v.iconic))]};
Client: PROC RETURNS [BOOL] = INLINE
{RETURN[y IN [viewer.cy..viewer.cy+viewer.ch) AND
x IN [viewer.cx..viewer.cx+viewer.cw)]};
CheckViewersChildren: PROC [v: Viewer] RETURNS [BOOL] = BEGIN
ex, ey: INTEGER;
EmbeddedViewerHit: PROC [v: Viewer] RETURNS [BOOL] = INLINE
{RETURN[ex IN [v.wx..v.wx+v.ww) AND ey IN [v.wy..v.wy+v.wh)]};
ex ← x - v.cx;
ey ← y - v.cy;
invert ← IF v.parent=NIL THEN v.class.coordSys=top
ELSE v.class.coordSys#v.parent.class.coordSys;
IF invert THEN ey ← Bottom2Top[v, ey];
FOR v ← v.child, v.sibling UNTIL v=NIL DO
IF EmbeddedViewerHit[v] THEN BEGIN
embedded frames are client info relative
x ← ex;
y ← ey;
viewer ← v;
RETURN[TRUE];
END;
ENDLOOP;
RETURN[FALSE];
END;
IF (viewer ← TopLevelHit[]) = NIL THEN RETURN [NIL, FALSE];
IF viewer.child#NIL AND ~viewer.iconic AND Client[] THEN WHILE viewer.child#NIL
AND CheckViewersChildren[viewer] DO ENDLOOP;
client ← Client[];
invert ← IF viewer.parent=NIL THEN client AND viewer.class.coordSys=top
ELSE viewer.class.coordSys#viewer.parent.class.coordSys;
overwrite screen coords record
tsc.mouseX ← x - (IF client THEN viewer.cx ELSE viewer.wx);
y ← y - (IF client THEN viewer.cy ELSE viewer.wy);
tsc.mouseY ← IF ~invert THEN y ELSE IF client THEN Top2Bottom[viewer, y] ELSE viewer.wh-y;
END;
END . . .