TEditDocuments2Impl.mesa
Paxton on June 7, 1983 9:38 am
McGregor on September 10, 1982 1:53 pm
Maxwell, January 6, 1983 11:06 am
Plass, April 15, 1983 1:10 pm
Russ Atkinson, September 26, 1983 3:20 pm
L. Stewart, November 3, 1983 3:28 pm
Doug Wyatt, December 15, 1983 6:42 pm
DIRECTORY
AMMiniModel USING [ImplementorName],
FS USING [ComponentPositions, Error, ExpandName, FileInfo, GetName, nullOpenFile, Open, OpenFile],
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, Flatten, ROPE, Size, SkipTo, Substr],
RopeEdit USING [BlankChar, IllegalChar],
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,
TEditDocumentPrivateExtras USING [InitViewerDocInternal],
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],
WorldVM USING [LocalWorld, World];
TEditDocuments2Impl: CEDAR MONITOR
IMPORTS AMMiniModel, FS, Menus, MessageWindow, NodeProps, Process, Rope, RopeEdit, TEditDocument, TEditDocumentPrivate, TEditDocumentPrivateExtras, TextEdit, TextNode, TEditDisplay, TEditInput, TEditInputOps, TEditProfile, TEditSelection, ViewerOps, ViewerTools, VirtualDesktops, WorldVM
EXPORTS TEditDocument, TEditDocumentPrivate, TEditOps
SHARES ViewerClasses =
BEGIN OPEN Rope, TEditDocument, TEditDocumentPrivate, ViewerClasses;
ROPE: TYPE = Rope.ROPE;
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]]] };
GetFileName: PROC[file: FS.OpenFile, removeVersion: BOOLFALSE]
RETURNS[name: ROPENIL] = {
name ← FS.GetName[file ! FS.Error => CONTINUE].fullFName;
IF name#NIL AND removeVersion THEN name ← name.Flatten[0, name.SkipTo[0, "!"]];
};
GetCreateName: PROC[name: ROPE] RETURNS[ROPE] = {
cp: FS.ComponentPositions;
[fullFName: name, cp: cp] ← FS.ExpandName[name ! FS.Error => GOTO Fail];
RETURN[name.Flatten[start: cp.subDirs.start, len: cp.ext.start+cp.ext.length-cp.subDirs.start]];
Include subDirs, base, and extension (omit server, dir, and version)
EXITS Fail => {
TRUSTED { Process.Detach[FORK IllegalFileName[]] };
RETURN[NIL];
};
};
IsNewFile: PROC[name: ROPE] RETURNS[new: BOOLFALSE] = {
[] ← FS.FileInfo[name ! FS.Error => { new ← TRUE; CONTINUE }];
};
PreStore: PUBLIC Menus.MenuProc = { -- called when Store menu item becomes unguarded
sel: ROPE ← ViewerTools.GetSelectionContents[];
fileName: ROPENIL; new: BOOLFALSE;
IF Rope.Size[sel]=0 THEN {PleaseSelectFileName[]; RETURN};
fileName ← GetCreateName[sel];
IF fileName=NIL THEN RETURN;
new ← IsNewFile[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] };
checkForInViewer: BOOLFALSE;
DoStoreFile: PUBLIC PROC [parent: ViewerClasses.Viewer, fileName: ROPENIL] = BEGIN
linked: BOOL;
tdd: TEditDocumentData;
IF fileName=NIL THEN fileName ← ViewerTools.GetSelectionContents[];
IF Rope.Size[fileName]=0 THEN {PleaseSelectFileName[]; RETURN};
fileName ← GetCreateName[fileName];
IF fileName=NIL THEN RETURN;
IF checkForInViewer AND
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 LoadHistoryEtc;
[] ← SpinAndLock[tdd, "EmptyViewer"];
KillSelections[parent];
SaveLoadHistory[parent];
prop ← NARROW[ViewerOps.FetchProp[parent, $LoadHistoryEtc]]; -- 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, $LoadHistoryEtc, 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] RETURNS [BOOLEAN] = {
FOR i: INT 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] = {
MessageWindow.Append[directoryName, TRUE];
MessageWindow.Append[" not found."];
MessageWindow.Blink[];
RETURN};
TryToOpen: PROC[name: ROPE] RETURNS[file: FS.OpenFile] = {
file ← FS.Open[name ! FS.Error => { file ← FS.nullOpenFile; CONTINUE }];
};
FileExists: PROC[file: FS.OpenFile] RETURNS[BOOL] = INLINE { RETURN[file#FS.nullOpenFile] };
TryExtensions: PROC[name: ROPE, extensions: LIST OF ROPE] RETURNS[FS.OpenFile] = {
FOR lst: LIST OF ROPE ← extensions, lst.rest UNTIL lst=NIL DO
file: FS.OpenFile ~ TryToOpen[name.Concat[lst.first]];
IF FileExists[file] THEN RETURN[file];
ENDLOOP;
RETURN[FS.nullOpenFile];
};
IsAnExtension: PROC[ext: ROPE, extensions: LIST OF ROPE] RETURNS[BOOL] = {
FOR lst: LIST OF ROPE ← extensions, lst.rest UNTIL lst=NIL DO
IF Rope.Equal[ext, lst.first, FALSE] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
fileNameProc: PROC [ROPE, ViewerClasses.Viewer]
RETURNS [fileName: ROPE, search: ROPE] ← NIL;
RegisterFileNameProc: PUBLIC PROC [proc: PROC [ROPE, ViewerClasses.Viewer]
RETURNS [fileName: ROPE, search: ROPE]] = { fileNameProc ← proc };
TryFileNameProc: PROC[sel: ROPE, viewer: ViewerClasses.Viewer]
RETURNS[file: FS.OpenFile ← FS.nullOpenFile, search: ROPE] = {
name, srch: ROPENIL;
IF fileNameProc#NIL THEN [name, srch] ← fileNameProc[sel, viewer];
IF name#NIL THEN {
file ← TryToOpen[name];
IF FileExists[file] THEN RETURN[file, srch];
};
RETURN[FS.nullOpenFile, NIL];
};
HasVersion: PROC[name: ROPE] RETURNS[BOOL] = {
RETURN[name.Find["!"]>=0];
};
LookupProc: TYPE = PROC[sel: ROPE, fileNameProcViewer: Viewer ← NIL]
RETURNS[name: ROPENIL, file: FS.OpenFile, search: ROPENIL];
LookupFile: LookupProc = {
MessageWindow.Append[Rope.Concat["Directory lookup for ", sel], TRUE];
First, try the entire selection as the file name
file ← TryToOpen[sel];
IF HasVersion[sel] THEN { -- explicit version number
IF FileExists[file] THEN name ← GetFileName[file: file, removeVersion: FALSE];
RETURN; -- give up if the file was not found
};
IF NOT FileExists[file] THEN {
dot: INT ~ sel.Find["."]; -- find the first dot, if any, in the selection
extensions: LIST OF ROPE ~ TEditProfile.sourceExtensions;
If there is no dot, try appending extensions to sel to get a full file name.
IF dot<0 THEN {
file ← TryExtensions[sel, extensions];
IF NOT FileExists[file] THEN [file, search] ← TryFileNameProc[sel, fileNameProcViewer];
}
If there is a dot, separate sel into "name" (before dot) and "ext" (dot and after) parts.
If ext is not a standard extension, try appending extensions to name to get a full file name.
ELSE {
Try the file name proc first (for example, the extension might be misspelled).
[file, search] ← TryFileNameProc[sel, fileNameProcViewer];
IF NOT FileExists[file] AND NOT IsAnExtension[sel.Substr[dot], extensions] THEN {
base: ROPE ~ sel.Substr[0, dot]; -- the text before the "."
search ← sel.Substr[dot+1]; -- the text after the "."
file ← TryExtensions[base, extensions];
};
};
};
IF FileExists[file] THEN name ← GetFileName[file: file, removeVersion: TRUE];
};
LookupImpl: LookupProc = {
defs, impl: ROPENIL;
dot: INT ~ sel.Find["."]; -- find the first dot, if any
IF dot<0 THEN { defs ← sel; search ← NIL } -- no dot
ELSE { defs ← sel.Substr[0, dot]; search ← sel.Substr[dot+1] };
IF search.Size>0 THEN {
world: WorldVM.World;
MessageWindow.Append[Rope.Concat["Model lookup for implementor of ", search], TRUE];
TRUSTED { world ← WorldVM.LocalWorld[] };
impl ← AMMiniModel.ImplementorName[defs, search, world];
};
IF impl=NIL THEN impl ← defs.Concat["Impl"];
MessageWindow.Append[Rope.Concat["Directory lookup for ", impl], TRUE];
file ← TryExtensions[impl, TEditProfile.implExtensions];
IF FileExists[file] THEN name ← GetFileName[file: file, removeVersion: TRUE];
};
DoSearch: PROC [v: Viewer, search: 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: INTMAX[0, MIN[loc.where, TextEdit.Size[node]-1]];
backStop: INTMAX[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]]};
};
LoadHistoryEtc: TYPE = RECORD [
name: ROPE,
place: TextNode.Offset
];
CopyLoadHistory: PUBLIC PROC [from, to: Viewer] = {
old: REF LoadHistoryEtc ← NARROW[ViewerOps.FetchProp[from, $LoadHistoryEtc]];
new: REF LoadHistoryEtc ← NARROW[ViewerOps.FetchProp[to, $LoadHistoryEtc]];
IF old=NIL THEN RETURN;
IF new=NIL THEN {
new ← TextNode.pZone.NEW[LoadHistoryEtc];
ViewerOps.AddProp[to, $LoadHistoryEtc, 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 LoadHistoryEtc;
propName: ROPE;
tdd ← NARROW[viewer.data];
IF tdd = NIL THEN RETURN;
[] ← SpinAndLock[tdd, "PreLoadPrevious"]; -- delay until after other op completes
prop ← NARROW[ViewerOps.FetchProp[viewer, $LoadHistoryEtc]];
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 LoadHistoryEtc;
propName: ROPE;
propPlace: TextNode.Offset;
file: FS.OpenFile;
tdd ← NARROW[parent.data];
IF tdd = NIL THEN RETURN;
[] ← SpinAndLock[tdd, "DoLoadPreviousFile"]; -- delay until after other op completes
prop ← NARROW[ViewerOps.FetchProp[parent, $LoadHistoryEtc]];
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;
file ← TryToOpen[propName];
IF FileExists[file] THEN {
IF open AND ~close THEN [] ← DoOpen[name: propName, file: file, parent: parent,
place: propPlace]
ELSE [] ← DoLoad[viewer: parent, name: propName, file: file, close: close,
place: propPlace];
}
ELSE UnknownFile[propName];
END;
CheckAnonymous: PROC [parent: ViewerClasses.Viewer, txt: 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;
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,
close: BOOLFALSE, fileNameProcViewer: ViewerClasses.Viewer ← NIL]
RETURNS [viewer: ViewerClasses.Viewer] = {
RETURN[DoLoadAndSearch[parent: parent, txt: txt, lookup: LookupFile,
close: close, fileNameProcViewer: fileNameProcViewer]];
};
AnonymousLoadImplFile: PUBLIC PROC [parent: ViewerClasses.Viewer] = {
txt: 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, close: BOOLFALSE]
RETURNS [viewer: Viewer] = {
RETURN[DoLoadAndSearch[parent: parent, txt: txt, lookup: LookupImpl,
close: close, fileNameProcViewer: NIL]];
};
LoadFile: PUBLIC PROC [parent: ViewerClasses.Viewer] = {
[] ← DoLoadFile[parent, NIL, FALSE] };
DoLoadFile: PUBLIC PROC [
parent: ViewerClasses.Viewer, fileName: ROPENIL, close: BOOLFALSE,
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: ROPENIL, close: BOOLFALSE]
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] RETURNS [BOOLEAN] = {
IF Rope.Size[sel]=0 THEN {
PleaseSelectFileName[];
RETURN[FALSE]};
IF ~CheckFileName[sel] THEN RETURN[FALSE];
RETURN [TRUE] };
AllocLoadHistoryEtc: PROC [viewer: Viewer] RETURNS [prop: REF LoadHistoryEtc] = {
IF (prop ← NARROW[ViewerOps.FetchProp[viewer, $LoadHistoryEtc]]) # NIL THEN RETURN;
prop ← TextNode.pZone.NEW[LoadHistoryEtc];
ViewerOps.AddProp[viewer, $LoadHistoryEtc, prop] };
SetLoadHistoryInfo: PROC [viewer: Viewer, prop: REF LoadHistoryEtc] = {
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
IF tdd = NIL THEN RETURN;
prop.name ← viewer.name;
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 LoadHistoryEtc ← AllocLoadHistoryEtc[viewer];
SetLoadHistoryInfo[parent, prop] };
SaveLoadHistory: PROC [viewer: Viewer] = {
prop: REF LoadHistoryEtc ← AllocLoadHistoryEtc[viewer];
SetLoadHistoryInfo[viewer, prop] };
DoLoadAndSearch: PROC[parent: Viewer, txt: ROPE, lookup: LookupProc,
close: BOOLFALSE, fileNameProcViewer: Viewer ← NIL]
RETURNS[viewer: Viewer] = {
name: ROPE; file: FS.OpenFile; search: ROPE;
IF fileNameProcViewer=NIL THEN fileNameProcViewer ← parent;
[name, file, search] ← lookup[txt, fileNameProcViewer];
IF FileExists[file] THEN {
viewer ← DoLoad[viewer: parent, name: name, file: file, close: close];
IF viewer#NIL AND search#NIL THEN DoSearch[viewer, search];
}
ELSE { UnknownFile[txt]; RETURN[NIL] };
};
DoLoad: PROC [viewer: Viewer, name: ROPE, file: FS.OpenFile,
close: BOOLFALSE, place: TextNode.Offset ← 0]
RETURNS [Viewer] = BEGIN
tdd: TEditDocument.TEditDocumentData;
oldViewer, destroyViewer: Viewer;
tddOld: TEditDocument.TEditDocumentData;
clearMessage: BOOLTRUE;
fileName: ROPE ← GetFileName[file];
IF Rope.Equal[viewer.file, fileName, FALSE] THEN {
MessageWindow.Append[Rope.Concat[fileName," is already loaded."],TRUE];
RETURN [viewer] };
MessageWindow.Append[Rope.Concat["Loading ", name],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, name, 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[name];
viewer.name ← name; viewer.file ← fileName;
viewer.newVersion ← viewer.newFile ← FALSE;
IF oldViewer # NIL AND NOT oldViewer.destroyed AND (tddOld ← NARROW[oldViewer.data]) # NIL THEN { -- link them
viewer.name ← oldViewer.name;
viewer.file ← oldViewer.file;
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[name]] };
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[name];
tdd.text ← NIL; -- so InitViewerDoc won't worry about freeing it
IF root # NIL THEN {
TRUSTED {Process.Detach[FORK ReloadedMessage[name]] }; -- can deadlock if don't fork
clearMessage ← FALSE;
};
TEditDocumentPrivateExtras.InitViewerDocInternal[viewer, file, root];
viewer.newFile ← FALSE;
viewer.newVersion ← root#NIL;
};
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] = {
MessageWindow.Append[Rope.Concat[name," was already loaded."],TRUE] };
ReloadedMessage: PROC [name: 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: BOOLTRUE]
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: ROPENIL]
RETURNS [viewer: Viewer] = { RETURN [DoLoadFile[parent, fileName, TRUE]] };
OpenFile: PUBLIC PROC [parent: ViewerClasses.Viewer] = {
[] ← DoOpenFile[NIL, parent] };
DoOpenFile: PUBLIC PROC [
fileName: ROPENIL, parent, fileNameProcViewer: ViewerClasses.Viewer ← NIL]
RETURNS [viewer: ViewerClasses.Viewer] = BEGIN
RETURN[DoOpenAndSearch[txt: fileName, lookup: LookupFile, parent: parent,
fileNameProcViewer: fileNameProcViewer]]
END;
CloseAndOpenImplFile: PUBLIC PROC [parent: Viewer] = { [] ← DoCloseAndOpenImplFile[parent, NIL] };
DoCloseAndOpenImplFile: PUBLIC PROC [parent: Viewer, fileName: ROPENIL]
RETURNS [viewer: Viewer] = { RETURN [DoLoadImplFile[parent, fileName, TRUE]] };
OpenImplFile: PUBLIC PROC [parent: ViewerClasses.Viewer] = BEGIN
[] ← DoOpenImplFile[NIL, parent];
END;
DoOpenImplFile: PUBLIC PROC [fileName: ROPENIL, parent: ViewerClasses.Viewer ← NIL]
RETURNS [viewer: Viewer] = BEGIN
RETURN[DoOpenAndSearch[txt: fileName, lookup: LookupImpl, parent: parent]]
END;
PreOpenCheck: PROC [sel: ROPE] RETURNS [BOOLEAN] = {
IF Rope.Size[sel]=0 THEN {PleaseSelectFileName[]; RETURN[FALSE]};
IF ~CheckFileName[sel] THEN RETURN[FALSE];
RETURN [TRUE] };
DoOpenAndSearch: PROC[txt: ROPE, lookup: LookupProc,
parent: Viewer ← NIL, fileNameProcViewer: Viewer ← NIL]
RETURNS[viewer: Viewer] = {
name: ROPE; file: FS.OpenFile; search: ROPE;
IF txt=NIL THEN txt ← ViewerTools.GetSelectionContents[];
IF ~PreOpenCheck[txt] THEN RETURN[NIL];
IF fileNameProcViewer=NIL THEN fileNameProcViewer ← parent;
[name, file, search] ← lookup[txt, fileNameProcViewer];
IF FileExists[file] THEN {
viewer ← DoOpen[name: name, file: file, parent: parent];
IF viewer#NIL AND search#NIL THEN DoSearch[viewer, search];
}
ELSE { UnknownFile[txt]; RETURN[NIL] };
};
DoOpen: PROC [name: ROPE, file: FS.OpenFile, parent: Viewer,
place: TextNode.Offset ← 0]
RETURNS [viewer: Viewer] = BEGIN
-- this needs to work ok in case parent=NIL
viewer ← DoLoad[viewer: MakeNewViewer[parent,TRUE], name: name, file: file,
close: FALSE, place: place];
END;
DefaultMenus: PUBLIC PROC [viewer: Viewer, paint: BOOLFALSE] = 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.