ViewerOpsImplB.mesa; Written by Maxwell
Edited by Maxwell, May 18, 1983 9:19 am
Last Edited by: Pausch, August 17, 1983 12:01 pm
Last Edited by: Wyatt, October 24, 1983 10:27 am
DIRECTORY
Atom USING [GetPropFromList, PutPropOnList],
Carets USING [ResumeCarets, SuspendCarets],
Cursors USING [InvertCursor],
InputFocus USING [GetInputFocus, SetInputFocus],
Process USING [GetPriority, Priority, priorityForeground, SetPriority],
RefTab USING [Create, Fetch, Ref, Store],
Rope USING [Compare, ROPE],
RTOS USING [GetCurrent, RegisterCedarProcess, UnregisterCedarProcess],
TIPUser USING [TIPScreenCoords],
ViewerEvents USING [ProcessEvent],
ViewerOps,
ViewerLocks,
MenusPrivate USING [ViewerMenus],
ViewerClasses,
ViewerSpecs,
WindowManager USING [colorDisplayOn],
WindowManagerPrivate;
ViewerOpsImplB: CEDAR PROGRAM
IMPORTS Atom, Carets, Cursors, InputFocus, Process, RefTab, Rope, RTOS, ViewerEvents, ViewerLocks, ViewerOps, WindowManager, WindowManagerPrivate
EXPORTS ViewerOps
SHARES ViewerEvents
= BEGIN OPEN ViewerClasses, ViewerSpecs, ViewerOps;
classTable: RefTab.Ref ← RefTab.Create[mod:50];
-- hold viewer classes
RegisterViewerClass:
PUBLIC
PROC [flavor: ViewerFlavor, class: ViewerClass,
superClassFlavor: ViewerFlavor ← NIL] = {
let the Viewer package know about a new class of viewer.
class.flavor ← flavor;
IF superClassFlavor#
NIL
THEN {
superClass: ViewerClass = FetchViewerClass[superClassFlavor];
class.parent ← superClass;
IF class.notify=NIL THEN class.notify ← superClass.notify;
IF class.paint=NIL THEN class.paint ← superClass.paint;
IF class.modify=NIL THEN class.modify ← superClass.modify;
IF class.destroy=NIL THEN class.destroy ← superClass.destroy;
IF class.copy=NIL THEN class.copy ← superClass.copy;
IF class.set=NIL THEN class.set ← superClass.set;
IF class.get=NIL THEN class.get ← superClass.get;
IF class.save=NIL THEN class.save ← superClass.save;
IF class.scroll=NIL THEN class.scroll ← superClass.scroll;
IF class.hscroll=NIL THEN class.hscroll ← superClass.hscroll;
IF class.caption=NIL THEN class.caption ← superClass.caption;
};
[] ← RefTab.Store[classTable, flavor, class]
};
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.
IsClass:
PUBLIC
PROC [viewer: Viewer, class: ViewerFlavor]
RETURNS[
BOOL] = {
flavor: ViewerFlavor = class; -- should change the parameter name to flavor
FOR class: ViewerClass ← viewer.class, class.parent
UNTIL class=
NIL
DO
IF class.flavor=flavor THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
EnumerateViewers:
PUBLIC
PROC [enum: EnumProc] = {
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;
};
EnumerateChildren:
PUBLIC
PROC [viewer: Viewer, enum: EnumProc] = {
v: Viewer ← viewer.child;
next: Viewer;
WHILE v#
NIL
DO
next ← v.sibling;
EnumerateChildren[v, enum];
IF ~enum[v] THEN RETURN;
v ← next;
ENDLOOP;
};
FindViewer:
PUBLIC
PROC [name: Rope.
ROPE]
RETURNS [viewer: Viewer] = {
MatchName: EnumProc = {
IF Rope.Compare[name, v.name,
FALSE]=equal
THEN {
viewer ← v;
RETURN[FALSE];
}
ELSE RETURN[TRUE];
};
EnumerateViewers[MatchName];
};
SaveViewer:
PUBLIC
PROC [viewer: Viewer] = {
list: LIST OF Viewer;
ok: BOOL ← FALSE;
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;
v.saveInProgress ← FALSE;
IF ok THEN v.newVersion ← v.newFile ← 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 ok ← viewer.class.save[viewer];
LockedStop[];
};
RestoreViewer:
PUBLIC
PROC [viewer: Viewer] = {
DoOne:
PROC [v: Viewer] =
INLINE {
KillInputFocus[v];
IF v.class.init # NIL THEN v.class.init[v];
v.newVersion ← v.newFile ← FALSE;
PaintViewer[v, all];
};
DoOne[viewer];
IF viewer.link#
NIL
THEN
FOR v: Viewer ← viewer.link, v.link UNTIL v=viewer DO DoOne[v]; ENDLOOP;
};
KillInputFocus:
PROC [viewer: Viewer] = {
focus: Viewer ← InputFocus.GetInputFocus[].owner;
WHILE focus #
NIL
DO
IF focus # viewer
THEN focus ← focus.parent
ELSE {InputFocus.SetInputFocus[]; EXIT};
ENDLOOP;
};
IndicateNewVersion:
PUBLIC
PROC [viewer: Viewer] = {
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];
};
SetNewFile:
PUBLIC
PROC [viewer: Viewer] = {
DoOne:
PROC [v: Viewer] = {
v.newFile ← TRUE;
PaintViewer[v, caption];
};
DoOne[viewer];
IF viewer.link#
NIL
THEN
FOR v: Viewer ← viewer.link, v.link UNTIL v=viewer DO DoOne[v]; ENDLOOP;
};
SetOpenHeight:
PUBLIC
PROC [viewer: Viewer, clientHeight:
INTEGER] = {
overhead: INTEGER ← captionHeight + (IF viewer.border THEN windowBorderSize ELSE 0);
IF viewer.menus#
NIL
THEN overhead ←
overhead+NARROW[viewer.menus, MenusPrivate.ViewerMenus].h+menuBarHeight;
viewer.openHeight ← clientHeight+overhead;
};
SaveAllEdits:
PUBLIC
PROC = {
poor man's crash recovery
old: Process.Priority = Process.GetPriority[];
Save: EnumProc = {
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];
};
IF old>Process.priorityForeground
THEN
TRUSTED {
-- called from CoPilot
Process.SetPriority[Process.priorityForeground];
RTOS.RegisterCedarProcess[RTOS.GetCurrent[]];
};
EnumerateViewers[Save];
IF old>Process.priorityForeground
THEN
TRUSTED {
-- called from CoPilot
RTOS.UnregisterCedarProcess[RTOS.GetCurrent[]];
Process.SetPriority[old];
};
};
PaintEverything:
PUBLIC
PROC = {
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[];
};
OldUserToScreenCoords: PUBLIC PROC [self: Viewer, vx, vy: INTEGER ← 0]
RETURNS [sx, sy: INTEGER] = {
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];
};
UserToScreenCoords:
PUBLIC
PROC[self: Viewer, vx, vy:
INTEGER ← 0]
RETURNS[sx, sy: INTEGER] = {
UNTIL self=
NIL
DO
-- compute enclosing viewer offsets
vx ← vx + self.cx;
vy ← vy + self.cy;
self ← self.parent;
ENDLOOP;
RETURN [vx, vy];
};
OldMouseInViewer: PUBLIC PROC [tsc: TIPUser.TIPScreenCoords]
RETURNS [viewer: Viewer, client: BOOL] = {
ENABLE UNWIND => NULL;
x: INTEGER ← tsc.mouseX;
y: INTEGER ← tsc.mouseY;
invert: BOOL;
TopLevelHit: PROC RETURNS [Viewer] = {
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;
};
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] = {
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 {
embedded frames are client info relative
x ← ex;
y ← ey;
viewer ← v;
RETURN[TRUE];
};
ENDLOOP;
RETURN[FALSE];
};
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;
};
Display: TYPE = {default, color};
MouseInViewer: PUBLIC PROC[tsc: TIPUser.TIPScreenCoords]
RETURNS[viewer: Viewer ← NIL, client: BOOL ← FALSE] = {
ENABLE UNWIND => NULL;
TopLevelHit: PROC[x, y: INTEGER, display: Display] RETURNS[Viewer] = {
FOR column: Column DECREASING IN Column DO
head: Viewer = WindowManagerPrivate.rootViewerTree[column]; -- head of column list
FOR v: Viewer ← head, v.sibling UNTIL v=NIL DO
viewerDisplay: Display = (IF v.column=color AND NOT v.iconic THEN color ELSE default);
IF display=viewerDisplay
AND x IN [v.wx..v.wx+v.ww) AND y IN [v.wy..v.wy+v.wh) THEN RETURN[v];
ENDLOOP;
ENDLOOP;
RETURN[NIL];
};
Client: PROC[v: Viewer, x, y: INTEGER] RETURNS [BOOL] = INLINE {
RETURN[y IN [v.cy..v.cy+v.ch) AND x IN [v.cx..v.cx+v.cw)] };
ChildHit: PROC[parent: Viewer, x, y: INTEGER] RETURNS[Viewer] = {
FOR v: Viewer ← parent.child, v.sibling UNTIL v=NIL DO
IF x IN[v.wx..v.wx+v.ww) AND y IN[v.wy..v.wy+v.wh) THEN RETURN[v];
ENDLOOP;
RETURN[NIL];
};
x: INTEGER ← tsc.mouseX;
y: INTEGER ← tsc.mouseY;
parentCoordSys: CoordSys ← bottom;
viewer ← TopLevelHit[x, y, (IF tsc.color THEN color ELSE default)];
IF viewer=NIL THEN RETURN[NIL, FALSE];
WHILE (client ← Client[viewer, x, y]) AND NOT viewer.iconic AND viewer.child#NIL DO
embedded viewers are relative to client area of parent
coordSys: CoordSys = viewer.class.coordSys;
ex: INTEGER = x-viewer.cx;
ey: INTEGER = IF coordSys=parentCoordSys THEN y-viewer.cy
ELSE viewer.ch-(y-viewer.cy);
child: Viewer = ChildHit[viewer, ex, ey];
IF child=NIL THEN EXIT;
viewer ← child; x ← ex; y ← ey;
parentCoordSys ← coordSys;
ENDLOOP;
Overwrite screen coords record (Yecchh! — DKW)
{ invert: BOOL = IF viewer.parent=NIL THEN client AND viewer.class.coordSys=top
ELSE viewer.class.coordSys#viewer.parent.class.coordSys;
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 viewer.ch-y
ELSE viewer.wh-y;
};
};
MouseInViewer:
PUBLIC
PROC[tsc: TIPUser.TIPScreenCoords]
RETURNS[viewer: Viewer ← NIL, client: BOOL ← FALSE] = {
ENABLE UNWIND => NULL;
TopLevelHit:
PROC[x, y:
INTEGER, d: Display]
RETURNS[Viewer] = {
FOR column: Column
DECREASING
IN Column
DO
head: Viewer = WindowManagerPrivate.rootViewerTree[column]; -- head of column list
FOR v: Viewer ← head, v.sibling
UNTIL v=
NIL
DO
vd: Display = (IF v.column=color AND NOT v.iconic THEN color ELSE default);
IF d=vd AND x IN[v.wx..v.wx+v.ww) AND y IN[v.wy..v.wy+v.wh) THEN RETURN[v];
ENDLOOP;
ENDLOOP;
RETURN[NIL];
};
Client:
PROC[v: Viewer, x, y:
INTEGER]
RETURNS [
BOOL] =
INLINE {
RETURN[y IN [v.cy..v.cy+v.ch) AND x IN [v.cx..v.cx+v.cw)]
};
ChildHit:
PROC[parent: Viewer, x, y:
INTEGER]
RETURNS[Viewer] = {
FOR v: Viewer ← parent.child, v.sibling
UNTIL v=
NIL
DO
IF x IN[v.wx..v.wx+v.ww) AND y IN[v.wy..v.wy+v.wh) THEN RETURN[v];
ENDLOOP;
RETURN[NIL];
};
x: INTEGER ← tsc.mouseX;
y: INTEGER ← tsc.mouseY;
viewer ← TopLevelHit[x, y, (IF tsc.color THEN color ELSE default)];
IF viewer=NIL THEN RETURN[NIL, FALSE];
WHILE (client ← Client[viewer, x, y])
AND
NOT viewer.iconic
AND viewer.child#
NIL
DO
embedded viewers are relative to client area of parent
ex: INTEGER = x-viewer.cx;
ey: INTEGER = y-viewer.cy;
child: Viewer = ChildHit[viewer, ex, ey];
IF child=NIL THEN EXIT;
viewer ← child; x ← ex; y ← ey;
ENDLOOP;
Overwrite screen coords record (Yecchh! — DKW)
IF client THEN { tsc.mouseX ← x-viewer.cx; tsc.mouseY ← y-viewer.cy }
ELSE { tsc.mouseX ← x-viewer.wx; tsc.mouseY ← y-viewer.wy };
};
AddProp:
PUBLIC
PROC [viewer: Viewer, prop:
ATOM, val:
REF
ANY] = {
viewer.props ← Atom.PutPropOnList[viewer.props, prop, val]} ;
Associate a property with a viewer
FetchProp:
PUBLIC
PROC [viewer: Viewer, prop:
ATOM]
RETURNS [val:
REF
ANY] = {
RETURN[Atom.GetPropFromList[viewer.props, prop]]} ;
Get back a property associated with a viewer
SetMenu: PUBLIC PROC [viewer: Viewer, menu: Menus.Menu, paint: BOOL ← TRUE] = {
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.