-- TEditDocuments2Impl.mesa; Edited by Paxton on June 7, 1983 9:38 am
-- Edited by McGregor on September 10, 1982 1:53 pm
Last Edited by: Maxwell, January 6, 1983 11:06 am
Last Edited by: Plass, April 15, 1983 1:10 pm
DIRECTORY
CIFS USING [Error, Open, OpenFile, Close, GetPathname, read],
Menus USING [Menu, MenuEntry, MenuProc, SetLine],
MessageWindow USING [Append, Blink, Clear],
NodeProps USING [GetProp, PutProp, Register, NullRead, NullWrite, NullCopy],
Process USING [Detach],
Rope USING [Concat, Equal, Fetch, Find, ROPE, Size, Substr],
RopeEdit USING [BlankChar, IllegalChar],
RTMiniModel USING [ImplementorName],
TextEdit USING [Size],
TextNode 
USING [FirstChild, LocNumber, Location, LocWithin, NarrowToTextNode,
nullLocation, Offset, pZone, Ref, RefTextNode, Root],
TEditDisplay USING [EstablishLine],
TEditDocument 
USING [LineTableRec, maxClip, Selection, SpinAndLock, TEditDocumentData,
Unlock],
TEditDocumentPrivate,
TEditInput USING [FreeTree],
TEditInputOps USING [WaitForInsertToFinish],
TEditOps,
TEditProfile USING [DefaultMenuChoice, menu1, menu2, menu3, openFirstLevelOnly, sourceExtensions, implExtensions],
TEditSelection USING [FindRope, LockSel, MakeSelection, pSel, sSel, fSel, UnlockSel],
ViewerOps USING [AddProp, CloseViewer, ComputeColumn, CreateViewer, DestroyViewer, EstablishViewerPosition, FetchProp, MoveBelowViewer, OpenIcon, PaintHint, PaintViewer, SaveViewer, SetMenu],
ViewerClasses USING [GetProc, Viewer, ViewerClass, ViewerClassRec],
ViewerTools USING [GetContents, GetSelectionContents, SelPos, SelPosRec, SetSelection],
VirtualDesktops USING [FindViewer];
 
TEditDocuments2Impl: CEDAR MONITOR
IMPORTS CIFS, Menus, MessageWindow, NodeProps, Process, Rope, RopeEdit, RTMiniModel, TEditDocument, TEditDocumentPrivate, TextEdit, TextNode, TEditDisplay, TEditInput, TEditInputOps, TEditProfile, TEditSelection, ViewerOps, ViewerTools, VirtualDesktops
EXPORTS TEditDocument, TEditDocumentPrivate, TEditOps
SHARES ViewerClasses =
BEGIN OPEN Rope, TEditDocument, TEditDocumentPrivate, ViewerClasses;
ForceInitialCaret: PROC [viewer: Viewer] = {ViewerTools.SetSelection[viewer, initialCaret]};
initialCaret: ViewerTools.SelPos ← 
NEW[ViewerTools.SelPosRec];
CancelLinks: 
PUBLIC 
PROC [viewer: Viewer] = 
BEGIN
ForgetViewer[viewer];
IF viewer.link.link=viewer 
THEN 
BEGIN
viewer.link.link ← NIL;
ViewerOps.PaintViewer[viewer.link, caption];
END
 
ELSE 
FOR v: Viewer ← viewer.link.link, v.link 
UNTIL v.link=viewer 
DO
REPEAT FINISHED => v.link ← viewer.link;
ENDLOOP;
 
viewer.link ← NIL;
END;
 
lastRoot: TextNode.Ref;
lastViewer: ViewerClasses.Viewer;
RecordViewerForRoot: 
PUBLIC 
ENTRY 
PROC
[viewer: ViewerClasses.Viewer, root: TextNode.Ref] = {
ENABLE UNWIND => NULL;
RecordViewerForRootI[viewer, root] };
 
RecordViewerForRootI: 
INTERNAL 
PROC [viewer: ViewerClasses.Viewer, root: TextNode.Ref] = {
NodeProps.PutProp[root,$Viewer,viewer];
IF root = lastRoot THEN lastViewer ← viewer; -- keep the cache up to date
};
 
ForgetViewer: 
PUBLIC 
ENTRY 
PROC [viewer: ViewerClasses.Viewer] = {
ENABLE UNWIND => NULL;
tdd: TEditDocument.TEditDocumentData = NARROW[viewer.data];
root: TextNode.Ref;
IF tdd=NIL THEN RETURN;
root ← tdd.text;
IF GetViewerForRootI[root] # viewer THEN RETURN;
IF viewer.link # 
NIL 
THEN { 
-- change to a linked viewer
RecordViewerForRootI[viewer.link,root];
RETURN };
 
RecordViewerForRootI[NIL,root] };
 
GetViewerForRoot: 
PUBLIC 
ENTRY 
PROC [root: TextNode.Ref]
RETURNS [viewer: ViewerClasses.Viewer] = {
ENABLE UNWIND => NULL;
RETURN [GetViewerForRootI[root]] };
 
