DIRECTORY
AMMiniModel USING [ImplementorName],
BasicTime USING [GMT, nullGMT, Period],
Convert USING [Error, IntFromRope],
FS USING [ComponentPositions, Error, ExpandName, FileInfo, GetName, nullOpenFile, Open, OpenFile],
Menus USING [Menu, MenuEntry, MenuProc, SetLine],
MessageWindow USING [Append, Blink, Clear],
NodeProps USING [GetProp, NullCopy, NullRead, NullWrite, PutProp, Register],
Rope USING [Concat, Equal, Fetch, Flatten, Find, Match, ROPE, Size, SkipTo, Substr],
TEditDisplay USING [EstablishLine],
TEditDocument USING [maxClip, Selection, SpinAndLock, TEditDocumentData, Unlock],
TEditDocumentPrivate USING [findMenu, FindUnsavedDocument, InitViewerDoc, InitViewerDocInternal, levelMenu, PositionHistory, RecordUnsavedDocument],
TEditInput USING [FreeTree],
TEditInputOps USING [WaitForInsertToFinish],
TEditOps USING [FileNameProc],
TEditProfile USING [DefaultMenuChoice, implExtensions, menu1, menu2, menu3, openFirstLevelOnly, sourceExtensions, tryVersionMap],
TEditSelection USING [FindRope, fSel, LockSel, MakeSelection, pSel, sSel, UnlockSel],
TEditSelectionOps USING [ShowGivenPosition],
TextEdit USING [Size],
TextNode USING [Location, LocNumber, LocWithin, NarrowToTextNode, nullLocation, Ref, RefTextNode, Root],
VersionMap USING [MapList, Range, RangeList, RangeToEntry, ShortNameToRanges],
VersionMapDefaults USING [GetMapList],
ViewerClasses USING [Column, Viewer],
ViewerForkers USING [CallBack, ForkCall, ForkPaint],
ViewerGroupLocks USING [CallRootAndLinksUnderWriteLock],
ViewerLocks USING [CallUnderColumnLocks],
ViewerOps USING [AddProp, BlinkViewer, CloseViewer, ComputeColumn, CreateViewer, DestroyViewer, EnumerateViewers, EnumProc, EstablishViewerPosition, FetchProp, MoveBelowViewer, OpenIcon, PaintHint, PaintViewer, SaveViewer],
ViewerPrivate USING [AcquireWriteLocks, ReleaseWriteLocks],
ViewerTools USING [GetContents, GetSelectionContents, SelPos, SelPosRec, SetSelection],
WorldVM USING [LocalWorld];
QueryTEditDocuments2Impl:
CEDAR
MONITOR
IMPORTS AMMiniModel, BasicTime, Convert, FS, Menus, MessageWindow, NodeProps, Rope, TEditDocument, TEditDocumentPrivate, TextEdit, TextNode, TEditDisplay, TEditInput, TEditInputOps, TEditProfile, TEditSelection, TEditSelectionOps, VersionMap, VersionMapDefaults, ViewerForkers, ViewerGroupLocks, ViewerLocks, ViewerOps, ViewerPrivate, ViewerTools, WorldVM
EXPORTS TEditDocument, TEditDocumentPrivate, TEditOps
= BEGIN
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
Column: TYPE = ViewerClasses.Column;
File: TYPE = FS.OpenFile;
noFile: File = FS.nullOpenFile;
initialCaret: ViewerTools.SelPos ←
NEW[ViewerTools.SelPosRec ← []];
ColumnOf:
PROC [parent: Viewer]
RETURNS [Column] = {
RETURN [IF parent # NIL THEN parent.column ELSE left]
};
ForceInitialCaret:
PROC [viewer: Viewer] = {
ViewerTools.SetSelection[viewer, initialCaret];
};
lastRoot: TextNode.Ref;
GetViewerForRootI:
INTERNAL
PROC [root: TextNode.Ref]
RETURNS [viewer: Viewer] = {
IF root = NIL THEN RETURN [NIL];
IF root = lastRoot THEN RETURN [lastViewer];
lastRoot ← root;
RETURN [lastViewer ← NARROW[NodeProps.GetProp[root,$Viewer]]];
};
RecordViewerForRootI:
INTERNAL
PROC [viewer: Viewer, root: TextNode.Ref] = {
NodeProps.PutProp[root,$Viewer,viewer];
IF root = lastRoot THEN lastViewer ← viewer; -- keep the cache up to date
};
GetViewerForRoot:
PUBLIC
ENTRY
PROC [root: TextNode.Ref]
RETURNS [viewer: Viewer] = {
ENABLE UNWIND => NULL;
RETURN [GetViewerForRootI[root]];
};
RecordViewerForRoot:
PUBLIC
ENTRY
PROC [viewer: Viewer, root: TextNode.Ref] = {
ENABLE UNWIND => NULL;
RecordViewerForRootI[viewer, root];
};
ForgetViewer:
PUBLIC
ENTRY
PROC [viewer: 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];
};
LoadHistory:
TYPE =
RECORD[name:
ROPE, place:
INT];
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 ← NEW[LoadHistory];
ViewerOps.AddProp[to, $LoadHistory, new] };
new^ ← old^;
};
AllocLoadHistory:
PROC [viewer: Viewer]
RETURNS [prop:
REF LoadHistory] = {
IF (prop ← NARROW[ViewerOps.FetchProp[viewer, $LoadHistory]]) # NIL THEN RETURN;
prop ← 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.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 LoadHistory ← AllocLoadHistory[viewer];
SetLoadHistoryInfo[parent, prop];
};
SaveLoadHistory:
PROC [viewer: Viewer] = {
prop: REF LoadHistory ← AllocLoadHistory[viewer];
SetLoadHistoryInfo[viewer, prop];
};
PositionHistory:
TYPE = TEditDocumentPrivate.PositionHistory;
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 ← 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 ← NEW[PositionHistory];
ViewerOps.AddProp[viewer, $PositionHistory, prop] };
[] ← TEditDocument.SpinAndLock[tdd, "RememberCurrentPosition"];
loc ← tdd.lineTable.lines[0].pos;
TEditDocument.Unlock[tdd];
IF loc = prop.pos THEN RETURN;
prop.prev ← prop.pos; prop.pos ← loc;
};
PositionViewer:
PUBLIC
PROC [viewer: Viewer, loc: TextNode.Location, hint: ViewerOps.PaintHint ← client]
RETURNS [ok:
BOOL ←
FALSE] = {
ok ← PositionViewerInternal[viewer, loc];
IF ok THEN ForkViewerPainter[viewer, hint];
};
PositionViewerInternal:
PROC [viewer: Viewer, loc: TextNode.Location]
RETURNS [ok:
BOOL ←
FALSE] = {
CheckPosition:
PROC [viewer: Viewer, loc: TextNode.Location]
RETURNS [good:
BOOL, 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];
};
WITH viewer.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => {
node: TextNode.RefTextNode;
[ok, loc] ← CheckPosition[viewer, loc];
IF ok
THEN {
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];
};
};
ENDCASE;
};
CloseAndForkPaint:
PROC [viewer: Viewer] =
TRUSTED {
ViewerOps.CloseViewer[viewer, FALSE];
ViewerForkers.ForkPaint[viewer, all];
};
ForkViewerPainter:
PROC [viewer: Viewer, hint: ViewerOps.PaintHint] =
TRUSTED {
ViewerForkers.ForkPaint[viewer, hint];
};
ViewerPainter:
PROC [viewer: Viewer, hint: ViewerOps.PaintHint] = {
ENABLE ABORTED => CONTINUE;
All calls in this module to ViewerOps.PaintViewer go through here to aid debugging.
(note that calls to ComputeColumn also paint the screen)
ViewerOps.PaintViewer[viewer, hint];
};
KillSelections:
PUBLIC
PROC [parent: 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];
};
CancelLinks:
PUBLIC
PROC [viewer: Viewer] = {
ForgetViewer[viewer];
IF viewer.link.link=viewer
THEN {
viewer.link.link ← NIL;
ForkViewerPainter[viewer.link, caption];
}
ELSE
FOR v: Viewer ← viewer.link.link, v.link
UNTIL v.link=viewer
DO
REPEAT FINISHED => v.link ← viewer.link;
ENDLOOP;
viewer.link ← NIL;
};
DefaultMenus:
PUBLIC
PROC [viewer: Viewer, paint:
BOOL ←
FALSE] = {
menu: Menus.Menu ← viewer.menu;
num: INTEGER ← 1;
DoLine:
PROC [which: TEditProfile.DefaultMenuChoice] = {
entry: Menus.MenuEntry ←
SELECT which
FROM
places => TEditDocumentPrivate.findMenu,
levels => TEditDocumentPrivate.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 ForkViewerPainter[viewer, all];
};
MakeNewViewer:
PROC [column: Column, parent: Viewer, wDir:
ROPE ←
NIL]
RETURNS [viewer: Viewer] = {
Makes a new, empty, $Text viewer, placing it under the parent viewer (if any, and in column).
viewer ← MakeNewIcon[column, parent, wDir];
IF parent =
NIL
OR parent.column # column
THEN ViewerOps.OpenIcon[icon: viewer, paint: TRUE]
ELSE {
inner:
PROC = {
IF parent.destroyed THEN RETURN;
IF parent.iconic THEN RETURN;
IF NOT viewer.iconic THEN RETURN;
IF parent.column # column THEN RETURN; -- parent.column must have changed!
copy height and position info from old to new
viewer.openHeight ← parent.openHeight;
ViewerOps.OpenIcon[icon: viewer, paint: FALSE];
ViewerOps.MoveBelowViewer[static: parent, altered: viewer, paint: FALSE];
ViewerOps.ComputeColumn[column];
};
IF TEditProfile.openFirstLevelOnly
THEN
WITH viewer.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => tdd.clipLevel ← 1;
ENDCASE;
ViewerLocks.CallUnderColumnLocks[inner, column, static];
};
};
MakeNewIcon:
PROC [column: Column, parent: Viewer ←
NIL, wDir:
ROPE ←
NIL]
RETURNS [viewer: Viewer] = {
... returns a new iconic $Text viewer with default menus, no painting done.
IF wDir=NIL THEN wDir ← WorkingDirectoryFromViewer[parent];
IF column=static THEN column ← left;
viewer ← ViewerOps.CreateViewer[
flavor: $QueryText,
info: [name: wDir, column: column, iconic: TRUE],
paint: FALSE];
IF TEditProfile.openFirstLevelOnly
THEN
WITH viewer.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => tdd.clipLevel ← 1;
ENDCASE;
DefaultMenus[viewer];
};
ReplaceByNewViewer:
PROC [parent: Viewer, wDir:
ROPE ←
NIL]
RETURNS [viewer: Viewer] = {
viewer ← MakeNewIcon[column: ColumnOf[parent], parent: parent, wDir: wDir];
SwapViewers[parent, viewer];
};
SwapViewers:
PROC [parent: Viewer, icon: Viewer] = {
Must lock both columns
inner:
PROC = {
IF parent.destroyed THEN RETURN;
IF parent.iconic THEN RETURN;
IF NOT icon.iconic THEN RETURN;
IF parent.column # col THEN RETURN;
copy height and position info from old to new
icon.openHeight ← parent.openHeight;
ViewerOps.OpenIcon[icon: icon, paint: FALSE];
ViewerOps.MoveBelowViewer[static: parent, altered: icon, paint: FALSE];
CloseAndForkPaint[parent];
ViewerOps.ComputeColumn[col];
};
col: ViewerClasses.Column = parent.column;
KillSelections[parent]; -- need to deselect before making new viewer
ViewerLocks.CallUnderColumnLocks[inner, col, static];
};
FindOldViewer:
PROC [name:
ROPE, viewer: Viewer]
RETURNS [old: Viewer ←
NIL] = {
Match: ViewerOps.EnumProc
-- PROC[v: Viewer] RETURNS[BOOL ← TRUE] -- = {
IF v#viewer AND Rope.Equal[name, v.name, FALSE] THEN { old ← v; RETURN [FALSE] };
};
ViewerOps.EnumerateViewers[Match];
};
Report:
PROC [m1, m2, m3, m4:
ROPE ←
NIL, flash:
BOOL ←
FALSE] = {
ENABLE ABORTED => CONTINUE;
MessageWindow.Append[m1, TRUE];
IF m2#NIL THEN MessageWindow.Append[m2];
IF m3#NIL THEN MessageWindow.Append[m3];
IF m4#NIL THEN MessageWindow.Append[m4];
IF flash THEN MessageWindow.Blink[];
};
Flash:
PROC [m1, m2:
ROPE ←
NIL] = {
Report[m1: m1, m2: m2, flash: TRUE];
};
PleaseSelectFileName:
PROC = {
Flash["Please select file name."];
};
IllegalFileName: ViewerForkers.CallBack = {
Flash["Illegal file name."];
};
ReloadedMessage: ViewerForkers.CallBack = {
WITH data
SELECT
FROM
name: ROPE => Flash[name," restored with previous unsaved edits."];
ENDCASE;
};
RemoveVersion:
PROC [x:
ROPE]
RETURNS [
ROPE] = {
RETURN [x.Flatten[len: x.SkipTo[skip: "!"]]];
};
GetFileName:
PROC [file:
FS.OpenFile, removeVersion:
BOOL ←
FALSE]
RETURNS [name:
ROPE ←
NIL] = {
name ← FS.GetName[file ! FS.Error => CONTINUE].fullFName;
IF name#NIL AND removeVersion THEN name ← RemoveVersion[name];
};
LoadOp: TYPE ~ {load, open, replace};
LookupType:
TYPE ~ {source, impl};
DoGet:
PROC [parent: Viewer, column: Column, file:
FS.OpenFile, specificVersion:
BOOL, op: LoadOp ← $load, place:
INT ← 0, search:
ROPE ←
NIL, forceOpen:
BOOL ←
TRUE]
RETURNS [viewer: Viewer] = {
viewerIn: Viewer ← parent;
needPaint: BOOL ← FALSE;
loaded: BOOL ← FALSE;
IF parent = NIL THEN op ← open;
SELECT op
FROM
load => viewerIn ← parent;
open => viewerIn ← MakeNewIcon[column: column, parent: parent];
replace => viewerIn ← ReplaceByNewViewer[parent];
ENDCASE => ERROR;
[viewer, loaded] ← DoLoad[viewer: viewerIn, file: file, op: op, specificVersion: specificVersion, place: place];
IF viewer #
NIL
THEN {
We were able to do the load
IF viewer # viewerIn
THEN {
Humph! A previous viewer existed!
Report[viewer.name, " was already loaded."];
ViewerOps.BlinkViewer[viewer: viewer, milliseconds: 200];
SELECT op
FROM
open, replace =>
TRUSTED {
We created a viewer to hold the result, but we will never use it now.
ViewerForkers.ForkCall[viewerIn, FlameOut, viewerIn];
viewerIn ← NIL};
ENDCASE;
};
IF Rope.Size[search] # 0
THEN
WITH viewer.data
SELECT
FROM
ntdd: TEditDocument.TEditDocumentData => {
We need to perform a search for the given definition.
loc: TextNode.Location ← TextNode.LocWithin[ntdd.text, 0];
sel: TEditDocument.Selection ← NIL;
IF Rope.Match["|*", search]
THEN {
This is a position spec to parse
pos:
INT ← Convert.IntFromRope[Rope.Flatten[search, 1]
! Convert.Error => GO TO faulty];
nloc: TextNode.Location ← TextNode.LocWithin[n: ntdd.text, count: pos, skipCommentNodes: TRUE];
[] ← PositionViewerInternal[viewer, nloc];
TEditSelectionOps.ShowGivenPosition[viewer, pos];
EXITS faulty => {};
}
ELSE {
This is a definition to search for
TEditDisplay.EstablishLine[ntdd, loc];
TEditSelection.FindRope[
viewer: viewer, rope: search, case: TRUE, word: TRUE, def: TRUE, id: feedback];
sel ← TEditSelection.fSel;
IF sel #
NIL
AND sel.viewer = viewer
THEN
capture the location found and establish it as the place to display
loc ← sel.start.pos;
TEditDisplay.EstablishLine[ntdd, loc];
};
RememberCurrentPosition[viewer];
needPaint ← TRUE;
};
ENDCASE;
SELECT
TRUE
FROM
viewer.iconic
AND forceOpen => {
RRA: For some stupid reason we have to open the icon first before moving things. I wonder why?
ViewerOps.OpenIcon[viewer];
IF parent #
NIL
AND parent # viewer
AND
NOT parent.destroyed
THEN
IF op = replace
THEN CloseAndForkPaint[parent]
ELSE ViewerOps.MoveBelowViewer[viewer, parent, TRUE];
};
loaded =>
ForkViewerPainter[viewer, all];
needPaint =>
ForkViewerPainter[viewer, client];
ENDCASE;
};
};
DoLoad:
PROC [viewer: Viewer, file:
FS.OpenFile, op: LoadOp, specificVersion:
BOOL ←
FALSE, place:
INT ← 0]
RETURNS [out: Viewer, loaded:
BOOL ←
FALSE] = {
Load a file into a viewer
viewer is the viewer to be loaded with the contents of the file
name is the specified name for the file
file is the open file
clearMessage: BOOL ← TRUE;
fileName: ROPE ~ FS.GetName[file].fullFName;
name: ROPE ← fileName;
out ← viewer;
IF NOT specificVersion THEN name ← name.Flatten[len: name.SkipTo[skip: "!"]];
IF Rope.Equal[viewer.file, fileName,
FALSE]
THEN {
Report[fileName, " is already loaded."];
ViewerOps.BlinkViewer[viewer: viewer, milliseconds: 200];
RETURN;
};
Report["Loading ", name];
KillSelections[viewer];
WITH viewer.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => {
oldViewer: Viewer ← NIL;
linkViewer: Viewer ← NIL;
vFile: ROPE ← NIL;
DO
oldViewer ← FindOldViewer[name, viewer];
IF oldViewer # NIL THEN linkViewer ← oldViewer.link;
ViewerPrivate.AcquireWriteLocks[viewer, oldViewer, linkViewer];
IF oldViewer = FindOldViewer[name, viewer]
AND (oldViewer =
NIL
OR linkViewer = oldViewer.link)
THEN
EXIT;
Note that if anything changed while we were waiting for the lock we should release our locks, then go around and try again
ViewerPrivate.ReleaseWriteLocks[viewer, oldViewer, linkViewer];
ENDLOOP;
{
ENABLE UNWIND => ViewerPrivate.ReleaseWriteLocks[viewer, oldViewer, linkViewer];
[] ← TEditDocument.SpinAndLock[tdd, "DoLoad"];
Can this deadlock? We hope not!
IF (vFile ← viewer.file) #NIL THEN SaveLoadHistory[viewer];
IF oldViewer #
NIL
THEN {
IF
NOT oldViewer.destroyed
THEN
WITH oldViewer.data
SELECT
FROM
tddOld: TEditDocument.TEditDocumentData =>
GO
TO oldCase;
An old viewer was found, so don't do the load!
ENDCASE;
EXITS oldCase => {};
};
SELECT
TRUE
FROM
oldViewer =
NIL => {
There is no other viewer with the same name
root: TextNode.Ref ~ TEditDocumentPrivate.FindUnsavedDocument[fileName];
IF viewer.link=
NIL
THEN {
ForgetViewer[viewer]; -- remove this from the root => viewer mapping
IF viewer.newVersion
AND (
NOT viewer.saveInProgress)
AND vFile#
NIL
THEN TEditDocumentPrivate.RecordUnsavedDocument[vFile, tdd.text]
ELSE TEditInput.FreeTree[tdd.text];
}
ELSE
CancelLinks[viewer];
viewer.name ← name;
viewer.file ← fileName;
viewer.newVersion ← viewer.newFile ← FALSE;
IF root #
NIL
THEN {
ViewerForkers.ForkCall[NIL, ReloadedMessage, name];
clearMessage ← FALSE;
};
tdd.text ← NIL; -- so InitViewerDoc won't worry about freeing it
TEditDocumentPrivate.InitViewerDocInternal[viewer, file, root];
viewer.newFile ← FALSE;
viewer.newVersion ← root#NIL;
loaded ← TRUE;
ClearPositionHistory[viewer];
[] ← PositionViewerInternal[viewer, TextNode.LocWithin[tdd.text, place]];
RememberCurrentPosition[viewer];
};
ENDCASE => {
Another viewer has this name, so return it (caller will handle this case)
out ← oldViewer;
};
TEditDocument.Unlock[tdd];
ViewerPrivate.ReleaseWriteLocks[viewer, oldViewer, linkViewer];
};
IF clearMessage THEN MessageWindow.Clear[];
};
ENDCASE;
};
FlameOut: ViewerForkers.CallBack = {
ENABLE ABORTED => CONTINUE;
WITH data
SELECT
FROM
v: Viewer => ViewerOps.DestroyViewer[v];
ENDCASE;
};
TryToOpen:
PROC [name:
ROPE, wDir:
ROPE ←
NIL]
RETURNS [file:
FS.OpenFile ←
FS.nullOpenFile] = {
file ←
FS.Open[name: name, wDir: wDir
! FS.Error => IF error.code=$unknownFile THEN CONTINUE];
};
TryExtensions:
PROC [name:
ROPE, wDir:
ROPE, extensions:
LIST
OF
ROPE]
RETURNS [file:
FS.OpenFile ←
FS.nullOpenFile] = {
base: ROPE ~ name.Concat["."];
FOR list:
LIST
OF
ROPE ← extensions, list.rest
UNTIL list=
NIL
DO
file ← TryToOpen[name: base.Concat[list.first], wDir: wDir];
IF file#FS.nullOpenFile THEN EXIT;
ENDLOOP;
};
IsAnExtension:
PROC [ext:
ROPE, extensions:
LIST
OF
ROPE]
RETURNS [
BOOL] = {
FOR list:
LIST
OF
ROPE ← extensions, list.rest
UNTIL list=
NIL
DO
IF Rope.Equal[ext, list.first, FALSE] THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
FileNameProc:
TYPE = TEditOps.FileNameProc;
fileNameProc: FileNameProc ←
NIL;
RegisterFileNameProc:
PUBLIC
PROC [proc: FileNameProc] = {
fileNameProc ← proc;
};
ReplaceFileNameProc:
PUBLIC
PROC [new: FileNameProc]
RETURNS [old: FileNameProc] = {
old ← fileNameProc;
fileNameProc ← new;
};
TryVersionMap:
PROC [shortName:
ROPE]
RETURNS [file:
FS.OpenFile ←
FS.nullOpenFile] = {
Adapted from VersionMapCommandsImpl.FindSource.
Stops at the first name it finds, if any.
mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Source];
ranges: VersionMap.RangeList ~ VersionMap.ShortNameToRanges[mapList, shortName];
bestName: ROPE ← NIL;
bestDate: BasicTime.GMT ← BasicTime.nullGMT;
FOR list: VersionMap.RangeList ← ranges, list.rest
UNTIL list=
NIL
DO
range: VersionMap.Range ← list.first;
WHILE range.len # 0
DO
fullName: ROPE;
created: BasicTime.GMT;
[name: fullName, created: created, next: range] ← VersionMap.RangeToEntry[range];
IF bestDate = BasicTime.nullGMT OR BasicTime.Period[from: bestDate, to: created] > 0 THEN {bestDate ← created; bestName ← fullName};
ENDLOOP;
ENDLOOP;
IF bestName #
NIL
THEN
RETURN [FS.Open[name: bestName, wantedCreatedTime: bestDate]];
};
TryVersionMapExtensions:
PROC [name:
ROPE, extensions:
LIST
OF
ROPE]
RETURNS [file:
FS.OpenFile ←
FS.nullOpenFile] = {
base: ROPE ~ name.Concat["."];
FOR list:
LIST
OF
ROPE ← extensions, list.rest
UNTIL list=
NIL
DO
file ← TryVersionMap[base.Concat[list.first]];
IF file#FS.nullOpenFile THEN EXIT;
ENDLOOP;
};
FileNotFound:
ERROR[fileName:
ROPE] =
CODE;
LookupSource:
PROC [sel:
ROPE, wDir:
ROPE, fileNameProcViewer: Viewer ←
NIL]
RETURNS [file:
FS.OpenFile ←
FS.nullOpenFile, specificVersion:
BOOL ←
FALSE, search:
ROPE ←
NIL] = {
dot: INT ← 0;
hasExtension, standardExtension, simpleName: BOOL ← FALSE;
base, ext: ROPE ← NIL;
proc: FileNameProc ~ fileNameProc;
Report["Directory lookup for ", sel];
The first pass is for the position specification (if any)
FOR i:
INT
DECREASING
IN[0..sel.Size[])
DO
SELECT sel.Fetch[i]
FROM
'| => {search ← Rope.Flatten[sel, i]; sel ← Rope.Flatten[sel, 0, i]; EXIT};
IN ['0..'9] => {};
ENDCASE => EXIT;
ENDLOOP;
FOR i:
INT
DECREASING
IN[0..sel.Size[])
DO
SELECT sel.Fetch[i]
FROM
'! => specificVersion ← TRUE;
'. => IF hasExtension THEN NULL ELSE { dot ← i; hasExtension ← TRUE };
'], '>, '/ => EXIT;
ENDCASE;
REPEAT FINISHED => simpleName ← TRUE;
ENDLOOP;
file ← TryToOpen[name: sel, wDir: wDir];
IF file#FS.nullOpenFile THEN RETURN;
IF hasExtension
OR specificVersion
THEN
NULL
ELSE {
file ← TryExtensions[name: sel, wDir: wDir, extensions: TEditProfile.sourceExtensions];
IF file#FS.nullOpenFile THEN RETURN;
};
IF proc#
NIL
THEN {
pname, psearch: ROPE;
[pname, psearch] ← proc[sel, fileNameProcViewer];
IF pname.Size[]#0
THEN {
file ← TryToOpen[name: pname, wDir: wDir];
IF file#FS.nullOpenFile THEN { search ← psearch; RETURN };
};
};
IF specificVersion THEN RETURN;
IF hasExtension
THEN {
base ← sel.Substr[len: dot];
ext ← sel.Substr[start: dot+1];
IF IsAnExtension[ext, TEditProfile.sourceExtensions] THEN standardExtension ← TRUE;
IF
NOT standardExtension
THEN {
file ← TryExtensions[name: base, wDir: wDir, extensions: TEditProfile.implExtensions];
IF file#FS.nullOpenFile THEN { search ← ext; RETURN };
};
};
IF simpleName
AND TEditProfile.tryVersionMap
THEN {
Report["Get: trying version map for ", sel];
file ← TryVersionMap[sel]; -- try sel verbatim first
IF file#FS.nullOpenFile THEN RETURN;
IF
NOT hasExtension
THEN {
file ← TryVersionMapExtensions[sel, TEditProfile.sourceExtensions];
IF file#FS.nullOpenFile THEN RETURN;
}
ELSE
IF
NOT standardExtension
THEN {
file ← TryVersionMapExtensions[base, TEditProfile.implExtensions];
IF file#FS.nullOpenFile THEN { search ← ext; RETURN };
};
};
};
LookupImpl:
PROC [sel:
ROPE, wDir:
ROPE]
RETURNS [file:
FS.OpenFile ←
FS.nullOpenFile, search:
ROPE ←
NIL] = {
cp: FS.ComponentPositions;
simpleName: BOOL ~ sel.SkipTo[skip: "[]<>/"]=sel.Size[];
xname, base, ext, impl: ROPE ← NIL;
[fullFName: xname, cp: cp] ← FS.ExpandName[name: sel, wDir: wDir];
base ← xname.Substr[start: cp.base.start, len: cp.base.length]; -- base part is defs name
ext ← xname.Substr[start: cp.ext.start, len: cp.ext.length]; -- extension part is search item
IF ext.Size[]#0
THEN {
Report["Model lookup for implementor of ", base, ".", ext];
TRUSTED{ impl ← AMMiniModel.ImplementorName[
defsName: base, itemName: ext, world: WorldVM.LocalWorld[]] };
};
IF impl=NIL THEN impl ← base.Concat["Impl"];
Report["Directory lookup for ", impl];
file ← TryExtensions[name: impl, wDir: xname.Substr[len: cp.base.start],
extensions: TEditProfile.implExtensions];
IF file#FS.nullOpenFile THEN { search ← ext; RETURN };
IF simpleName
AND TEditProfile.tryVersionMap
THEN {
Report["GetImpl: trying version map for ", impl];
file ← TryVersionMapExtensions[impl, TEditProfile.implExtensions];
IF file#FS.nullOpenFile THEN { search ← ext; RETURN };
};
ERROR FileNotFound[impl];
};
WorkingDirectoryFromViewer:
PUBLIC
PROC [parent: Viewer]
RETURNS [
ROPE] = {
name, fname: ROPE ← NIL; cp: FS.ComponentPositions;
IF parent#NIL THEN name ← parent.name;
{
ENABLE FS.Error => CONTINUE;
IF name=
NIL
OR parent.file=
NIL
THEN [fullFName: fname, cp: cp] ← FS.ExpandName[name: "*", wDir: name ! FS.Error => CONTINUE]
ELSE [fullFName: fname, cp: cp] ← FS.ExpandName[name: name ! FS.Error => CONTINUE];
RETURN [fname.Substr[len: cp.base.start]];
};
RETURN ["[]<>"];
};
IsAWorkingDirectory:
PROC [name:
ROPE]
RETURNS [
BOOL] = {
RETURN [Rope.Match["*/", name] OR Rope.Match["*>", name]];
};
CanonicalWorkingDirectory:
PROC [txt:
ROPE, wDir:
ROPE ←
NIL]
RETURNS [
ROPE] = {
fname: ROPE ← NIL; cp: FS.ComponentPositions;
[fullFName: fname, cp: cp] ←
FS.ExpandName[name: txt.Concat["*"], wDir: wDir
! FS.Error => CONTINUE];
IF fname=NIL THEN RETURN [NIL] ELSE RETURN [fname.Substr[len: cp.base.start]];
};
DoGetFile:
PROC [txt:
ROPE, column: Column, parent: Viewer, op: LoadOp ← $load, lookup: LookupType ← $source, fileNameProcViewer: Viewer ←
NIL]
RETURNS [viewer: Viewer ←
NIL] = {
parentDir: ROPE ~ WorkingDirectoryFromViewer[parent];
file: FS.OpenFile ← FS.nullOpenFile;
specificVersion: BOOL ← FALSE;
search: ROPE ← NIL;
IF txt=NIL THEN txt ← ViewerTools.GetSelectionContents[];
IF fileNameProcViewer=NIL THEN fileNameProcViewer ← parent;
IF txt.Size[]=0 THEN { PleaseSelectFileName[]; GOTO Fail };
IF IsAWorkingDirectory[txt]
THEN {
wDir: ROPE ~ CanonicalWorkingDirectory[txt, parentDir];
IF wDir=NIL THEN { Flash[txt, " is an illegal working directory."]; GOTO Fail };
SELECT op
FROM
load => DoEmptyViewer[parent: viewer ← parent, wDir: wDir];
open => viewer ← MakeNewViewer[column: column, parent: parent, wDir: wDir];
replace => viewer ← ReplaceByNewViewer[parent: parent, wDir: wDir];
ENDCASE => ERROR;
RETURN [viewer];
};
{
ENABLE {
FS.Error => { Flash[error.explanation]; GOTO Fail };
FileNotFound => { Flash[fileName, " not found."]; GOTO Fail };
};
SELECT lookup
FROM
$source => [file: file, specificVersion: specificVersion, search: search] ←
LookupSource[sel: txt, wDir: parentDir, fileNameProcViewer: fileNameProcViewer];
$impl => [file: file, search: search] ← LookupImpl[sel: txt, wDir: parentDir];
ENDCASE => ERROR;
IF file=FS.nullOpenFile THEN { Flash[txt, " not found."]; GOTO Fail };
};
viewer ← DoGet[parent, column, file, specificVersion, op, 0, search];
EXITS Fail => RETURN [NIL];
};
The following are the top level procedures that implement the Tioga file operations: they are available as menu items, system buttons, or keystrokes (LF).
Clear:
EmptyViewer[viewer]
NewViewer[viewer]
CloseAndNewViewer[viewer]
Reset:
TEditDocuments3Impl.ResetOp[viewer]
Get:
[] ← DoLoadFile[viewer, selection, FALSE]
[] ← DoOpenFile[selection, viewer]
[] ← DoCloseAndOpenFile[viewer, selection]
GetImpl:
[] ← DoLoadImplFile[viewer, selection, FALSE]
[] ← DoOpenImplFile[selection, viewer]
[] ← DoCloseAndOpenImplFile[viewer, selection]
PrevFile:
LoadPreviousFile[viewer]
OpenPreviousFile[viewer]
CloseAndOpenPreviousFile[viewer]
Store:
DoStoreFile[viewer, selection]
Save:
TEditDocuments3Impl.SaveOp[viewer]
New button:
NewViewer[NIL]
Open button:
[] ← DoOpenFile[NIL, selection]
LF keyboard command:
AnonymousLoadFile[viewer]
AnonymousLoadImplFile[viewer]
DoEmptyViewer:
PROC [parent: Viewer, wDir:
ROPE ←
NIL] = {
inner:
PROC [tdd: TEditDocument.TEditDocumentData] = {
link1: Viewer ← parent.link;
link2: Viewer ← NIL;
IF wDir=NIL THEN wDir ← WorkingDirectoryFromViewer[parent];
TEditSelection.LockSel[primary, "EmptyViewer"];
{
ENABLE UNWIND => TEditSelection.UnlockSel[primary];
prop: REF LoadHistory;
[] ← TEditDocument.SpinAndLock[tdd, "EmptyViewer"];
KillSelections[parent];
SaveLoadHistory[parent];
prop ← NARROW[ViewerOps.FetchProp[parent, $LoadHistory]]; -- hang onto it
SELECT
TRUE
FROM
link1 # NIL => CancelLinks[parent];
parent.newVersion
AND ~parent.saveInProgress
AND parent.file #
NIL =>
TEditDocumentPrivate.RecordUnsavedDocument[parent.file, tdd.text];
ENDCASE => TEditInput.FreeTree[tdd.text];
parent.name ← wDir;
parent.file ← NIL;
parent.newVersion ← parent.newFile ← FALSE;
tdd.text ← NIL; -- so InitViewerDoc won't free it
TEditDocumentPrivate.InitViewerDoc[parent,NIL];
TEditDocument.Unlock[tdd];
ClearPositionHistory[parent];
ViewerOps.AddProp[parent, $LoadHistory, prop]; -- restore file history
ViewerPainter[parent, all];
ForceInitialCaret[parent];
};
TEditSelection.UnlockSel[primary];
};
IF parent # NIL THEN LockTheWorks[inner, parent, "EmptyViewer"];
};
EmptyViewer:
PUBLIC
PROC [parent: Viewer] = {
DoEmptyViewer[parent];
};
DoNewViewer:
PUBLIC
PROC [column: Column, parent: Viewer ←
NIL]
RETURNS [new: Viewer] = {
TEditSelection.LockSel[primary, "DoNewViewer"];
{
ENABLE UNWIND => TEditSelection.UnlockSel[primary];
new ← MakeNewViewer[column, parent];
ForceInitialCaret[new];
};
TEditSelection.UnlockSel[primary];
};
NewViewer:
PUBLIC
PROC [parent: Viewer] = {
[] ← DoNewViewer[column: ColumnOf[parent], parent: parent];
};
DoCloseAndNewViewer:
PUBLIC
PROC [parent: Viewer]
RETURNS [new: Viewer] = {
TEditSelection.LockSel[primary, "DoCloseAndNewViewer"];
{
ENABLE
UNWIND => TEditSelection.UnlockSel[primary];
new ← ReplaceByNewViewer[parent];
ForceInitialCaret[new];
};
TEditSelection.UnlockSel[primary];
};
CloseAndNewViewer:
PUBLIC
PROC [parent: Viewer] = {
[] ← DoCloseAndNewViewer[parent];
};
DoLoadFile:
PUBLIC
PROC [parent: Viewer, fileName:
ROPE ←
NIL, close:
BOOL ←
FALSE,
fileNameProcViewer: Viewer ← NIL] RETURNS [viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, column: ColumnOf[parent], parent: parent,
op: IF close THEN $replace ELSE $load, lookup: $source,
fileNameProcViewer: fileNameProcViewer];
};
LoadFile:
PUBLIC
PROC [parent: Viewer] = {
[] ← DoLoadFile[parent: parent, fileName: NIL, close: FALSE];
};
DoOpenFile:
PUBLIC
PROC [fileName:
ROPE ←
NIL, column: Column, parent: Viewer ←
NIL, fileNameProcViewer: Viewer ←
NIL]
RETURNS [viewer: Viewer] = {
viewer ← DoGetFile[
txt: fileName, column: column, parent: parent, op: $open,
lookup: $source, fileNameProcViewer: fileNameProcViewer];
};
OpenFile:
PUBLIC
PROC [parent: Viewer] = {
[] ← DoOpenFile[fileName: NIL, column: ColumnOf[parent], parent: parent];
};
DoCloseAndOpenFile:
PUBLIC
PROC [parent: Viewer, fileName:
ROPE ←
NIL]
RETURNS [viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, column: ColumnOf[parent], parent: parent, op: $replace, lookup: $source];
};
CloseAndOpenFile:
PUBLIC
PROC [parent: Viewer, fileNameProcViewer: Viewer ←
NIL] = {
[] ← DoGetFile[txt:
NIL, column: ColumnOf[parent], parent: parent, op: $replace, lookup: $source,
fileNameProcViewer: fileNameProcViewer];
};
DoLoadImplFile:
PUBLIC
PROC [parent: Viewer, fileName:
ROPE ←
NIL, close:
BOOL ←
FALSE]
RETURNS [viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, column: ColumnOf[parent], parent: parent,
op: IF close THEN $replace ELSE $load, lookup: $impl];
};
LoadImplFile:
PUBLIC
PROC [parent: Viewer] = {
[] ← DoLoadImplFile[parent: parent, fileName: NIL];
};
DoOpenImplFile:
PUBLIC
PROC [fileName:
ROPE ←
NIL, column: Column, parent: Viewer ←
NIL]
RETURNS [viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, column: column, parent: parent, op: $open, lookup: $impl];
};
OpenImplFile:
PUBLIC
PROC [parent: Viewer] = {
[] ← DoOpenImplFile[fileName: NIL, column: ColumnOf[parent], parent: parent];
};
DoCloseAndOpenImplFile:
PUBLIC
PROC [parent: Viewer, fileName:
ROPE ←
NIL]
RETURNS [viewer: Viewer] = {
viewer ← DoGetFile[txt: fileName, column: ColumnOf[parent], parent: parent, op: $replace, lookup: $impl];
};
CloseAndOpenImplFile:
PUBLIC
PROC [parent: Viewer] = {
[] ← DoCloseAndOpenImplFile[parent, NIL];
};
PreLoadPrevious:
PUBLIC Menus.MenuProc = {
-- called when unguarding PrevFile
viewer: Viewer = NARROW[parent];
tdd: TEditDocument.TEditDocumentData;
prop: REF LoadHistory;
propName: ROPE;
tdd ← NARROW[viewer.data];
IF tdd = NIL THEN RETURN;
[] ← TEditDocument.SpinAndLock[tdd, "PreLoadPrevious"];
delay until after other op completes
prop ← NARROW[ViewerOps.FetchProp[viewer, $LoadHistory]];
TEditDocument.Unlock[tdd];
IF prop=
NIL
OR Rope.Equal[prop.name, viewer.file,
FALSE]
THEN {
Flash["No record of previous file loaded in this viewer"];
RETURN;
};
propName ← prop.name;
Report[propName," ~ Click LEFT to load, MIDDLE for new, RIGHT for close & new"];
};
DoLoadPreviousFile:
PROC [parent: Viewer, op: LoadOp] = {
WITH parent.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => {
prop: REF LoadHistory;
propName: ROPE;
propPlace: INT;
file: FS.OpenFile;
[] ← TEditDocument.SpinAndLock[tdd, "DoLoadPreviousFile"];
delay until after other op completes
prop ← NARROW[ViewerOps.FetchProp[parent, $LoadHistory]];
TEditDocument.Unlock[tdd];
IF prop =
NIL
OR Rope.Equal[prop.name, parent.file,
FALSE]
THEN {
Flash["No record of previous file loaded in this viewer"];
RETURN };
propName ← prop.name; propPlace ← prop.place;
file ← FS.Open[propName ! FS.Error => { Flash[error.explanation]; GOTO Fail }];
[] ← DoGet[parent: parent, column: ColumnOf[parent], file: file, specificVersion: (propName.Find["!"]>=0), op: op, place: propPlace, forceOpen: FALSE];
};
ENDCASE;
EXITS Fail => NULL;
};
LoadPreviousFile:
PUBLIC
PROC [parent: Viewer] = {
DoLoadPreviousFile[parent: parent, op: $load];
};
OpenPreviousFile:
PUBLIC
PROC [parent: Viewer] = {
DoLoadPreviousFile[parent: parent, op: $open];
};
CloseAndOpenPreviousFile:
PUBLIC
PROC [parent: Viewer] = {
DoLoadPreviousFile[parent: parent, op: $replace];
};
GetCreateName:
PROC [parent: Viewer, name:
ROPE]
RETURNS [
ROPE ←
NIL] = {
wDir: ROPE ~ WorkingDirectoryFromViewer[parent];
fullFName: ROPE; cp: FS.ComponentPositions;
[fullFName: fullFName, cp: cp] ← FS.ExpandName[name, wDir ! FS.Error => GOTO Fail];
RETURN [Rope.Flatten[base: fullFName, len: cp.ext.start+cp.ext.length]]; -- strip version
EXITS Fail => ViewerForkers.ForkCall[NIL, IllegalFileName];
};
IsNewFile:
PROC [name:
ROPE]
RETURNS [new:
BOOL ←
FALSE] = {
[] ← FS.FileInfo[name ! FS.Error => { new ← TRUE; CONTINUE }];
};
PreStore:
PUBLIC Menus.MenuProc = {
-- called when unguarding Store
sel: ROPE ← ViewerTools.GetSelectionContents[];
fileName: ROPE ← NIL; new: BOOL ← FALSE;
IF sel.Size[]=0 THEN {PleaseSelectFileName[]; RETURN};
fileName ← GetCreateName[NARROW[parent], sel];
IF fileName=NIL THEN RETURN;
new ← IsNewFile[fileName];
Report["Confirm Store to file: ", fileName, IF new THEN " [New File]" ELSE " [Old File]"];
};
DoStoreFile:
PUBLIC
PROC [parent: Viewer, fileName:
ROPE ←
NIL] = {
inner:
PROC [tdd: TEditDocument.TEditDocumentData] = {
linked: BOOL ← parent.link#NIL;
oldName: ROPE ~ parent.name;
oldFile: ROPE ~ parent.file;
IF fileName=NIL THEN fileName ← ViewerTools.GetSelectionContents[];
IF fileName.Size[]=0 THEN {PleaseSelectFileName[]; RETURN};
fileName ← GetCreateName[parent, fileName];
IF fileName=NIL THEN RETURN;
IF parent.file # NIL THEN SaveLoadHistory[parent];
IF linked 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
ViewerOps.SaveViewer[parent
! UNWIND => { parent.name ← oldName; parent.file ← oldFile }];
IF linked
THEN {
copy tdd.text data structure
loc: INT;
KillSelections[parent];
loc ← TextNode.LocNumber[tdd.lineTable.lines[0].pos];
tdd.text ← NIL; -- so InitViewerDoc won't free it
TEditDocumentPrivate.InitViewerDoc[parent, NIL];
RememberCurrentPosition[parent];
ForkViewerPainter[parent, caption];
};
};
LockTheWorks[inner, parent, "StoreFile"];
};
LockTheWorks:
PROC [inner:
PROC [tdd: TEditDocument.TEditDocumentData], viewer: Viewer, who:
ROPE] = {
innerWorks:
PROC = {
WITH viewer.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => {
[] ← TEditDocument.SpinAndLock[tdd, who];
inner[tdd ! UNWIND => TEditDocument.Unlock[tdd]];
TEditDocument.Unlock[tdd];
}
ENDCASE
};
ViewerGroupLocks.CallRootAndLinksUnderWriteLock[innerWorks, viewer];
};
StoreFile:
PUBLIC
PROC [parent: Viewer] = {
DoStoreFile[parent: parent, fileName: NIL];
};
GetViewerContents:
PROC [viewer: Viewer]
RETURNS [
ROPE] = {
TEditInputOps.WaitForInsertToFinish[];
RETURN [ViewerTools.GetContents[viewer]];
};
DoAnonymousLoadFile:
PROC [parent: Viewer, lookup: LookupType, fileNameProcViewer: Viewer ←
NIL] = {
IF parent.file#
NIL
THEN Flash["Viewer already contains a file."]
ELSE {
txt: ROPE ~ GetViewerContents[parent];
size: INT ~ txt.Size[];
IF size=0
THEN Flash["Enter a file name."]
ELSE {
v: Viewer ← DoGetFile[txt: txt, column: ColumnOf[parent], parent: parent, op: $load, lookup: lookup, fileNameProcViewer: fileNameProcViewer];
IF v#NIL AND v#parent THEN ViewerForkers.ForkCall[parent, FlameOut, parent];
};
};
};
AnonymousLoadFile:
PUBLIC
PROC [parent: Viewer, fileNameProcViewer: Viewer ←
NIL] = {
DoAnonymousLoadFile[parent: parent, lookup: $source, fileNameProcViewer: fileNameProcViewer];
};
AnonymousLoadImplFile:
PUBLIC
PROC [parent: Viewer] = {
DoAnonymousLoadFile[parent: parent, lookup: $impl];
};
NodeProps.Register[$Viewer,
NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
NodeProps.Register[$LockedViewer,
NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
NodeProps.Register[$FileCreateDate,
NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];
NodeProps.Register[$FileExtension,
NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy];