TEditDocumentsImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Michael Plass, July 19, 1985 2:18:22 pm PDT
Russ Atkinson (RRA) August 19, 1985 4:07:47 pm PDT
Doug Wyatt, September 2, 1986 2:55:07 pm PDT
DIRECTORY
Buttons USING [Create],
Convert USING [RopeFromInt],
EditSpanSupport USING [Apply],
FS USING [Create, Error, GetName, Lock, nullOpenFile, Open, OpenFile],
Menus USING [AppendMenuEntry, ChangeNumberOfLines, ClickProc, CreateEntry, CreateMenu, GetLine, GetNumberOfLines, InsertMenuEntry, Menu, MenuEntry, MenuLine, MenuProc, SetLine],
MessageWindow USING [Append, Blink, Clear],
NodeProps USING [GetProp, MapProps, PutProp],
Process USING [Detach, GetCurrent],
PutGet USING [FromFileC, FromFileError, FromRope, ToFileC, ToRope, WriteRopePlain],
Rope USING [Cat, Concat, Equal, Flatten, FromRefText, Length, ROPE, SkipTo, Substr],
TEditCompile USING [minAvgLineLeading],
TEditDocument USING [ForgetViewer, LineTable, LineTableRec, RecordViewerForRoot, Selection, TEditDocumentData, TEditDocumentDataRec],
TEditDocumentPrivate USING [AllLevels, Clear, FewerLevels, Find, FindDef, FindWord, FirstLevelOnly, Get, GetImpl, JumpToPrevious, KillSelections, MoreLevels, NewButton, Normalize, OpenButton, Position, PreReset, PreStore, PreviousFile, RecordUnsavedDocument, Reselect, Reset, Save, Split, Store, Time],
TEditInput USING [CommandProc, FreeTree, InterpretAtom, Register],
TEditInputOps USING [CallWithLocks],
TEditLocks USING [Lock, LockDocAndTdd, Unlock, UnlockDocAndTdd],
TEditPrivate USING [PaintTEditDocument, TEditNotifyProc],
TEditProfile USING [DoList],
TEditRefresh USING [ScrollToEndOfSel],
TEditScrolling USING [ScrollTEditDocument],
TEditSelection USING [Alloc, Free, InputModify, MakeSelection],
TEditTouchup USING [fullUpdate],
TextEdit USING [DocFromNode, FromRope],
TextLooks USING [noLooks],
TextNode USING [FirstChild, LastLocWithin, Location, LocNumber, LocOffset, LocRelative, LocWithin, Node, NodeProps],
TiogaMenuOps USING [], -- exports tiogaMenu
TIPUser USING [InstantiateNewTIPTable, InvalidTable, TIPTable],
UserProfile USING [Number],
ViewerBLT USING [ChangeNumberOfLines],
ViewerClasses USING [AdjustProc, DestroyProc, GetProc, InitProc, Lock, SaveAborted, SaveProc, SetProc, Viewer, ViewerClass, ViewerClassRec],
ViewerForkers USING [ForkPaint],
ViewerLocks USING [CallUnderViewerTreeLock],
ViewerOps USING [AddProp, EnumerateChildren, EnumerateViewers, FetchProp, FetchViewerClass, PaintHint, PaintViewer, RegisterViewerClass, SetMenu],
ViewerTools USING [EnableUserEdits, SelPos, SelPosRec, TiogaContents, TiogaContentsRec];
TEditDocumentsImpl:
CEDAR
MONITOR
IMPORTS Buttons, Convert, EditSpanSupport, FS, Menus, MessageWindow, NodeProps, Process, PutGet, Rope, TEditDocument, TEditDocumentPrivate, TEditInput, TEditInputOps, TEditLocks, TEditPrivate, TEditProfile, TEditRefresh, TEditScrolling, TEditSelection, TEditTouchup, TextEdit, TextNode, TIPUser, UserProfile, ViewerBLT, ViewerClasses, ViewerForkers, ViewerLocks, ViewerOps, ViewerTools
EXPORTS TEditDocument, TEditDocumentPrivate, TEditPrivate, TiogaMenuOps
SHARES Menus, ViewerClasses
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
fatalTiogaError: PUBLIC ERROR = CODE; -- for RETURN WITH ERROR monitor clearing punts
InitTEditDocument:
PUBLIC ViewerClasses.InitProc = {
data: REF ANY ← self.data;
self.data ← NIL;
InitViewerDoc[self, data]
};
InitViewerDoc:
PUBLIC
PROC [self: ViewerClasses.Viewer, data:
REF
ANY] = {
InitViewerDocInternal[self: self, file: FS.nullOpenFile, data: data];
};
InitViewerDocInternal:
PUBLIC
PROC [self: ViewerClasses.Viewer, file:
FS.OpenFile, data:
REF
ANY] = {
tdd: TEditDocument.TEditDocumentData ← NARROW[self.data];
InitTddText:
PROC = {
IF tdd.text # NIL THEN TEditInput.FreeTree[tdd.text];
tdd.text ← NIL;
IF file=
FS.nullOpenFile
THEN {
IF self.file#NIL THEN file ← FS.Open[self.file ! FS.Error => CONTINUE];
};
IF file#
FS.nullOpenFile
THEN {
self.file ← FS.GetName[file].fullFName;
tdd.text ← PutGet.FromFileC[file !
FS.Error => { self.newFile ← TRUE; CONTINUE };
PutGet.FromFileError => {
MessageWindow.Append["TEditDocumentsImpl: Error in Tioga formatting of ", TRUE];
MessageWindow.Append[self.file, FALSE];
MessageWindow.Blink[];
CONTINUE;
};
];
};
IF tdd.text=
NIL
THEN {
tdd.text ← TextEdit.DocFromNode[TextEdit.FromRope[""]];
};
};
InitLineTable:
PROC = {
IF NodeProps.GetProp[tdd.text, $OpenFirstLevelOnly]#NIL THEN tdd.clipLevel ← 1;
tdd.lineTable.lastLine ← 0;
tdd.lineTable[0].pos ← [TextNode.FirstChild[tdd.text], 0];
};
IF self.link#
NIL
THEN {
make sure another link didn't already init
and if so, copy that data
otherInit: ViewerClasses.Viewer ← NIL;
IF tdd=NIL THEN self.data ← tdd ← NARROW[data]; -- someday I should really find out how to fix this in a less horrible manner
[] ← SpinAndLock[tdd, "InitViewerDoc"];
FOR v: ViewerClasses.Viewer ← self.link, v.link
UNTIL v=self
DO
IF ~v.newVersion THEN {otherInit ← v; EXIT};
ENDLOOP;
IF otherInit=
NIL
THEN InitTddText[]
ELSE {
somebody else already did the init
otherTdd: TEditDocument.TEditDocumentData ← NARROW[otherInit.data];
tdd.text ← otherTdd.text;
};
InitLineTable[];
}
ELSE
IF data=
NIL
THEN {
IF tdd=NIL THEN tdd ← AllocateDataForNewViewer[self];
[] ← SpinAndLock[tdd, "InitViewerDoc"];
InitTddText[];
InitLineTable[];
}
ELSE
WITH data
SELECT
FROM
d: TEditDocument.TEditDocumentData => {
self.data ← tdd ← d;
[] ← SpinAndLock[tdd, "InitViewerDoc"];
InitTddText[];
InitLineTable[];
};
root: TextNode.Node => {
IF tdd=NIL THEN tdd ← AllocateDataForNewViewer[self];
[] ← SpinAndLock[tdd, "InitViewerDoc"];
tdd.text ← root;
InitLineTable[];
};
r: Rope.
ROPE => {
IF tdd=NIL THEN tdd ← AllocateDataForNewViewer[self];
[] ← SpinAndLock[tdd, "InitViewerDoc"];
tdd.text ← TextEdit.DocFromNode[TextEdit.FromRope[r]];
InitLineTable[];
};
t:
REF
TEXT => {
IF tdd=NIL THEN tdd ← AllocateDataForNewViewer[self];
[] ← SpinAndLock[tdd, "InitViewerDoc"];
tdd.text ← TextEdit.DocFromNode[TextEdit.FromRope[Rope.FromRefText[t]]];
InitLineTable[];
};
ENDCASE => {
bad data
IF tdd=NIL THEN tdd ← AllocateDataForNewViewer[self];
[] ← SpinAndLock[tdd, "InitViewerDoc"];
tdd.text ← PutGet.FromRope["*ERROR*"];
InitLineTable[];
};
TEditDocument.RecordViewerForRoot[self,tdd.text];
old heuristic: IF HasBrackets[self.file] THEN ViewerTools.InhibitUserEdits[self] ELSE
ViewerTools.EnableUserEdits[self];
Unlock[tdd];
};
AllocateDataForNewViewer:
PROC [self: ViewerClasses.Viewer]
RETURNS [tdd: TEditDocument.TEditDocumentData] = {
tdd ← NEW[TEditDocument.TEditDocumentDataRec];
for now, just hack in a conservative size line table
tdd.lineTable ←
NEW[TEditDocument.LineTableRec[
MAX[2, (self.ch/TEditCompile.minAvgLineLeading)+1]] ←
[lastLine: 0, lastY: 0, lines: NULL]];
IF self.column#static AND self.parent=NIL THEN ViewerOps.SetMenu[self, tiogaMenu, FALSE];
self.data ← tdd;
};
number: INT ← 0;
SaveTEditDocument:
PUBLIC ViewerClasses.SaveProc = {
tdd: TEditDocument.TEditDocumentData ← NARROW[self.data];
root: TextNode.Node;
name: ROPE ← self.file;
errorMessage: ROPE ← NIL;
IF name.Length = 0
THEN {
IF ~force THEN ERROR ViewerClasses.SaveAborted["no file name!"];
name ← Rope.Flatten[Rope.Cat[
"SaveAllEdits-",
Convert.RopeFromInt[number ← number + 1],
".tioga"
]];
};
IF tdd.readOnly
THEN
ERROR ViewerClasses.SaveAborted["read only document!"];
IF ~force
THEN {
lock so no edits while saving
MessageWindow.Clear[];
TEditDocumentPrivate.KillSelections[self];
We want to kill off selections so we don't lock up the world while saving.
[] ← SpinAndLock[tdd, "SaveTEditDocument"]; -- may have other operation in progress
root ← tdd.text;
Unlock[tdd]; -- don't need to keep this locked during rest of Save
[] ← TEditLocks.Lock[root, "SaveTEditDocument", read];
}
ELSE root ← tdd.text;
Save the file.
{
ENABLE FS.Error => { errorMessage ← error.explanation; CONTINUE };
defaultKeep: INT ← UserProfile.Number["Tioga.defaultKeep", 2];
file: FS.OpenFile ~ FS.Create[name: RemoveVersionNumber[name],
setPages: FALSE, setKeep: FALSE, keep: defaultKeep];
newFile: ROPE ~ FS.GetName[file].fullFName;
newName: ROPE ~ RemoveVersionNumber[newFile];
[] ← PutGet.ToFileC[file, root];
FOR v: ViewerClasses.Viewer ← self, v.link
DO
v.name ← newName;
v.file ← newFile;
SELECT v.link
FROM
NIL, self => EXIT;
ENDCASE;
ENDLOOP;
};
Unlock the document if it was locked to begin with.
IF ~force THEN TEditLocks.Unlock[root];
IF errorMessage#NIL THEN ERROR ViewerClasses.SaveAborted[errorMessage];
};
RemoveVersionNumber:
PROC[name:
ROPE]
RETURNS[
ROPE] = {
RETURN[name.Flatten[start: 0, len: name.SkipTo[0, "!"]]];
};
FileIsMoreRecent:
PUBLIC
PROC [root: TextNode.Node, file: Rope.
ROPE]
RETURNS [
BOOL] = {
Returns true if the file on the disk has a more recent create date than the root file create date. RRA sez that this should never happen in the wonderful world of Cedar 5.0.
RETURN [FALSE];
};
nullNode: TextNode.Node = TextEdit.FromRope[NIL];
SetTEditDocument: ViewerClasses.SetProc = {
[self: ViewerClasses.Viewer, data: REF ANY, finalise: BOOL ← TRUE, op: ATOM ← NIL]
IF
NOT self.destroyed
THEN
WITH self.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => {
SELECT op
FROM
NIL, $TiogaContents, $TiogaDocument, $KeepRootStyleProps => {
hint: ViewerOps.PaintHint ← client;
rope: Rope.ROPE;
IF self.link#NIL THEN ERROR; -- not yet implemented
TEditDocumentPrivate.KillSelections[self];
[] ← SpinAndLock[tdd, "SetTEditDocument"];
SELECT op
FROM
NIL, $KeepRootStyleProps => {
root: TextNode.Node = tdd.text;
node: TextNode.Node = root;
formatName: ATOM = root.formatName;
child: TextNode.Node = IF node=NIL THEN NIL ELSE node.child;
rope ← IF data=NIL THEN "" ELSE NARROW[data];
IF child#
NIL
AND child.last
AND child.child=
NIL
THEN {
reuse rather than reallocate
RemoveProps:
PROC [name:
ATOM, value:
REF]
RETURNS [
BOOL] = {
IF name#$DocumentLock AND name#$Viewer
AND ~(op=$KeepRootStyleProps AND (name=$Prefix OR name=$Postfix)) THEN NodeProps.PutProp[root, name, NIL];
RETURN [FALSE]
};
rootProps: TextNode.NodeProps;
Should really have a write lock on the document for this. change for Tioga2.
child^ ← nullNode^;
rootProps ← root.props; root^ ← nullNode^; root.props ← rootProps;
root.child ← child; child.next ← root;
root.last ← child.last ← TRUE; child.rope ← rope;
IF op = $KeepRootStyleProps THEN root.formatName ← formatName;
[] ← NodeProps.MapProps[root, RemoveProps, FALSE, FALSE]
}
ELSE {
prefix, postfix: REF;
IF op=$KeepRootStyleProps
THEN {
prefix ← NodeProps.GetProp[root, $Prefix];
postfix ← NodeProps.GetProp[root, $Postfix]
};
TEditInput.FreeTree[root];
tdd.text ← TextEdit.DocFromNode[TextEdit.FromRope[rope]];
IF op=$KeepRootStyleProps
THEN {
IF prefix#NIL THEN NodeProps.PutProp[tdd.text, $Prefix, prefix];
IF postfix#NIL THEN NodeProps.PutProp[tdd.text, $Postfix, postfix];
tdd.text.formatName ← formatName
};
};
};
$TiogaContents => {
info: ViewerTools.TiogaContents ← NARROW[data];
rope ← Rope.Concat[info.contents, info.formatting];
TEditInput.FreeTree[tdd.text];
tdd.text ← PutGet.FromRope[rope];
};
$TiogaDocument => {
root: TextNode.Node ← NARROW[data];
TEditInput.FreeTree[tdd.text];
tdd.text ← root
};
ENDCASE;
tdd.lineTable.lastLine ← 0;
tdd.lineTable[0].pos ← [TextNode.FirstChild[tdd.text], 0];
TEditDocument.RecordViewerForRoot[self,tdd.text];
self.newVersion ← FALSE; -- clear edited bit
Unlock[tdd];
IF finalise
THEN {
Need a repaint
ViewerForkers.ForkPaint[self, hint, TRUE, TEditTouchup.fullUpdate, TRUE];
};
};
$SelPos =>
IF ~self.iconic
AND ~ self.destroyed
THEN {
no iconic selections please
tSel: TEditDocument.Selection ← TEditSelection.Alloc[];
sel: ViewerTools.SelPos ← NARROW[data];
start, length: INT;
[] ← TEditLocks.LockDocAndTdd[tdd, "SetTEditDocument", read];
IF sel=
NIL
THEN {
IF tdd.tsInfo#
NIL
THEN {
for typescripts, NIL => caret at end
start ← TextNode.LocNumber[
includes 1 for root, so must subtract 1
at: TextNode.LastLocWithin[tdd.text], skipCommentNodes: TRUE];
length ← 0
}
ELSE {
for other text viewers, NIL => entire contents
start ← 0;
length ← TextNode.LocNumber[
includes 1 for root, so must subtract 1
at: TextNode.LastLocWithin[tdd.text], skipCommentNodes: TRUE]-1
};
}
ELSE {
tiogaFile: BOOL ← (NodeProps.GetProp[tdd.text, $FromTiogaFile] = $Yes);
start ← sel.start;
IF ~tiogaFile THEN start ← start+1; -- hack to compensate for leading CR
length ← sel.length
};
tSel.start.pos ← TextNode.LocWithin[n: tdd.text, count: start, skipCommentNodes: TRUE];
tSel.end.pos ← TextNode.LocRelative[
location: tSel.start.pos, count: MAX[length-1, 0], skipCommentNodes: TRUE];
TEditLocks.UnlockDocAndTdd[tdd];
tSel.granularity ← IF length=0 THEN point ELSE char;
tSel.viewer ← self;
tSel.data ← tdd;
tSel.insertion ← IF length=0 THEN before ELSE after;
tSel.pendingDelete ← TRUE;
tSel.looks ← TextLooks.noLooks;
TEditSelection.MakeSelection[new: tSel];
TEditSelection.Free[tSel];
TEditRefresh.ScrollToEndOfSel[self, FALSE];
};
$ReadOnly => {
[] ← TEditLocks.LockDocAndTdd[tdd, "SetTEditDocument", read];
self.tipTable ← readonlyTIP;
tdd.readOnly ← TRUE;
TEditLocks.UnlockDocAndTdd[tdd];
};
$ReadWrite => {
[] ← TEditLocks.LockDocAndTdd[tdd, "SetTEditDocument", read];
self.tipTable ← tiogaTIP;
tdd.readOnly ← FALSE;
TEditLocks.UnlockDocAndTdd[tdd];
};
ENDCASE;
};
ENDCASE;
};
GetTEditDocument: ViewerClasses.GetProc = {
[self: ViewerClasses.Viewer, op: ATOM ← NIL] RETURNS [data: REF ANY]
data ← NIL;
WITH self.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData =>
SELECT op
FROM
NIL => {
node: TextNode.Node = tdd.text.child;
IF node#
NIL
AND node.last
AND node.child=
NIL
THEN data ← node.rope
simple case of only one node
ELSE data ← PutGet.WriteRopePlain[tdd.text];
};
$TiogaContents => {
contents, formatting: Rope.ROPE;
dataLen, count: INT;
[dataLen, count, contents] ← PutGet.ToRope[tdd.text];
formatting ← Rope.Substr[base: contents, start: dataLen, len: count-dataLen];
contents ← Rope.Substr[base: contents, start: 0, len: dataLen];
data ← NEW[ViewerTools.TiogaContentsRec ← [contents, formatting]];
};
$SelChars => {
rope: Rope.ROPE; -- hack to fix compiler problem
DoSelChars:
PROC [root: TextNode.Node, tSel: TEditDocument.Selection] = {
SelConcat:
PROC [node: TextNode.Node, start, len:
INT]
RETURNS [stop:
BOOL] = {
rope ←
IF rope =
NIL
THEN Rope.Substr[node.rope, start, len]
ELSE Rope.Cat[rope, "\n", Rope.Substr[node.rope, start, len]];
RETURN [FALSE];
};
IF tSel.viewer # NIL AND tSel.granularity # point THEN
EditSpanSupport.Apply[[tSel.start.pos, tSel.end.pos], SelConcat];
IF rope=NIL THEN rope ← "";
};
TEditInputOps.CallWithLocks[DoSelChars, read];
data ← rope;
};
$SelPos => {
sel: ViewerTools.SelPos ← NEW[ViewerTools.SelPosRec];
DoSelPos:
PROC [root: TextNode.Node, tSel: TEditDocument.Selection] = {
sel.start ← TextNode.LocNumber[at: tSel.start.pos, skipCommentNodes: TRUE];
sel.length ← TextNode.LocOffset[
loc1: tSel.start.pos, loc2: tSel.end.pos, skipCommentNodes: TRUE]
};
TEditInputOps.CallWithLocks[DoSelPos, read];
data ← sel;
}
ENDCASE;
ENDCASE;
};
DestroyTEditDocument: ViewerClasses.DestroyProc = {
TEditDocument.ForgetViewer[self];
this also fixes up the Root => Viewer mapping if necessary
IF self.link # NIL THEN RETURN; -- linked, so not finished with document yet
TRUSTED {Process.Detach[FORK CleanupAfterDestroy[self, self.file, NARROW[self.data]]] }
};
CleanupAfterDestroy:
PROC [self: ViewerClasses.Viewer, file: Rope.
ROPE, tdd: TEditDocument.TEditDocumentData] = {
Need to pass self.file as variable because it will be set to NIL
root: TextNode.Node;
IF tdd = NIL THEN RETURN; -- anybody remember why this is here (SM)?
[] ← SpinAndLock[tdd, "DestroyTEditDocument"];
root ← tdd.text;
IF self.newVersion AND ~self.saveInProgress AND file # NIL THEN
TEditDocumentPrivate.RecordUnsavedDocument[file, root]
ELSE TEditInput.FreeTree[root];
Unlock[tdd]
};
ChangeMenu:
PROC [viewer: ViewerClasses.Viewer, subMenu: Menus.MenuEntry] = {
menu: Menus.Menu ← viewer.menu;
found: BOOL ← FALSE;
numLines: Menus.MenuLine = Menus.GetNumberOfLines[menu];
newLines: Menus.MenuLine ← numLines;
FOR i: Menus.MenuLine
IN [1..numLines)
DO
see if already showing the submenu
IF Rope.Equal[Menus.GetLine[menu,i].name, subMenu.name]
THEN {
-- yes, so remove it
FOR j: Menus.MenuLine
IN (i..numLines)
DO
Menus.SetLine[menu, j-1, Menus.GetLine[menu, j]];
ENDLOOP;
Menus.ChangeNumberOfLines[menu, newLast-1];
newLines ← newLines-1;
found ← TRUE; EXIT
};
ENDLOOP;
IF ~found
THEN {
add it. do insertion sort to get it in the right place
GoesBefore:
PROC [m1, m2: Menus.MenuEntry]
RETURNS [
BOOL] = {
Priority:
PROC [m: Menus.MenuEntry]
RETURNS [
INTEGER] = {
higher priority means goes above in series of submenus
looks at rope for first item to identify the subMenu.
RETURN [
SELECT
TRUE
FROM
Rope.Equal[m.name, "Find"] => 1,
Rope.Equal[m.name, "FirstLevelOnly"] => 0,
ENDCASE => -1 -- unknown menu goes at bottom -- ]
};
RETURN [Priority[m1] > Priority[m2]]
};
newLast: Menus.MenuLine = MIN[numLines, LAST[Menus.MenuLine]];
newLines ← newLines+1;
FOR i: Menus.MenuLine
IN [1..numLines)
DO
IF GoesBefore[subMenu, Menus.GetLine[menu, i]]
THEN {
put it here
FOR j: Menus.MenuLine
DECREASING
IN (i..newLast]
DO
Menus.SetLine[menu, j, Menus.GetLine[menu, j-1]]; ENDLOOP;
Menus.SetLine[menu, i, subMenu];
found ← TRUE; EXIT
};
ENDLOOP;
IF ~found THEN Menus.SetLine[menu, newLast, subMenu];
};
ViewerBLT.ChangeNumberOfLines[viewer, newLines];
};
LevelMenu: PUBLIC Menus.MenuProc = { ChangeMenu[NARROW[parent], levelMenu] };
FindMenu: PUBLIC Menus.MenuProc = { ChangeMenu[NARROW[parent], findMenu] };
unlocked: CONDITION;
SpinAndLock:
PUBLIC
ENTRY
PROC [tdd: TEditDocument.TEditDocumentData, who: Rope.
ROPE, interrupt, defer:
BOOL ←
FALSE]
RETURNS [ok:
BOOL] =
TRUSTED {
ENABLE UNWIND => NULL;
myProcess: PROCESS = LOOPHOLE[Process.GetCurrent[]];
IF myProcess#tdd.lockProcess
THEN {
IF interrupt
THEN {
IF defer AND tdd.interrupt > 0 THEN RETURN [FALSE];
tdd.interrupt ← tdd.interrupt+1
};
WHILE tdd.lock>0 DO WAIT unlocked; ENDLOOP;
tdd.lockProcess ← myProcess;
tdd.who ← who; -- save the first guy who got the lock
IF interrupt THEN tdd.interrupt ← tdd.interrupt-1;
};
tdd.lock ← tdd.lock+1;
RETURN [TRUE];
};
Unlock:
PUBLIC
ENTRY
PROC [tdd: TEditDocument.TEditDocumentData] =
TRUSTED {
ENABLE UNWIND => NULL;
IF tdd.lockProcess # Process.GetCurrent[] THEN ERROR;
IF (tdd.lock ← tdd.lock-1) = 0 THEN BROADCAST unlocked
};
ReloadTipTable:
PUBLIC
PROC = {
newTIP: TIPUser.TIPTable ← ReloadTable[tiogaTIP, "TiogaTIP", "[]<>Tioga.tip"];
IF newTIP # NIL THEN tiogaClass.tipTable ← tiogaTIP ← newTIP;
};
ReloadReadonlyTipTable:
PUBLIC
PROC = {
newTIP: TIPUser.TIPTable ←
ReloadTable[readonlyTIP, "ReadonlyTiogaTIP", "[]<>ReadonlyTioga.tip"];
IF newTIP # NIL THEN readonlyTIP ← newTIP;
};
readonlyTIP: TIPUser.TIPTable;
tiogaTIP: PUBLIC TIPUser.TIPTable;
ChangeTipTables:
PROC [newTIP, oldTIP: TIPUser.TIPTable] = {
WithViewerTreeLocked:
PROC ~ {
changeTip:
PROC [v: ViewerClasses.Viewer]
RETURNS [
BOOL ←
TRUE] = {
WITH v.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => {
IF v.tipTable = oldTIP THEN v.tipTable ← newTIP;
};
ENDCASE => NULL;
ViewerOps.EnumerateChildren[v, changeTip];
};
typescriptClass: ViewerClasses.ViewerClass ~ ViewerOps.FetchViewerClass[$Typescript];
ViewerOps.EnumerateViewers[changeTip];
Would really like to enumerate viewer classes here . . .
IF tiogaClass.tipTable = oldTIP THEN tiogaClass.tipTable ← newTIP;
IF tiogaTIP = oldTIP THEN tiogaTIP ← newTIP;
IF readonlyTIP = oldTIP THEN readonlyTIP ← newTIP;
IF typescriptClass#NIL AND typescriptClass.tipTable = oldTIP THEN typescriptClass.tipTable ← newTIP;
};
ViewerLocks.CallUnderViewerTreeLock[WithViewerTreeLocked];
};
ReloadTable:
PUBLIC
PROC [oldTIP: TIPUser.TIPTable, profileKey, default: Rope.
ROPE]
RETURNS [newTIP: TIPUser.TIPTable] = {
ok: BOOL ← TRUE;
AddTable:
PROC [r: Rope.
ROPE] = {
bad: BOOL ← FALSE;
t: TIPUser.TIPTable;
msg: Rope.ROPE;
IF ~ok THEN RETURN;
IF Rope.Equal[r,"default",
FALSE]
THEN {
TEditProfile.DoList["", AddTable, default];
RETURN
};
t ← TIPUser.InstantiateNewTIPTable[r
!
FS.Error => {
bad ← TRUE;
msg ← Rope.Concat["Cannot read TIP table file: ", r];
CONTINUE
};
TIPUser.InvalidTable => {
bad ← TRUE;
msg ← Rope.Concat["Error(s) saved on TIP.Errors for: ", r];
CONTINUE
}];
IF bad THEN { ok ← FALSE; MessageWindow.Append[msg, TRUE]; RETURN };
IF newTIP=NIL THEN { newTIP ← t; RETURN };
FOR x: TIPUser.TIPTable ← newTIP, x.link
UNTIL x=
NIL
DO
FOR y: TIPUser.TIPTable ← t, y.link
UNTIL y=
NIL
DO
IF x # y THEN LOOP;
ok ← FALSE;
MessageWindow.Append[Rope.Concat["Loop in TIP table layers caused by ", r], TRUE];
RETURN;
ENDLOOP;
ENDLOOP;
FOR x: TIPUser.TIPTable ← newTIP, x.link
UNTIL x.link=
NIL
DO
REPEAT
FINISHED => {
newTIP.mouseTicks ← MIN[t.mouseTicks, newTIP.mouseTicks];
x.opaque ← FALSE;
x.link ← t;
};
ENDLOOP;
};
TEditProfile.DoList[profileKey, AddTable, default];
IF ~ok
AND oldTIP=
NIL
THEN {
try the default by itself
ok ← TRUE; TEditProfile.DoList["", AddTable, default]
};
IF oldTIP #
NIL
AND newTIP #
NIL
THEN
TRUSTED {
Process.Detach[FORK ChangeTipTables[newTIP, oldTIP]];
};
};
alwaysInvalid: BOOL ← FALSE;
LocalAdjust: ViewerClasses.AdjustProc = {
[self: ViewerClasses.Viewer] RETURNS [adjusted: BOOL ← FALSE]
IF self = NIL OR self.iconic OR self.destroyed OR self.paintingWedged THEN RETURN [FALSE];
WITH self.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => {
ch: INTEGER ← self.ch;
lines: TEditDocument.LineTable ← tdd.lineTable;
IF lines #
NIL
THEN
FOR i:
INT
IN [0..lines.lastLine]
DO
IF lines[i].valid
THEN
IF alwaysInvalid
OR lines[i].yOffset >= ch
THEN
lines[i].valid ← FALSE;
ENDLOOP;
};
ENDCASE;
RETURN [TRUE];
};
tiogaClass: ViewerClasses.ViewerClass ←
NEW[ViewerClasses.ViewerClassRec ← [
paint: TEditPrivate.PaintTEditDocument,
bltV: top, -- use blt to copy screen contents to top of viewer when height changes
bltH: none, -- but not if width changes
notify: TEditPrivate.TEditNotifyProc,
modify: TEditSelection.InputModify,
adjust: LocalAdjust,
init: InitTEditDocument,
set: SetTEditDocument,
get: GetTEditDocument,
save: SaveTEditDocument,
destroy: DestroyTEditDocument,
scroll: TEditScrolling.ScrollTEditDocument
tipTable set below
]];
tiogaMenu: PUBLIC Menus.Menu ← Menus.CreateMenu[];
findMenu, levelMenu, lineMenu: PUBLIC Menus.MenuEntry;
preSave: REF Menus.ClickProc = NEW[Menus.ClickProc ← PreSave];
preReset: REF Menus.ClickProc = NEW[Menus.ClickProc ← TEditDocumentPrivate.PreReset];
preClear: REF Menus.ClickProc = NEW[Menus.ClickProc ← PreClear];
preStore: REF Menus.ClickProc = NEW[Menus.ClickProc ← TEditDocumentPrivate.PreStore];
preLoadPrevious: REF Menus.ClickProc = NEW[Menus.ClickProc ← PreLoadPrevious];
preGet: REF Menus.ClickProc = NEW[Menus.ClickProc ← PreGet];
preGetImpl: REF Menus.ClickProc = NEW[Menus.ClickProc ← PreGetImpl];
first row
Stuff for StyleKind button (should live in TEditDocuments3Impl, but I didn't want to change or add interfaces. — MFP)
StyleKindClick:
PUBLIC Menus.ClickProc = { TEditInput.InterpretAtom[
NARROW[parent],
SELECT mouseButton
FROM
red => $StyleKindScreen,
yellow, blue => $StyleKindPrint,
ENDCASE => ERROR];
};
StyleKindScreenOp: TEditInput.CommandProc = {
old: REF ~ ViewerOps.FetchProp[viewer, $StyleKind];
IF old#
NIL
THEN {
ViewerOps.AddProp[viewer, $StyleKind, NIL];
ViewerOps.PaintViewer[viewer: viewer, hint: client, clearClient: TRUE];
};
};
StyleKindPrintOp: TEditInput.CommandProc = {
old: REF ~ ViewerOps.FetchProp[viewer, $StyleKind];
IF old#$Print
THEN {
ViewerOps.AddProp[viewer, $StyleKind, $Print];
ViewerOps.PaintViewer[viewer: viewer, hint: client, clearClient: TRUE];
};
};
TEditInput.Register[$StyleKindPrint, StyleKindPrintOp];
TEditInput.Register[$StyleKindScreen, StyleKindScreenOp];
Menu creation
Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "Clear", proc: TEditDocumentPrivate.Clear
--, guarded: TRUE, documentation: preClear--]];
Menus.AppendMenuEntry[menu: tiogaMenu,
entry: Menus.CreateEntry[name: "Reset", proc: TEditDocumentPrivate.Reset, guarded: TRUE, documentation: preReset]];
Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "Get", proc: TEditDocumentPrivate.Get
--, guarded: TRUE, documentation: preGet--]];
Menus.AppendMenuEntry[menu: tiogaMenu,
entry: Menus.CreateEntry[name: "GetImpl", proc: TEditDocumentPrivate.GetImpl
--,guarded: TRUE, documentation: preGetImpl--]];
Menus.AppendMenuEntry[menu: tiogaMenu,
entry: Menus.CreateEntry[name: "PrevFile", proc: TEditDocumentPrivate.PreviousFile
--,guarded: TRUE, documentation: preLoadPrevious--]];
Menus.AppendMenuEntry[menu: tiogaMenu,
entry: Menus.CreateEntry[name: "Store", proc: TEditDocumentPrivate.Store, guarded: TRUE, documentation: preStore]];
Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "Save", proc: TEditDocumentPrivate.Save
--, guarded: TRUE, documentation: preSave--]];
Menus.AppendMenuEntry[tiogaMenu, Menus.CreateEntry["Time", TEditDocumentPrivate.Time]];
Menus.AppendMenuEntry[tiogaMenu, Menus.CreateEntry["Split", TEditDocumentPrivate.Split]];
Menus.AppendMenuEntry[tiogaMenu, Menus.CreateEntry["Places", FindMenu]];
Menus.AppendMenuEntry[tiogaMenu, Menus.CreateEntry["Levels", LevelMenu]];
build the places menu
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["StyleKind", StyleKindClick], 1];
StyleKind in the places menu is flakey, but there's room
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Reselect", TEditDocumentPrivate.Reselect], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["PrevPlace", TEditDocumentPrivate.JumpToPrevious], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Normalize", TEditDocumentPrivate.Normalize], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Position", TEditDocumentPrivate.Position], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Def", TEditDocumentPrivate.FindDef], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Word", TEditDocumentPrivate.FindWord], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Find", TEditDocumentPrivate.Find], 1];
findMenu ← Menus.GetLine[tiogaMenu, 1];
Menus.SetLine[tiogaMenu, 1, NIL];
build the levels menu
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["AllLevels", TEditDocumentPrivate.AllLevels], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["FewerLevels", TEditDocumentPrivate.FewerLevels], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["MoreLevels", TEditDocumentPrivate.MoreLevels], 1];
Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["FirstLevelOnly", TEditDocumentPrivate.FirstLevelOnly], 1];
levelMenu ← Menus.GetLine[tiogaMenu, 1];
Menus.SetLine[tiogaMenu, 1, NIL];
Menus.ChangeNumberOfLines[tiogaMenu, 1]; -- only show the top level to start
ReloadTipTable[]; ReloadReadonlyTipTable[];
ViewerOps.RegisterViewerClass [$Text, tiogaClass];
[] ← Buttons.Create[info: [name: "New"], proc: TEditDocumentPrivate.NewButton, fork: FALSE];
don't fork since will change the selection; want to capture following typein
[] ← Buttons.Create[info: [name: "Open"], proc: TEditDocumentPrivate.OpenButton];
END.