GetViewerForRootI: 
INTERNAL 
PROC [root: TextNode.Ref]
RETURNS [viewer: ViewerClasses.Viewer] = {
IF root = NIL THEN RETURN [NIL];
IF root = lastRoot THEN RETURN [lastViewer];
lastRoot ← root;
RETURN [lastViewer ← NARROW[NodeProps.GetProp[root,$Viewer]]] };
 
LookupDirectoryName: 
PROC [file: Rope.
ROPE] 
RETURNS [fullName: Rope.
ROPE] = 
BEGIN
fh: CIFS.OpenFile ← CIFS.Open[file, CIFS.read];
fullName ← CIFS.GetPathname[fh];
CIFS.Close[fh];
IF fullName=NIL THEN fullName ← file; -- temp hack until CIFS bug is fixed.
IF Rope.Find[fullName,"/local/",0,
FALSE]=0 
THEN 
-- delete it from start of name
fullName ← Rope.Substr[fullName, Rope.Size["/local/"]];
 
END;
 
GetDirectoryName: 
PROC [file: Rope.
ROPE] 
RETURNS [directoryName: Rope.
ROPE, new: 
BOOL] = {
new ← FALSE;
IF Rope.Size[file]=0 THEN RETURN [NIL,TRUE];
directoryName ← LookupDirectoryName[file
! CIFS.Error => {directoryName ← file; new ← TRUE; CONTINUE}] };
 
PreStore: 
PUBLIC Menus.MenuProc = { 
-- called when Store menu item becomes unguarded
new: BOOL;
fileName: Rope.ROPE ← ViewerTools.GetSelectionContents[];
IF Rope.Size[fileName]=0 THEN {PleaseSelectFileName[]; RETURN};
IF ~CheckFileName[fileName] THEN RETURN;
[fileName, new] ← GetDirectoryName[fileName];
MessageWindow.Append[Rope.Concat["Confirm Store to file: ",
Rope.Concat[fileName, IF new THEN " [New File]" ELSE " [Old File]"]], TRUE]
};
 
StoreFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = { DoStoreFile[parent,
NIL] };
 
DoStoreFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer, fileName: Rope.
ROPE ← 
NIL] = 
BEGIN
new, linked: BOOL;
tdd: TEditDocumentData;
IF fileName=NIL THEN fileName ← ViewerTools.GetSelectionContents[];
IF Rope.Size[fileName]=0 THEN {PleaseSelectFileName[]; RETURN};
IF ~CheckFileName[fileName] THEN RETURN;
[fileName, new] ← GetDirectoryName[fileName];
IF VirtualDesktops.FindViewer[fileName].viewer # 
NIL 
THEN { 
-- already loaded
MessageWindow.Append[
Rope.Concat[fileName, " is already in a viewer!  Cannot Store to that name."], TRUE];
MessageWindow.Blink;
RETURN };
 
