GEditViewerImpl.mesa; Edited by McGregor on December 15, 1982 1:04 pm
DIRECTORY
CIFS USING [Error],
EditSpan USING [Delete, InsertOtherNode],
Directory USING [Error, LookupUnlimited, RemoveFile, Rename],
File USING [Capability, Delete, nullCapability],
GEditClasses,
GEditIO USING [MyParent],
GEditMotion USING [Move],
GEditPaint USING [GEditPainter],
GEditSelect,
GEditViewer,
Graphics USING [Context, PaintMode, SetCP, SetPaintMode],
GraphicsOps USING [BitmapRef, BitmapRep, DrawBitmap],
InputFocus USING [GetInputFocus, SetInputFocus],
Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc, MouseButton],
MessageWindow USING [Append, Blink],
OtherNode USING [CopyInfo, Register],
Process USING [Detach],
PutGet USING [FromFile, ToFile],
RefTab USING [Create, Fetch, Ref, Store],
Rope USING [Cat, ROPE, ToRefText],
SafeStorage USING [NewZone],
TextEdit USING [DocFromNode],
TextNode USING [FirstChild, LastChild, MakeNodeSpan, NarrowToOtherNode, NewOtherNode, Next, Previous, Ref, Root],
TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords],
ViewerClasses USING [InitProc, ModifyProc, NotifyProc, SaveProc, Viewer, ViewerClass, ViewerClassRec],
ViewerMenus USING [Save],
ViewerOps USING [PaintViewer, RegisterViewerClass, SetMenu, SetNewVersion];
GEditViewerImpl: PROGRAM
IMPORTS CIFS, Directory, EditSpan, File, GEditIO, GEditMotion, GEditPaint, Graphics, GraphicsOps, InputFocus, Menus, MessageWindow, OtherNode, Process, PutGet, RefTab, Rope, SafeStorage, TextEdit, TextNode, TIPUser, ViewerMenus, ViewerOps
EXPORTS GEditSelect, GEditViewer =
BEGIN OPEN GEditViewer, GEditClasses;
gZ: ZONE ← SafeStorage.NewZone[quantized];
selection: PUBLIC Selection ← NEW[SelectionRec];
classes: LIST OF Class ← NIL;
RegisterClass: PUBLIC PROC [flavor: ClassID, classInfo: ClassRec] RETURNS [class: Class] = BEGIN
class ← gZ.NEW[ClassRec ← classInfo];
class.flavor ← flavor;
[] ← RefTab.Store[classTable, flavor, class];
OtherNode.Register[flavor, class.readProc, class.writeProc, class.copyProc];
classes ← CONS[class, classes];
END;
FetchClass: PUBLIC PROC [flavor: ClassID] RETURNS [class: Class] =
{RETURN[NARROW[RefTab.Fetch[classTable, flavor].val]]};
GEditInit: ViewerClasses.InitProc = BEGIN
gvd: GEditViewerData;
gvd ← self.data ← gZ.NEW[GEditViewerDataRec];
IF self.file#NIL THEN gvd.root ← PutGet.FromFile[self.file
! CIFS.Error => CHECKED {self.newFile ← TRUE; CONTINUE};
GEditIO.MyParent => RESUME[self];
];
IF gvd.root=NIL THEN gvd.root ← TextEdit.DocFromNode[Nil[]];
ViewerOps.SetMenu[self, gEditMenu, FALSE];
END;
Nil: PROC RETURNS [object: Object] = BEGIN
object ← TextNode.NewOtherNode[];
object.body ← other [$Nil, NIL];
END;
SetSelection: PROC [self: ViewerClasses.Viewer, gvd: GEditViewerData ← NIL, grain: SelectionGrain ← point, objects: SelectedObject ← NIL, x, y: INTEGER ← 0, paint: BOOLTRUE] = BEGIN
IF selection.displayed AND paint THEN PaintSelection[];
selection^ ← [self, IF self=NIL THEN NIL ELSE gvd, objects, grain, self#NIL, x, y];
IF selection.displayed THEN-- put up new
{IF InputFocus.GetInputFocus[].owner#self THEN InputFocus.SetInputFocus[self] ELSE IF paint THEN PaintSelection[]};
END;
KillSelection: PROC [paint: BOOLTRUE] = BEGIN
IF selection.displayed AND paint THEN PaintSelection[];
selection^ ← [NIL, NIL, NIL, point, FALSE];
END;
PaintSelection: PROC = BEGIN
IF selection.viewer#NIL THEN ViewerOps.PaintViewer[selection.viewer, client, FALSE, $Selection];
END;
DrawControlPoint: PUBLIC PROC [context: Graphics.Context, x, y: INTEGER, marking: SelectionGrain] = BEGIN
Graphics.SetCP[context, x-SIZE[PtArray]/2, y+SIZE[PtArray]/2];
GraphicsOps.DrawBitmap[context, IF marking=point THEN pointMap ELSE objectMap, 16, SIZE[PtArray]];
END;
MarkSelection: PUBLIC PROC [context: Graphics.Context] = BEGIN
HighlightObject: PROC [sel: SelectedObject] = BEGIN
class: Class ~ NARROW[sel.object.class];
IF class#NIL AND class.selectProc#NIL THEN
class.selectProc[sel.object, context, sel.controlPoint, selection.grain];
END;
oldPaintMode: Graphics.PaintMode ← Graphics.SetPaintMode[context, invert];
FOR obj: SelectedObject ← selection.objects, obj.nextObject UNTIL obj=NIL DO
HighlightObject[obj];
ENDLOOP;
[] ← Graphics.SetPaintMode[context, oldPaintMode];
END;
GEditModify: ViewerClasses.ModifyProc = BEGIN
SELECT change FROM
set  => PaintSelection[];
kill => KillSelection[];
ENDCASE;
END;

SelectListFromObject: PROC [object: Object, controlPoint: INTEGER ← 0] RETURNS [sel: SelectedObject] = INLINE BEGIN
sel ← NEW [SelectedObjectRec ← [controlPoint, object, NIL]];
END;
GEditNotify: ViewerClasses.NotifyProc = BEGIN
gvd: GEditViewerData ← NARROW[self.data];
button: Menus.MouseButton ← red;
shift, control: BOOLFALSE;
mouse: TIPUser.TIPScreenCoords;
FOR list: LIST OF REF ANY ← input, list.rest UNTIL list = NIL DO
WITH list.first SELECT FROM
x: ATOM => SELECT x FROM
$Blue     => button ← blue;
$Control    => control ← TRUE;
$Copy    => BEGIN
newObject: Object;
IF selection.objects=NIL OR selection.objects.nextObject#NIL THEN BEGIN
MessageWindow.Append["Please make a single object selection to copy...", TRUE];
MessageWindow.Blink[];
RETURN;
END;
newObject ← CopyObject[gvd, selection.objects.object];
SetSelection[self, gvd, selection.grain, SelectListFromObject[newObject, selection.objects.controlPoint], selection.caretX, selection.caretY, TRUE];
ViewerOps.PaintViewer[selection.viewer, client, FALSE, $XORSelection]; -- since overlaps old object
GEditMotion.Move[mouse.mouseX, mouse.mouseY];
IF ~self.newVersion THEN ViewerOps.SetNewVersion[self];
END;
$Delete    => DeleteSelection[];
$Move    => GEditMotion.Move[mouse.mouseX, mouse.mouseY];
$Next     => Next[shift];
$Red     => button ← red;
$Sampler    => BEGIN
x: INTEGER ← 10;
y: INTEGER ~ 20;
FOR c: LIST OF Class ← classes, c.rest UNTIL c=NIL DO
IF c.first.newProc#NIL THEN BEGIN
object: Object ← EditSpan.InsertOtherNode[gvd.root, TextNode.LastChild[gvd.root]];
object.body ← other [c.first.flavor, NIL];
object.class ← c.first;
c.first.newProc[object, x, y, self];
ViewerOps.PaintViewer[self, client, FALSE, object];
x ← x+100;
END;
ENDLOOP;
END;
$Shift     => shift ← TRUE;
$Select    => Select[self, gvd, mouse.mouseX, mouse.mouseY, selection.grain];
$SelectExtend  => BEGIN
object: Object;
x, y, controlPoint: INTEGER;
[object, controlPoint, x, y] ← Resolve[gvd, mouse.mouseX, mouse.mouseY];
MessageWindow.Append[IF object=NIL THEN "NIL" ELSE SELECT controlPoint FROM 0 => "0", 1 => "1", 2 => "2", ENDCASE => "3", TRUE];
END;
$SelectObject   => Select[self, gvd, mouse.mouseX, mouse.mouseY, object];
$SelectPoint   => Select[self, gvd, mouse.mouseX, mouse.mouseY, point];
$Yellow    => button ← yellow;
ENDCASE    => IF selection.objects#NIL THEN BEGIN
paintAll: BOOLFALSE;
FOR sel: SelectedObject ← selection.objects, sel.nextObject UNTIL sel=NIL DO
class: Class ~ NARROW[sel.object.class];
all, me: BOOLFALSE;
IF class.inputProc#NIL THEN [all, me] ← class.inputProc[sel.object, x];
IF all THEN paintAll ← TRUE
ELSE ViewerOps.PaintViewer[self, client, FALSE, sel.object];
ENDLOOP;
IF paintAll THEN ViewerOps.PaintViewer[self, client];
END;
z: TIPUser.TIPScreenCoords => mouse ← z;
ENDCASE => ERROR;
ENDLOOP;
END;
Select: PROC [self: ViewerClasses.Viewer, gvd: GEditViewerData, mx, my: INTEGER, grain: SelectionGrain] = BEGIN
object: Object;
x, y, controlPoint: INTEGER;
[object, controlPoint, x, y] ← Resolve[gvd, mx, my];
IF object#NIL THEN BEGIN
IF selection.objects#NIL AND selection.objects.object=object AND selection.objects.nextObject=NIL AND selection.grain=grain AND selection.objects.controlPoint = controlPoint THEN RETURN; -- already selected
SetSelection[self, gvd, grain, SelectListFromObject[object, controlPoint], x, y, TRUE];
END
ELSE IF selection.viewer#self THEN SetSelection[self, gvd, grain];
END;
DeleteSelection: PROC = BEGIN
savedSel: SelectedObject ~ selection.objects;
savedViewer: ViewerClasses.Viewer ~ selection.viewer;
KillSelection[];
FOR obj: SelectedObject ← savedSel, obj.nextObject UNTIL obj=NIL DO
Delete[obj.object];
ENDLOOP;
ViewerOps.PaintViewer[savedViewer, client];
IF ~savedViewer.newVersion THEN ViewerOps.SetNewVersion[savedViewer];
END;
Resolve: PROC [gvd: GEditViewerData, x, y: INTEGER] RETURNS [object: Object, controlPoint, trueX, trueY: INTEGER ← 0] = BEGIN
bestAffinitySoFar: INTEGERLAST[INTEGER];
tempControlPoint, tempAffinity, tempX, tempY: INTEGER;
Affinity: PROC [object: Object] RETURNS [affinity, controlPoint, tx, ty: INTEGER] = BEGIN
class: Class ~ NARROW[object.class];
IF class=NIL THEN affinity ← LAST[INTEGER]
ELSE [affinity, controlPoint, tx, ty] ← class.resolveProc[object, x, y];
END;
trueX ← x; trueY ← y;
FOR ref: Object ← TextNode.NarrowToOtherNode[TextNode.FirstChild[gvd.root]],
TextNode.NarrowToOtherNode[TextNode.Next[ref]] UNTIL ref=NIL DO
[tempAffinity, tempControlPoint, tempX, tempY] ← Affinity[ref];
IF tempAffinity <= bestAffinitySoFar THEN BEGIN-- <= favors top most when equal
bestAffinitySoFar ← tempAffinity;
object ← ref;
controlPoint ← tempControlPoint;
trueX ← tempX; trueY ← tempY;
END;
ENDLOOP;
END;
Next: PROC [backwards: BOOL] = BEGIN
move selection through data structure
IF selection.objects=NIL THEN BEGIN
MessageWindow.Append["Please make a selection first", TRUE];
MessageWindow.Blink[];
RETURN;
END;
IF selection.objects.nextObject#NIL THEN BEGIN
MessageWindow.Append["Sorry, NEXT only works for single selected objects", TRUE];
MessageWindow.Blink[];
RETURN;
END;
IF selection.grain = point THEN BEGIN
class: Class ~ NARROW[selection.objects.object.class];
ViewerOps.PaintViewer[selection.viewer, client, FALSE, $Selection];
selection.objects.controlPoint ←
class.nextProc[selection.objects.object, selection.objects.controlPoint, backwards];
ViewerOps.PaintViewer[selection.viewer, client, FALSE, $Selection];
END
ELSE BEGIN-- object
nextObject: TextNode.Ref ← IF backwards THEN TextNode.Previous[selection.objects.object] ELSE TextNode.Next[selection.objects.object];
IF nextObject=NIL THEN BEGIN
MessageWindow.Append[IF backwards THEN "Start of object tree" ELSE "End of object tree", TRUE];
MessageWindow.Blink[];
RETURN;
END;
SetSelection[selection.viewer, selection.data, selection.grain, SelectListFromObject[TextNode.NarrowToOtherNode[nextObject]], selection.caretX, selection.caretY, TRUE]; -- cx, cy are wrong; these should be computed for new control point!!!
END;
END;
EnumProc: TYPE = PROC [self: Object] ;
EnumerateObjects: PROC [gvd: GEditViewerData, enum: EnumProc] = BEGIN
FOR ref: Object ← TextNode.NarrowToOtherNode[TextNode.FirstChild[gvd.root]],
TextNode.NarrowToOtherNode[TextNode.Next[ref]] UNTIL ref=NIL DO
enum[ref];
ENDLOOP;
END;
gEditClass: ViewerClasses.ViewerClass ← NEW[ViewerClasses.ViewerClassRec ← [
paint: GEditPaint.GEditPainter,
init: GEditInit,
save: GEditSave,
notify: GEditNotify,
modify: GEditModify,
tipTable: TIPUser.InstantiateNewTIPTable["GEdit.tip"],
cursor: crossHairsCircle
]];
Delete: EnumProc = BEGIN
class: Class ~ NARROW[self.class];
IF class#NIL AND class.deleteProc#NIL THEN class.deleteProc[self];
EditSpan.Delete[TextNode.Root[self], TextNode.MakeNodeSpan[self, self]];
END;
Clear: Menus.MenuProc = BEGIN
self: ViewerClasses.Viewer ~ NARROW[parent];
gvd: GEditViewerData ~ NARROW[self.data];
IF selection.viewer=self THEN KillSelection[];
EnumerateObjects[gvd, Delete];
gvd.root ← TextEdit.DocFromNode[Nil[]];
ViewerOps.PaintViewer[self, all];
IF ~self.newVersion THEN ViewerOps.SetNewVersion[self];
END;
Reset: Menus.MenuProc = BEGIN
self: ViewerClasses.Viewer ~ NARROW[parent];
gvd: GEditViewerData ~ NARROW[self.data];
IF selection.viewer=self THEN KillSelection[];
EnumerateObjects[gvd, Delete];
GEditInit[NARROW[parent]];
IF self.newVersion THEN self.newVersion ← FALSE;
ViewerOps.PaintViewer[self, all]
END;
Store: Menus.MenuProc = BEGIN
MessageWindow.Append["Sorry, that's not yet implemented...", TRUE];
END;
Load: Menus.MenuProc = BEGIN
MessageWindow.Append["Sorry, that's not yet implemented...", TRUE];
END;
GEditSave: ViewerClasses.SaveProc = BEGIN
gvd: GEditViewerData ~ NARROW[self.data];
dollarFile: REF READONLY TEXT;
dollarCap: File.Capability ← File.nullCapability;
root: TextNode.Ref ~ gvd.root;
file: Rope.ROPE ~ self.file;
IF file=NIL THEN BEGIN
MessageWindow.Append["Can't SAVE -- no file name!", TRUE];
MessageWindow.Blink[];
ViewerOps.SetNewVersion[self]; -- turn the bit back on
RETURN;
END;
dollarFile ← Rope.ToRefText[Rope.Cat[file, "$"]];
dollarCap ← Directory.LookupUnlimited[fileName: LOOPHOLE[dollarFile]
! Directory.Error => CONTINUE];
IF dollarCap#File.nullCapability THEN
Directory.RemoveFile[LOOPHOLE[dollarFile], dollarCap];
Directory.Rename[LOOPHOLE[Rope.ToRefText[file]], LOOPHOLE[dollarFile]
! Directory.Error => CONTINUE];
[] ← PutGet.ToFile[file, root];
IF dollarCap#File.nullCapability THEN
Process.Detach[FORK ReclaimDollarFile[dollarCap]];
END;
ReclaimDollarFile: PROC [file: File.Capability] = {File.Delete[file ! ABORTED => CONTINUE]};
CopyObject: PROC [gvd: GEditViewerData, object: Object] RETURNS [newObject: Object] = BEGIN
newObject ← EditSpan.InsertOtherNode[gvd.root, TextNode.LastChild[gvd.root]];
newObject.class ← object.class;
newObject.body ← other [object.variety, OtherNode.CopyInfo[object]];
END;
PtArray: TYPE = ARRAY [0..6) OF UNSPECIFIED;
objectMap: GraphicsOps.BitmapRef ← NEW[GraphicsOps.BitmapRep ← [base: NEW[PtArray ←
[176000B, 102000B, 102000B, 102000B, 102000B, 176000B]],
raster: (SIZE[PtArray]+15)/16,
width: SIZE[PtArray],
height: SIZE[PtArray]
]];
pointMap: GraphicsOps.BitmapRef ← NEW[GraphicsOps.BitmapRep ← [base: NEW[PtArray ←
[176000B, 176000B, 176000B, 176000B, 176000B, 176000B]],
raster: (SIZE[PtArray]+15)/16,
width: SIZE[PtArray],
height: SIZE[PtArray]
]];
classTable: RefTab.Ref ← RefTab.Create[mod:32]; -- hold GEdit classes
gEditMenu: Menus.Menu ← Menus.CreateMenu[];
Menus.AppendMenuEntry[gEditMenu,
Menus.CreateEntry[name: "Clear", proc: Clear, documentation: "Destroy contents...", guarded: FALSE]];
Menus.AppendMenuEntry[gEditMenu,
Menus.CreateEntry[name: "Reset", proc: Reset, documentation: "Read from file...", guarded: FALSE]];
Menus.AppendMenuEntry[gEditMenu,
Menus.CreateEntry[name: "Store", proc: Store, documentation: "Store contents to file...", guarded: FALSE]];
Menus.AppendMenuEntry[gEditMenu,
Menus.CreateEntry[name: "Load", proc: Load, documentation: "Read contents from a file...", guarded: FALSE]];
Menus.AppendMenuEntry[gEditMenu,
Menus.CreateEntry[name: "Save", proc: ViewerMenus.Save, documentation: "Save contents to file...", guarded: FALSE]];
ViewerOps.RegisterViewerClass[$Graphics, gEditClass]; -- plug in to Viewers
END.