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:
BOOL ←
TRUE] =
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:
BOOL ←
TRUE] =
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: BOOL ← FALSE;
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: BOOL ← FALSE;
FOR sel: SelectedObject ← selection.objects, sel.nextObject
UNTIL sel=
NIL
DO
class: Class ~ NARROW[sel.object.class];
all, me: BOOL ← FALSE;
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: INTEGER ← LAST[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.