tdd ← NARROW[parent.data];
IF tdd = NIL THEN RETURN;
[] ← SpinAndLock[tdd, "DoStoreFile"];
IF parent.file # NIL THEN SaveLoadHistory[parent];
IF linked ← (parent.link#NIL) THEN CancelLinks[parent]; -- remove viewer from link chain
parent.name ← parent.file ← fileName;
NodeProps.PutProp[tdd.text, $FileCreateDate, NIL]; -- remove so Save won't test
Unlock[tdd];
ViewerOps.SaveViewer[parent];
IF linked 
THEN { 
-- copy tdd.text data structure
loc: TextNode.Offset;
KillSelections[parent];
[] ← SpinAndLock[tdd, "DoStoreFile2"];
loc ← TextNode.LocNumber[tdd.lineTable.lines[0].pos];
tdd.text ← NIL; -- so InitViewerDoc won't free it
InitViewerDoc[parent, NIL];
IF ~PositionViewer[parent,TextNode.LocWithin[tdd.text,loc],all] 
THEN
TRUSTED {Process.Detach[FORK PaintAndRemember[parent]]};
 
Unlock[tdd] };
 
END;
 
KillSelections: 
PROC [parent: ViewerClasses.Viewer] = { 
OPEN TEditSelection;
IF pSel # NIL AND pSel.viewer = parent THEN MakeSelection[selection: primary];
IF sSel # NIL AND sSel.viewer = parent THEN MakeSelection[selection: secondary];
IF fSel # NIL AND fSel.viewer = parent THEN MakeSelection[selection: feedback] };
 
EmptyViewer: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = 
BEGIN
IF parent#
NIL 
AND parent.data#
NIL 
THEN 
WITH parent.data 
SELECT 
FROM
tdd: TEditDocument.TEditDocumentData => {
TEditSelection.LockSel[primary, "EmptyViewer"];
{ 
ENABLE 
UNWIND => TEditSelection.UnlockSel[primary];
prop: REF LoadHistory;
[] ← SpinAndLock[tdd, "EmptyViewer"];
KillSelections[parent];
SaveLoadHistory[parent];
prop ← NARROW[ViewerOps.FetchProp[parent, $LoadHistory]]; -- hang onto it
IF parent.link#NIL THEN CancelLinks[parent]
ELSE 
IF parent.newVersion 
AND ~parent.saveInProgress 
AND parent.file # 
NIL 
THEN
RecordUnsavedDocument[parent.file, tdd.text]
 
ELSE TEditInput.FreeTree[tdd.text];
parent.name ← "No Name";  parent.file ← NIL;
parent.newVersion ← parent.newFile ← FALSE;
tdd.text  ← NIL; -- so InitViewerDoc won't free it
InitViewerDoc[parent,NIL];
Unlock[tdd];
ClearPositionHistory[parent];
ViewerOps.AddProp[parent, $LoadHistory, prop]; -- restore file history
ViewerOps.PaintViewer[parent, all];
ForceInitialCaret[parent] };
TEditSelection.UnlockSel[primary];
};
ENDCASE;
 
END;
 
PleaseSelectFileName: 
PROC = {
MessageWindow.Append["Please select file name.", TRUE];
MessageWindow.Blink[] };
 
IllegalFileName: 
PROC = {
MessageWindow.Append["Illegal file name.", TRUE];
MessageWindow.Blink[] };
 
CheckFileName: 
PROC [directoryName: Rope.
ROPE] 
RETURNS [
BOOLEAN] = {
FOR i:
NAT 
IN [0..Rope.Size[directoryName]) 
DO
char: CHAR ← Rope.Fetch[directoryName, i];
IF RopeEdit.BlankChar[char] 
OR RopeEdit.IllegalChar[char] 
THEN {
TRUSTED { Process.Detach[FORK IllegalFileName[]] };
RETURN [FALSE] };
 
ENDLOOP;
 
RETURN [TRUE] };
 
UnknownFile: 
PROC [directoryName: Rope.
ROPE] = {
MessageWindow.Append[directoryName, TRUE];
MessageWindow.Append[" not found."];
MessageWindow.Blink[];
RETURN};
 
CheckName: 
PROC [name, ext: Rope.
ROPE] 
RETURNS [file: Rope.
ROPE, fileExists: 
BOOL] = {
fileExists ← TRUE;
file ← Rope.Concat[name, ext];
file ← LookupDirectoryName[file ! CIFS.Error => { fileExists ← FALSE; CONTINUE }] };
 
GetNameAndSearchRope: 
PROC [
sel: Rope.ROPE, fileNameProcViewer: ViewerClasses.Viewer]
RETURNS [file, search: Rope.ROPE] = {
name: Rope.ROPE;
dot, extSize: INT;
fileExists, standard, noDot: BOOL ← FALSE;
MessageWindow.Append[Rope.Concat["Directory lookup for ", sel], TRUE];
[file, fileExists] ← CheckName[sel, NIL];
IF fileExists THEN RETURN;
IF (dot ← Rope.Find[sel, "."]) < 0 
THEN { 
-- no dot in selection
FOR lst: 
LIST 
OF Rope.
ROPE ← TEditProfile.sourceExtensions, lst.rest 
UNTIL lst=
NIL 
DO
[file, fileExists] ← CheckName[sel, lst.first];
IF fileExists THEN RETURN;
ENDLOOP;
 
noDot ← TRUE;
name ← sel;
search ← NIL }
 
ELSE {
name ← Rope.Substr[sel, 0, dot]; -- the text before the "."
search ← Rope.Substr[sel, dot+1]; -- the text after the "."
extSize ← Rope.Size[search]+1;
FOR lst: 
LIST 
OF Rope.
ROPE ← TEditProfile.sourceExtensions, lst.rest 
UNTIL lst=
NIL 
DO
IF Rope.Size[lst.first]=extSize 
AND Rope.Find[lst.first, search, 1, 
FALSE]=1 
THEN
{ standard ← TRUE; EXIT }; -- this is one of the standard extensions
 
ENDLOOP;
 
};
 
IF fileNameProc # 
NIL 
THEN {
srch: Rope.ROPE;
[file, srch] ← fileNameProc[sel, fileNameProcViewer];
IF file # NIL THEN RETURN [file, srch] };
 
IF ~standard 
AND ~noDot 
THEN 
-- try from the list of standard extensions
FOR lst: 
LIST 
OF Rope.
ROPE ← TEditProfile.sourceExtensions, lst.rest 
UNTIL lst=
NIL 
DO
[file, fileExists] ← CheckName[name, lst.first];
IF fileExists THEN RETURN;
ENDLOOP;
 
 
file ← sel; search ← NIL };
 
fileNameProc: 
PROC [Rope.
ROPE, ViewerClasses.Viewer]
RETURNS [fileName: Rope.ROPE, search: Rope.ROPE] ← NIL;
RegisterFileNameProc: 
PUBLIC 
PROC [proc: 
PROC [Rope.
ROPE, ViewerClasses.Viewer]
RETURNS [fileName: Rope.ROPE, search: Rope.ROPE]] = { fileNameProc ← proc };
 
GetImplNameAndSearchRope: 
PROC [sel: Rope.
ROPE] 
RETURNS [impl, search: Rope.
ROPE] = {
name, defsName: Rope.ROPE;
dot: INT;
fileExists: BOOL;
IF (dot ← Rope.Find[sel, "."]) < 0 THEN { defsName ← sel; search ← NIL }
ELSE { defsName ← Rope.Substr[sel, 0, dot]; search ← Rope.Substr[sel, dot+1] };
IF Rope.Size[search] > 0 
THEN {
MessageWindow.Append[Rope.Concat["Model lookup for implementor of ", search], TRUE];
name ← RTMiniModel.ImplementorName[defsName, search] };
 
IF name=NIL THEN name ← Rope.Concat[defsName, "Impl"];
MessageWindow.Append[Rope.Concat["Directory lookup for ", name], TRUE];
FOR lst: 
LIST 
OF Rope.
ROPE ← TEditProfile.implExtensions, lst.rest 
UNTIL lst=
NIL 
DO
[impl, fileExists] ← CheckName[name, lst.first];
IF fileExists THEN RETURN;
ENDLOOP;
 
RETURN [name, NIL] };
 
DoSearch: 
PROC [v: Viewer, search: Rope.
ROPE] = { 
-- search for definition
tdd: TEditDocument.TEditDocumentData ← NARROW[v.data];
loc: TextNode.Location;
IF tdd = NIL OR Rope.Size[search] = 0 THEN RETURN; -- forget it
IF (loc ← TextNode.LocWithin[tdd.text,0]) # tdd.lineTable.lines[0].pos 
THEN {
TEditDisplay.EstablishLine[tdd, loc];
ViewerOps.PaintViewer[v, client] }; -- always search from start
 
TEditSelection.FindRope[
viewer: v, rope: search, case: FALSE, word: TRUE, def: TRUE, id: feedback];
};
 
CopyPositionHistory: 
PUBLIC 
PROC [from, to: Viewer] = {
old: REF PositionHistory ← NARROW[ViewerOps.FetchProp[from, $PositionHistory]];
new: REF PositionHistory ← NARROW[ViewerOps.FetchProp[to, $PositionHistory]];
IF old=NIL THEN RETURN;
IF new=
NIL 
THEN {
new ← TextNode.pZone.NEW[PositionHistory];
ViewerOps.AddProp[to, $PositionHistory, new] };
 
new^ ← old^;
};
 
ClearPositionHistory: 
PROC [viewer: Viewer] = {
prop: REF PositionHistory ← NARROW[ViewerOps.FetchProp[viewer, $PositionHistory]];
IF prop = NIL THEN RETURN;
prop.pos ← prop.prev ← TextNode.nullLocation;
};
 
RememberCurrentPosition: 
PUBLIC 
PROC [viewer: Viewer] = {
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
prop: REF PositionHistory ← NARROW[ViewerOps.FetchProp[viewer, $PositionHistory]];
loc: TextNode.Location;
IF tdd=NIL THEN RETURN;
IF prop = 
NIL 
THEN {
prop ← TextNode.pZone.NEW[PositionHistory];
ViewerOps.AddProp[viewer, $PositionHistory, prop] };
 
[] ← SpinAndLock[tdd, "RememberCurrentPosition"];
loc ← tdd.lineTable.lines[0].pos;
Unlock[tdd];
IF loc = prop.pos THEN RETURN;
prop.prev ← prop.pos; prop.pos ← loc };
 
CheckPosition: 
PROC [viewer: Viewer, loc: TextNode.Location]
RETURNS [good: BOOLEAN, goodloc: TextNode.Location] = {
root, node: TextNode.Ref;
t1: TextNode.RefTextNode;
tdd: TEditDocument.TEditDocumentData;
IF viewer=NIL OR viewer.destroyed OR (node ← loc.node)=NIL THEN GOTO Failed;
IF (tdd ← NARROW[viewer.data]) = NIL THEN GOTO Failed;
IF (root ← tdd.text)=NIL THEN GOTO Failed;
IF TextNode.Root[node] # root THEN GOTO Failed; -- make sure still in the tree
IF (t1 ← TextNode.NarrowToTextNode[node])=
NIL 
OR
loc.where NOT IN [0..TextEdit.Size[t1]] THEN RETURN [TRUE, [node,0]];
 
RETURN [TRUE,loc];
EXITS Failed => RETURN [FALSE, TextNode.nullLocation];
};
 
PositionViewer: 
PUBLIC 
PROC [
viewer: Viewer, loc: TextNode.Location, hint: ViewerOps.PaintHint ← client] 
RETURNS [ok: BOOLEAN] = {
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
node: TextNode.RefTextNode;
[ok, loc] ← CheckPosition[viewer, loc];
IF tdd = NIL OR ~ok THEN RETURN;
RememberCurrentPosition[viewer];
IF (node ← TextNode.NarrowToTextNode[loc.node]) # 
NIL 
THEN { 
-- backup to line start
where: INT ← MAX[0, MIN[loc.where, TextEdit.Size[node]-1]];
backStop: INT ← MAX[0, where-300];
UNTIL where<=backStop 
OR Rope.Fetch[node.rope, where-1]=15C 
DO
where ← where - 1;
ENDLOOP;
 
loc.where ← where };
 
TEditDisplay.EstablishLine[tdd, loc];
TRUSTED {Process.Detach[FORK ViewerOps.PaintViewer[viewer, hint ! ABORTED => CONTINUE]]};
};
 
LoadHistory: 
TYPE = 
RECORD [
name: Rope.ROPE,
place: TextNode.Offset ];
 
CopyLoadHistory: 
PUBLIC 
PROC [from, to: Viewer] = {
old: REF LoadHistory ← NARROW[ViewerOps.FetchProp[from, $LoadHistory]];
new: REF LoadHistory ← NARROW[ViewerOps.FetchProp[to, $LoadHistory]];
IF old=NIL THEN RETURN;
IF new=
NIL 
THEN {
new ← TextNode.pZone.NEW[LoadHistory];
ViewerOps.AddProp[to, $LoadHistory, new] };
 
new^ ← old^;
};
 
LoadPreviousFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = 
BEGIN
DoLoadPreviousFile[parent, FALSE, FALSE];
END;
 
OpenPreviousFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = 
BEGIN
DoLoadPreviousFile[parent, TRUE, FALSE];
END;
 
CloseAndOpenPreviousFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = 
BEGIN
DoLoadPreviousFile[parent, TRUE, TRUE];
END;
 
PreLoadPrevious: 
PUBLIC Menus.MenuProc = {
viewer: ViewerClasses.Viewer = NARROW[parent];
tdd: TEditDocumentData;
prop: REF LoadHistory;
propName: Rope.ROPE;
tdd ← NARROW[viewer.data];
IF tdd = NIL THEN RETURN;
[] ← SpinAndLock[tdd, "PreLoadPrevious"]; -- delay until after other op completes
prop ← NARROW[ViewerOps.FetchProp[viewer, $LoadHistory]];
Unlock[tdd];
IF prop = 
NIL 
OR Rope.Equal[prop.name, viewer.file, 
FALSE] 
THEN {
MessageWindow.Append["No record of previous file loaded in this viewer",TRUE];
MessageWindow.Blink[];
RETURN };
 
propName ← prop.name;
IF ~CheckFileName[propName] THEN RETURN;
MessageWindow.Append[clearFirst: 
TRUE, message:
Rope.Concat[propName, " ~ Click LEFT to load, MIDDLE for new, RIGHT for close & new"]];
};
 
DoLoadPreviousFile: 
PROC [parent: ViewerClasses.Viewer, open, close: 
BOOL] = 
BEGIN
tdd: TEditDocumentData;
prop: REF LoadHistory;
propName: Rope.ROPE;
propPlace: TextNode.Offset;
tdd ← NARROW[parent.data];
IF tdd = NIL THEN RETURN;
[] ← SpinAndLock[tdd, "DoLoadPreviousFile"]; -- delay until after other op completes
prop ← NARROW[ViewerOps.FetchProp[parent, $LoadHistory]];
Unlock[tdd];
IF prop = 
NIL 
OR Rope.Equal[prop.name, parent.file, 
FALSE] 
THEN {
MessageWindow.Append["No record of previous file loaded in this viewer",TRUE];
MessageWindow.Blink[];
RETURN };
 
propName ← prop.name; propPlace ← prop.place;
IF ~CheckFileName[propName] THEN RETURN;
IF open AND ~close THEN [] ← DoOpen[propName, parent, propPlace]
ELSE [] ← DoLoad[parent, propName, close, propPlace];
END;
 
CheckAnonymous: 
PROC [parent: ViewerClasses.Viewer, txt: Rope.
ROPE]
RETURNS [BOOLEAN] = {
IF Rope.Size[txt]=0 
THEN {
MessageWindow.Append["Enter a file name.",TRUE];
MessageWindow.Blink[];
RETURN[FALSE]};
 
IF ~CheckFileName[txt] THEN RETURN[FALSE];
RETURN [TRUE];
};
 
CheckNoFile: 
PROC [parent: ViewerClasses.Viewer] 
RETURNS [
BOOLEAN] = {
IF parent.file # 
NIL 
THEN {
MessageWindow.Append["Viewer already contains a file.",TRUE];
MessageWindow.Blink[];
RETURN [FALSE]};
 
RETURN [TRUE];
};
 
AnonymousLoadFile: 
PUBLIC 
PROC [
parent: ViewerClasses.Viewer, fileNameProcViewer: ViewerClasses.Viewer ← NIL] = {
txt: Rope.ROPE;
IF ~CheckNoFile[parent] THEN RETURN;
TEditInputOps.WaitForInsertToFinish[];
txt ← ViewerTools.GetContents[parent];
IF ~CheckAnonymous[parent, txt] THEN RETURN;
[] ← LoadAndSearch[parent, txt, FALSE, fileNameProcViewer];
};
 
LoadAndSearch: 
PROC [
parent: ViewerClasses.Viewer, txt: Rope.ROPE,
close: BOOL ← FALSE, fileNameProcViewer: ViewerClasses.Viewer ← NIL]
RETURNS [viewer: ViewerClasses.Viewer] = {
file, search: Rope.ROPE;
IF fileNameProcViewer=NIL THEN fileNameProcViewer ← parent;
[file, search] ← GetNameAndSearchRope[txt, fileNameProcViewer];
IF (viewer ← DoLoad[parent, file, close]) # NIL THEN DoSearch[viewer, search];
};
 
AnonymousLoadImplFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = {
txt: Rope.ROPE;
IF ~CheckNoFile[parent] THEN RETURN;
TEditInputOps.WaitForInsertToFinish[];
txt ← ViewerTools.GetContents[parent];
IF ~CheckAnonymous[parent, txt] THEN RETURN;
[] ← LoadImplAndSearch[parent, txt];
};
 
LoadImplAndSearch: 
PROC [parent: ViewerClasses.Viewer, txt: Rope.
ROPE, close: 
BOOL ← 
FALSE]
RETURNS [viewer: Viewer] = {
impl, search: Rope.ROPE;
[impl, search] ← GetImplNameAndSearchRope[txt];
IF (viewer ← DoLoad[parent, impl, close]) # NIL THEN DoSearch[viewer, search];
};
 
LoadFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = {
[] ← DoLoadFile[parent, NIL, FALSE] };
 
DoLoadFile: 
PUBLIC 
PROC [
parent: ViewerClasses.Viewer, fileName: ROPE ← NIL, close: BOOL ← FALSE,
fileNameProcViewer: ViewerClasses.Viewer ← NIL]
RETURNS [viewer: ViewerClasses.Viewer] = BEGIN
tdd: TEditDocumentData;
IF fileName=NIL THEN fileName ← ViewerTools.GetSelectionContents[];
tdd ← NARROW[parent.data];
IF tdd = NIL THEN RETURN;
[] ← SpinAndLock[tdd, "DoLoadFile"]; Unlock[tdd]; -- let other op finish first
IF ~PreLoadCheck[parent, fileName] THEN RETURN;
viewer ← LoadAndSearch[parent, fileName, close, fileNameProcViewer];
END;
 
LoadImplFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = {
[] ← DoLoadImplFile[parent, NIL] };
 
DoLoadImplFile: 
PUBLIC 
PROC [
parent: ViewerClasses.Viewer, fileName: ROPE ← NIL, close: BOOL ← FALSE]
RETURNS [viewer: Viewer] = BEGIN
tdd: TEditDocumentData;
IF fileName=NIL THEN fileName ← ViewerTools.GetSelectionContents[];
tdd ← NARROW[parent.data];
IF tdd = NIL THEN RETURN;
[] ← SpinAndLock[tdd ,"DoLoadImplFile"]; Unlock[tdd]; -- let other op finish first
IF ~PreLoadCheck[parent, fileName] THEN RETURN;
viewer ← LoadImplAndSearch[parent, fileName, close];
END;
 
PreLoadCheck: 
PROC [parent: Viewer, sel: Rope.
ROPE] 
RETURNS [
BOOLEAN] = {
IF Rope.Size[sel]=0 
THEN {
PleaseSelectFileName[];
RETURN[FALSE]};
 
IF ~CheckFileName[sel] THEN RETURN[FALSE];
RETURN [TRUE] };
 
AllocLoadHistory: 
PROC [viewer: Viewer] 
RETURNS [prop: 
REF LoadHistory] = {
IF (prop ← NARROW[ViewerOps.FetchProp[viewer, $LoadHistory]]) # NIL THEN RETURN;
prop ← TextNode.pZone.NEW[LoadHistory];
ViewerOps.AddProp[viewer, $LoadHistory, prop] };
 
SetLoadHistoryInfo: 
PROC [viewer: Viewer, prop: 
REF LoadHistory] = {
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
IF tdd = NIL THEN RETURN;
prop.name ← viewer.file;
prop.place ← TextNode.LocNumber[tdd.lineTable.lines[0].pos] };
 
SetLoadHistory: 
PROC [parent, viewer: Viewer] = {
Make viewer's load history point to current contents of parent
prop: REF LoadHistory ← AllocLoadHistory[viewer];
SetLoadHistoryInfo[parent, prop] };
 
SaveLoadHistory: 
PROC [viewer: Viewer] = {
prop: REF LoadHistory ← AllocLoadHistory[viewer];
SetLoadHistoryInfo[viewer, prop] };
 
DoLoad: 
PROC [viewer: Viewer, file: Rope.
ROPE, close: 
BOOL ← 
FALSE, place: TextNode.Offset ← 0]
RETURNS [Viewer] = BEGIN
new: BOOLEAN ← FALSE;
tdd: TEditDocument.TEditDocumentData;
oldViewer, destroyViewer: Viewer;
tddOld: TEditDocument.TEditDocumentData;
clearMessage: BOOL ← TRUE;
directoryName: Rope.ROPE;
[directoryName,new] ← GetDirectoryName[file];
IF new THEN { UnknownFile[directoryName]; RETURN[NIL] };
IF Rope.Equal[directoryName,viewer.name] 
THEN {
MessageWindow.Append[Rope.Concat[directoryName," is already loaded."],TRUE];
RETURN [viewer] };
 
MessageWindow.Append[Rope.Concat["Loading ", directoryName],TRUE];
KillSelections[viewer];
tdd ← NARROW[viewer.data];
IF tdd = NIL THEN RETURN [viewer];
[] ← SpinAndLock[tdd, "DoLoad"];
IF viewer.file # NIL THEN SaveLoadHistory[viewer];
IF viewer.link=
NIL 
THEN {
IF close 
THEN {
Unlock[tdd];
viewer ← ReplaceByNewViewer[viewer];
RETURN [DoLoad[viewer, file, FALSE, place]] }; -- try again
 
ForgetViewer[viewer]; -- remove this from the root => viewer mapping
IF viewer.newVersion 
AND ~viewer.saveInProgress 
AND viewer.file # 
NIL 
THEN
RecordUnsavedDocument[viewer.file, tdd.text]
 
ELSE TEditInput.FreeTree[tdd.text] }
 
ELSE CancelLinks[viewer];
viewer.name ← viewer.file ← NIL;
[oldViewer, ----] ← VirtualDesktops.FindViewer[directoryName];
viewer.name ← viewer.file ← directoryName;
viewer.newVersion ← viewer.newFile ← FALSE;
IF oldViewer # 
NIL 
AND 
NOT oldViewer.destroyed 
AND (tddOld ← 
NARROW[oldViewer.data]) # 
NIL 
THEN { 
-- link them
viewer.newVersion ← oldViewer.newVersion;
viewer.newFile ← oldViewer.newFile;
[] ← SpinAndLock[tddOld, "DoLoad"];
tdd.text ← tddOld.text;
tdd.tsInfo ← tddOld.tsInfo;
Unlock[tddOld];
tdd.lineTable.lines[0].pos ← [TextNode.FirstChild[tdd.text],0];
tdd.clipLevel ← maxClip;
tdd.commentFilter ← includeComments;
viewer.link ← oldViewer;
IF oldViewer.link=
NIL 
THEN {
oldViewer.link ← viewer;
ViewerOps.PaintViewer[oldViewer, caption] }
 
ELSE 
FOR v: Viewer ← oldViewer.link, v.link 
UNTIL v.link=oldViewer 
DO
REPEAT FINISHED => v.link ← viewer;
ENDLOOP;
 
TRUSTED {Process.Detach[FORK WasLoadedMessage[directoryName]] };
clearMessage ← FALSE;
FOR v: Viewer ← oldViewer, v.link 
UNTIL v.link=oldViewer 
DO
IF v.iconic THEN { destroyViewer ← v; EXIT }; -- destroy an iconic viewer
ENDLOOP
 
}
 
ELSE {
root: TextNode.Ref ← FindUnsavedDocument[directoryName];
tdd.text ← NIL; -- so InitViewerDoc won't worry about freeing it
IF root # 
NIL 
THEN {
TRUSTED {Process.Detach[FORK ReloadedMessage[directoryName]] }; -- can deadlock if don't fork
clearMessage ← FALSE };
 
InitViewerDoc[viewer, root];
viewer.newVersion ← root # NIL;
viewer.newFile ← FALSE };
 
ClearPositionHistory[viewer];
IF ~PositionViewer[viewer,TextNode.LocWithin[tdd.text,place],all] 
THEN
TRUSTED {Process.Detach[FORK PaintAndRemember[viewer ! ABORTED => CONTINUE]]};
 
Unlock[tdd];
IF destroyViewer # 
NIL 
THEN ViewerOps.DestroyViewer[destroyViewer];
Cannot do the destroy until after have released lock.
 
IF clearMessage THEN MessageWindow.Clear[];
RETURN[viewer];
END;
 
WasLoadedMessage: 
PROC [name: Rope.
ROPE] = {
MessageWindow.Append[Rope.Concat[name," was already loaded."],TRUE] };
 
ReloadedMessage: 
PROC [name: Rope.
ROPE] = {
MessageWindow.Append[
Rope.Concat[name," restored with previous unsaved edits."],TRUE];
MessageWindow.Blink[] };
 
PaintAndRemember: 
PROC [viewer: ViewerClasses.Viewer] = {
ViewerOps.PaintViewer[viewer,all]; RememberCurrentPosition[viewer] };
 
ReplaceByNewViewer: 
PROC [parent: ViewerClasses.Viewer] 
RETURNS [viewer: Viewer] = {
Cannot have document or tdd locked when call this or may deadlock.
KillSelections[parent];
need to deselect before make new viewer
viewer ← MakeNewViewer[parent, FALSE];
copy height and position info from old to new
viewer.openHeight ← parent.openHeight;
viewer.position ← parent.position;
SetLoadHistory[parent, viewer];
make parent iconic without doing paints in the column, just paint the icon
ViewerOps.CloseViewer[viewer: parent, paint: FALSE];
ViewerOps.PaintViewer[parent, all]; -- paint the icon
};
 
MakeNewViewer: 
PROC [parent: ViewerClasses.Viewer, paint: 
BOOL ← 
TRUE]
RETURNS [viewer: Viewer] = {
Cannot have document or tdd locked when call this or may deadlock.
viewer ← ViewerOps.CreateViewer[flavor: $Text, info: [name: "No Name",
column: IF parent=NIL THEN left ELSE parent.column, iconic: TRUE], paint: FALSE];
IF TEditProfile.openFirstLevelOnly 
THEN {
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
IF tdd # NIL THEN tdd.clipLevel ← 1 };
 
ViewerOps.OpenIcon[icon: viewer, paint: 
FALSE];
Create it iconic and then open to minimize time that have a bad viewer tree.
IF parent # 
NIL 
THEN { 
-- move above parent and copy menus
ViewerOps.MoveBelowViewer[altered: viewer, static: parent, paint: FALSE];
ViewerOps.SetMenu[viewer, parent.menu, FALSE];
ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh];
}
 
ELSE DefaultMenus[viewer];
IF paint THEN ViewerOps.ComputeColumn[viewer.column]; -- paints the column
};
 
CloseAndNewViewer: PUBLIC PROC [parent: Viewer] = { [] ← DoCloseAndNewViewer[parent] };
DoCloseAndNewViewer: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer]
RETURNS [new: ViewerClasses.Viewer] = {
TEditSelection.LockSel[primary, "DoCloseAndNewViewer"];
{ 
ENABLE 
UNWIND => TEditSelection.UnlockSel[primary];
new ← ReplaceByNewViewer[parent];
ViewerOps.PaintViewer[new, all];
ForceInitialCaret[new] };
TEditSelection.UnlockSel[primary];
};
 
NewViewer: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = { [] ← DoNewViewer[parent] };
 
DoNewViewer: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer ← 
NIL]
RETURNS [new: ViewerClasses.Viewer] = BEGIN
TEditSelection.LockSel[primary, "DoCloseAndNewViewer"];
{ 
ENABLE 
UNWIND => TEditSelection.UnlockSel[primary];
ForceInitialCaret[new ← MakeNewViewer[parent,TRUE]] };
TEditSelection.UnlockSel[primary];
END;
 
CloseAndOpenFile: 
PUBLIC 
PROC [parent: Viewer, fileNameProcViewer: Viewer ← 
NIL] = {
[] ← DoLoadFile[parent, NIL, TRUE, fileNameProcViewer] };
 
DoCloseAndOpenFile: 
PUBLIC 
PROC [parent: Viewer, fileName: 
ROPE ← 
NIL]
RETURNS [viewer: Viewer] = { RETURN [DoLoadFile[parent, fileName, TRUE]] };
 
OpenFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = {
[] ← DoOpenFile[NIL, parent] };
 
DoOpenFile: 
PUBLIC 
PROC [
fileName: ROPE ← NIL, parent, fileNameProcViewer: ViewerClasses.Viewer ← NIL]
RETURNS [viewer: ViewerClasses.Viewer] = BEGIN
file, search: Rope.ROPE;
IF fileName=NIL THEN fileName ← ViewerTools.GetSelectionContents[];
IF ~PreOpenCheck[fileName] THEN RETURN;
IF fileNameProcViewer=NIL THEN fileNameProcViewer ← parent;
[file, search] ← GetNameAndSearchRope[fileName, fileNameProcViewer];
IF (viewer ← DoOpen[file,parent]) # NIL THEN DoSearch[viewer, search];
END;
 
CloseAndOpenImplFile: 
PUBLIC 
PROC [parent: Viewer] = { [] ← DoCloseAndOpenImplFile[parent, 
NIL] };
 
DoCloseAndOpenImplFile: 
PUBLIC 
PROC [parent: Viewer, fileName: 
ROPE ← 
NIL]
RETURNS [viewer: Viewer] = { RETURN [DoLoadImplFile[parent, fileName, TRUE]] };
 
OpenImplFile: 
PUBLIC 
PROC [parent: ViewerClasses.Viewer] = 
BEGIN
[] ← DoOpenImplFile[NIL, parent];
END;
 
DoOpenImplFile: 
PUBLIC 
PROC [fileName: 
ROPE ← 
NIL, parent: ViewerClasses.Viewer ← 
NIL]
RETURNS [viewer: Viewer] = BEGIN
impl, search: Rope.ROPE;
IF fileName=NIL THEN fileName ← ViewerTools.GetSelectionContents[];
IF ~PreOpenCheck[fileName] THEN RETURN;
[impl, search] ← GetImplNameAndSearchRope[fileName];
IF (viewer ← DoOpen[impl,parent]) # NIL THEN DoSearch[viewer, search];
END;
 
PreOpenCheck: 
PROC [sel: Rope.
ROPE] 
RETURNS [
BOOLEAN] = {
IF Rope.Size[sel]=0 THEN {PleaseSelectFileName[]; RETURN[FALSE]};
IF ~CheckFileName[sel] THEN RETURN[FALSE];
RETURN [TRUE] };
 
DoOpen: 
PROC [file: Rope.
ROPE, parent: Viewer, place: TextNode.Offset ← 0]
RETURNS [viewer: Viewer] = BEGIN
-- this needs to work ok in case parent=NIL
new: BOOLEAN ← FALSE;
directoryName: Rope.ROPE;
[directoryName,new] ← GetDirectoryName[file];
IF new THEN { UnknownFile[directoryName]; RETURN [NIL] };
viewer ← DoLoad[MakeNewViewer[parent,TRUE],directoryName,FALSE,place];
END;
 
DefaultMenus: 
PUBLIC PROC [viewer: Viewer, paint: 
BOOL ← 
FALSE] = 
BEGIN
menu: Menus.Menu ← viewer.menu;
num: INTEGER ← 1;
DoLine: 
PROC [which: TEditProfile.DefaultMenuChoice] = {
entry: Menus.MenuEntry ← 
SELECT which 
FROM
places => findMenu, levels => levelMenu, ENDCASE => NIL;
IF entry = NIL THEN RETURN;
Menus.SetLine[menu, num, entry];
num ← num+1 };
 
DoLine[TEditProfile.menu1]; DoLine[TEditProfile.menu2]; DoLine[TEditProfile.menu3];
ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh];
IF paint THEN ViewerOps.PaintViewer[viewer, all];
END;
 
NodeProps.Register[$Viewer, NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
NodeProps.Register[$FileCreateDate, NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
NodeProps.Register[$FileExtension, NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
END.