-- 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.