DIRECTORY
Buttons USING [Create],
Convert USING [RopeFromInt],
ConvertUnsafe USING [ToRope],
EditSpanSupport USING [Apply],
IO USING [Error],
Menus USING [AppendMenuEntry, ChangeNumberOfLines, ClickProc, CreateEntry, CreateMenu, GetLine, GetNumberOfLines, Menu, MenuEntry, MenuLine, MenuProc, SetLine],
MessageWindow USING [Append, Blink, Clear],
NodeProps USING [GetProp, PutProp],
PFS,
PFSNames USING [StripVersionNumber],
Process USING [Detach, GetCurrent],
Rope USING [Cat, Concat, Equal, Flatten, FromRefText, Length, ROPE, 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 [FromRope, GetNewlineDelimiter],
TextEditBogus USING [GetRope],
TextLooks USING [noLooks],
TextNode USING [FirstChild, LastLocWithin, Location, LocNumber, LocOffset, LocRelative, LocWithin, Ref, RefTextNode],
TiogaIO,
TiogaMenuOps USING [],
TIPLinking USING [Append],
TIPTypes USING [TIPTable],
TIPUser USING [InstantiateNewTIPTable, InvalidTable],
ViewerBLT USING [ChangeNumberOfLines],
ViewerClasses USING [AdjustProc, DestroyProc, GetProc, InitProc, 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, ConvertUnsafe, EditSpanSupport, IO, Menus, MessageWindow, NodeProps, PFS, PFSNames, Process, Rope, TEditDocument, TEditDocumentPrivate, TEditInput, TEditInputOps, TEditLocks, TEditPrivate, TEditProfile, TEditRefresh, TEditScrolling, TEditSelection, TEditTouchup, TextEdit, TextEditBogus, TextNode, TiogaIO, TIPLinking, TIPUser, ViewerBLT, ViewerClasses, ViewerForkers, ViewerLocks, ViewerOps, ViewerTools
EXPORTS TEditDocument, TEditDocumentPrivate, TEditPrivate, TiogaMenuOps
SHARES Menus, ViewerClasses
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
TIPTable: TYPE ~ TIPTypes.TIPTable;
Stats:
TYPE ~
RECORD [
next: NAT ¬ 0,
seq: SEQUENCE size: NAT OF RECORD [file: ROPE, estimated, actual: INT]
];
stats: REF Stats ~ NEW[Stats[20]];
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]
};
DocumentFromRope:
PROC [rope:
ROPE, topLevel:
BOOL]
RETURNS [TextNode.Ref] ~ {
RETURN [TiogaIO.SimpleDocFromRope[rope]]
};
InitViewerDoc:
PUBLIC
PROC [self: ViewerClasses.Viewer, data:
REF
ANY] = {
InitViewerDocInternal[self: self, file: PFS.nullOpenFile, data: data];
};
InitViewerDocInternal:
PUBLIC
PROC [self: ViewerClasses.Viewer, file:
PFS.OpenFile, data:
REF
ANY] = {
tdd: TEditDocument.TEditDocumentData ¬ NARROW[self.data];
needInitTddText: BOOL ¬ FALSE;
IF self.link#
NIL
THEN {
IF tdd=NIL THEN self.data ¬ tdd ¬ NARROW[data]; -- someday I should really find out how to fix this in a less horrible manner
}
ELSE {
WITH data
SELECT
FROM
d: TEditDocument.TEditDocumentData => { self.data ¬ tdd ¬ d };
ENDCASE => IF tdd=NIL THEN { tdd ¬ AllocateDataForNewViewer[self] };
};
[] ¬ SpinAndLock[tdd, "InitViewerDoc"];
IF self.link#
NIL
THEN {
make sure another link didn't already init
and if so, copy that data
otherInit: ViewerClasses.Viewer ¬ NIL;
FOR v: ViewerClasses.Viewer ¬ self.link, v.link
UNTIL v=self
DO
IF NOT v.newVersion THEN {otherInit ¬ v; EXIT};
ENDLOOP;
IF otherInit=
NIL
THEN {needInitTddText ¬ TRUE}
ELSE {
somebody else already did the init
otherTdd: TEditDocument.TEditDocumentData ¬ NARROW[otherInit.data];
tdd.text ¬ otherTdd.text;
};
}
ELSE {
WITH data
SELECT
FROM
d: TEditDocument.TEditDocumentData => { needInitTddText ¬ TRUE };
root: TextNode.RefTextNode => { tdd.text ¬ root };
r: Rope.ROPE => { tdd.text ¬ DocumentFromRope[r, self.parent=NIL] };
t: REF TEXT => { tdd.text ¬ DocumentFromRope[Rope.FromRefText[t], self.parent=NIL] };
ENDCASE => {
IF data =
NIL
THEN { needInitTddText ¬ TRUE }
ELSE {--bad data-- tdd.text ¬ TiogaIO.SimpleDocFromRope["*ERROR*"] }
};
};
IF needInitTddText
THEN {
ENABLE {
PFS.Error => {
self.newFile ¬ TRUE;
MessageWindow.Append[Rope.Concat["TEditDocumentsImpl: ", error.explanation], TRUE];
MessageWindow.Blink[];
CONTINUE;
};
TiogaIO.Error => {
MessageWindow.Append["TEditDocumentsImpl: Error in Tioga formatting of ", TRUE];
MessageWindow.Append[self.file, FALSE];
MessageWindow.Blink[];
CONTINUE;
};
};
fileName: PFS.PATH ¬ NIL;
wantedUniqueID: PFS.UniqueID ¬ PFS.nullUniqueID;
IF tdd.text # NIL THEN { TEditInput.FreeTree[tdd.text]; tdd.text ¬ NIL };
IF file=
PFS.nullOpenFile
THEN { IF self.file#NIL THEN fileName ¬ PFS.PathFromRope[self.file] }
ELSE [fullFName: fileName, uniqueID: wantedUniqueID] ¬ PFS.GetInfo[file];
IF fileName#
NIL
THEN {
fullFName: PFS.PATH; uniqueID: PFS.UniqueID;
[fullFName: fullFName, uniqueID: uniqueID, root: tdd.text] ¬
TiogaIO.FromFile[fileName: fileName, wantedUniqueID: wantedUniqueID];
self.file ¬ PFS.RopeFromPath[fullFName];
};
};
IF tdd.text=NIL THEN tdd.text ¬ DocumentFromRope["", self.parent=NIL];
IF NodeProps.GetProp[tdd.text, $OpenFirstLevelOnly]#NIL THEN tdd.clipLevel ¬ 1;
tdd.lineTable.lastLine ¬ 0;
tdd.lineTable[0].pos ¬ [TextNode.FirstChild[tdd.text], 0];
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] =
INLINE {
tdd ¬ NEW[TEditDocument.TEditDocumentDataRec];
{
-- for now, just hack in a conservative size line table
maxLines: NAT ~ MAX[2, (self.ch/TEditCompile.minAvgLineLeading)+1];
lineTable: TEditDocument.LineTable ~ NEW[TEditDocument.LineTableRec[maxLines]];
lineTable.lastLine ¬ 0; lineTable.lastY ¬ 0;
tdd.lineTable ¬ lineTable;
};
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: 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 {
PFS.Error => { errorMessage ¬ error.explanation; CONTINUE };
IO.Error => { errorMessage ¬ IF msg#NIL THEN msg ELSE "IO.Error"; CONTINUE };
};
defaultKeep: INT ~ UserProfile.Number["Tioga.defaultKeep", 2];
fileName: PFS.PATH ~ PFSNames.StripVersionNumber[PFS.PathFromRope[name]];
fullFName: PFS.PATH ~ TiogaIO.ToFile[fileName, root].fullFName;
newFile: ROPE ~ PFS.RopeFromPath[fullFName];
newName: ROPE ~ PFS.RopeFromPath[PFSNames.StripVersionNumber[fullFName]];
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];
};
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;
format: ATOM = root.format;
child: TextNode.RefTextNode = IF node=NIL THEN NIL ELSE node.child;
rope ¬ IF data=NIL THEN "" ELSE NARROW[data];
<< -- DKW: this first case clobbers the $NewlineDelimiter property
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 ¬ DocumentFromRope[rope, self.parent = NIL];
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.format ¬ format;
};
};
};
$TiogaContents => {
info: ViewerTools.TiogaContents ¬ NARROW[data];
TEditInput.FreeTree[tdd.text];
tdd.text ¬ TiogaIO.FromPair[[info.contents, info.formatting]];
};
$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
NOT self.iconic
AND
NOT 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 {
loc1: TextNode.Location ~ [TextNode.FirstChild[tdd.text], 0];
loc2: TextNode.Location ~ TextNode.LastLocWithin[tdd.text];
offset: INT ~ TextNode.LocOffset[loc1: loc1, loc2: loc2,
skipCommentNodes: TRUE];
IF tdd.tsInfo#
NIL
THEN { start ¬ offset; length ¬ 0 } -- for typescripts, caret at end
ELSE { start ¬ 0; length ¬ offset }; -- else entire contents
}
ELSE { start ¬ sel.start; 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 => data ¬ TiogaIO.WritePlainToRope[tdd.text];
$TiogaContents => {
pair: TiogaIO.Pair ~ TiogaIO.ToPair[tdd.text];
data ¬ NEW[ViewerTools.TiogaContentsRec ¬ [pair.contents, pair.formatting]];
};
$SelChars => {
rope: Rope.ROPE ¬ NIL;
DoSelChars:
PROC [root: TextNode.RefTextNode, tSel: TEditDocument.Selection] = {
IF tSel.viewer #
NIL
AND tSel.granularity # point
THEN {
newline: ROPE ~ TextEdit.GetNewlineDelimiter[root];
SelConcat:
PROC [node: TextNode.RefTextNode, start, len:
INT]
RETURNS [stop:
BOOL] = {
nrope: Rope.ROPE ~ Rope.Substr[TextEditBogus.GetRope[node], start, len];
rope ¬ IF rope=NIL THEN nrope ELSE Rope.Cat[rope, newline, nrope];
RETURN [FALSE];
};
EditSpanSupport.Apply[[tSel.start.pos, tSel.end.pos], SelConcat];
};
};
TEditInputOps.CallWithLocks[DoSelChars, read];
IF rope=NIL THEN rope ¬ "";
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: 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
NOT found
THEN {
add it. do insertion sort to get it in the right place
GoesBefore:
PROC [m1, m2: Menus.MenuEntry]
RETURNS [
BOOL] =
INLINE
--gfi saver-- {
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 NOT 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] = {
ENABLE UNWIND => NULL;
IF tdd.lockProcess # Process.GetCurrent[] THEN ERROR;
IF (tdd.lock ¬ tdd.lock-1) = 0 THEN BROADCAST unlocked
};
systemDir:
ROPE ~
PFS.RopeFromPath[
PFS.GetWDir[]];
SystemTip:
PROC [base:
ROPE]
RETURNS [
ROPE] ~ {
RETURN [Rope.Cat[systemDir, base, ".tip"]];
};
ReloadTipTable:
PUBLIC
PROC = {
newTIP: TIPTable ¬ ReloadTable[tiogaTIP, "TiogaTIP", SystemTip["Tioga"]];
IF newTIP # NIL THEN tiogaClass.tipTable ¬ tiogaTIP ¬ newTIP;
};
ReloadReadonlyTipTable:
PUBLIC
PROC = {
newTIP: TIPTable ¬ ReloadTable[readonlyTIP, "ReadonlyTiogaTIP", SystemTip["ReadonlyTioga"]];
IF newTIP # NIL THEN readonlyTIP ¬ newTIP;
};
readonlyTIP: TIPTable;
tiogaTIP:
PUBLIC TIPTable;
ChangeTipTables:
PROC [newTIP, oldTIP: 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: TIPTable, profileKey, default: Rope.
ROPE]
RETURNS [newTIP: TIPTable] = {
ok: BOOL ¬ TRUE;
AddTable:
PROC [r: Rope.
ROPE] = {
bad: BOOL ¬ FALSE;
t: TIPTable;
msg: Rope.ROPE;
IF NOT ok THEN RETURN;
IF Rope.Equal[r,"default",
FALSE]
THEN {
TEditProfile.DoList["", AddTable, default];
RETURN
};
t ¬ TIPUser.InstantiateNewTIPTable[r
!
PFS.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 };
IF TIPLinking.Append[newTIP, t]#
NIL
THEN {
ok ¬ FALSE;
MessageWindow.Append[Rope.Concat["Loop in TIP table layers caused by ", r], TRUE];
};
};
TEditProfile.DoList[profileKey, AddTable, default];
IF ~ok
AND oldTIP=
NIL
THEN {
try the default by itself
ok ¬ TRUE; TEditProfile.DoList["", AddTable, default]
};
IF ok
AND 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
]];
[] ¬ Buttons.Create[info: [name: "Open"], proc: TEditDocumentPrivate.OpenButton];