TEditDocumentsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, July 19, 1985 2:18:22 pm PDT
Doug Wyatt, April 10, 1985 11:03:35 am PST
Russ Atkinson (RRA) August 19, 1985 4:07:47 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],
TEditImpl USING [PaintTEditDocument, TEditNotifyProc],
TEditInput USING [CommandProc, FreeTree, InterpretAtom, Register],
TEditInputOps USING [CallWithLocks],
TEditLocks USING [Lock, LockDocAndTdd, Unlock, UnlockDocAndTdd],
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, NodeProps, Offset, Ref, RefTextNode],
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, TextEdit, TextNode, TEditImpl, TEditInput, TEditInputOps, TEditLocks, TEditProfile, TEditRefresh, TEditTouchup, TEditScrolling, TEditSelection, TIPUser, UserProfile, ViewerBLT, ViewerClasses, ViewerForkers, ViewerOps, ViewerLocks, ViewerTools
EXPORTS TEditImpl, TEditDocument, TEditDocumentPrivate, 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.RefTextNode => {
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.Ref;
name: ROPE ← self.file;
errorMessage: ROPENIL;
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], 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.Ref, 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.RefTextNode = 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.RefTextNode = tdd.text;
node: TextNode.RefTextNode = root;
formatName: ATOM = root.formatName;
child: TextNode.RefTextNode = 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.RefTextNode ← 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.RefTextNode = 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.RefTextNode, tSel: TEditDocument.Selection] = {
SelConcat: PROC [node: TextNode.RefTextNode, start, len: TextNode.Offset] 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.RefTextNode, 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.Ref;
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: BOOLFALSE;
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: BOOLFALSE] 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 [BOOLTRUE] = {
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: BOOLTRUE;
AddTable: PROC [r: Rope.ROPE] = {
bad: BOOLFALSE;
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: BOOLFALSE;
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: TEditImpl.PaintTEditDocument,
bltV: top, -- use blt to copy screen contents to top of viewer when height changes
bltH: none, -- but not if width changes
notify: TEditImpl.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.