-- Edited by Russ Atkinson, April 15, 1983 2:31 pm DIRECTORY Atom USING [Gensym, GetPName], Buttons USING [ButtonProc, Create], CIFS USING [Close, GetFC, Open, OpenFile, read, Error], Convert USING [ValueToRope], Directory USING [Error, GetProperty, LookupUnlimited, RemoveFile, Rename], File USING [Capability, Delete, nullCapability], FileIO USING [OpenFailed], InputFocus USING [GetInputFocus, SetInputFocus], Menus, -- USING lots MessageWindow USING [Append, Blink, Clear], NodeProps USING [GetProp, MapProps, PutProp], Process USING [Detach, GetCurrent], PropertyTypes USING [tCreateDate], PutGet USING [FromFile, ToFile, FromRope, ToRope, WriteRopePlain], EditSpanSupport USING [Apply], Rope USING [Cat, Concat, Equal, Find, Flatten, FromRefText, Length, ROPE, Substr, Text], RTFiles USING [IsFileInUse], System USING [GreenwichMeanTime, SecondsSinceEpoch], TextEdit USING [DocFromNode, FromRope, Ref], TextLooks USING [noLooks], TextNode USING [FirstChild, LastLocWithin, LocNumber, Location, LocOffset, LocRelative, LocWithin, Offset, NarrowToTextNode, NodeProps, pZone, Ref, RefTextNode, Root, TypeName], TEditCompile USING [minAvgLineLeading], TEditDocument USING [ForgetViewer, LineTableRec, RecordViewerForRoot, Selection, TEditDocumentData, TEditDocumentDataRec], TEditDocumentPrivate, TEditImpl USING [PaintTEditDocument, TEditNotifyProc], TEditInput USING [FreeTree], TEditInputOps USING [CallWithLocks], TEditLocks USING [Lock, LockDocAndTdd, Unlock, UnlockDocAndTdd], TEditProfile USING [DoList], TEditRefresh USING [ScrollToEndOfSel], TEditScrolling USING [ScrollTEditDocument], TEditSelection USING [Alloc, Free, InputModify, MakeSelection, pSel, sSel, fSel], TEditTouchup USING [fullUpdate], TiogaMenuOps, -- exports tiogaMenu TIPUser USING [InstantiateNewTIPTable, InvalidTable, TIPTable], ViewerBLT USING [ChangeNumberOfLines], ViewerOps USING [EnumerateChildren, PaintHint, PaintViewer, RegisterViewerClass, SetMenu, SetNewVersion], ViewerClasses USING [BltRule, DestroyProc, GetProc, InitProc, SaveProc, SetProc, Viewer, ViewerClass, ViewerClassRec], ViewerTools USING [EnableUserEdits, InhibitUserEdits, SelPos, SelPosRec, TiogaContents, TiogaContentsRec], VirtualDesktops USING [EnumerateViewers]; TEditDocumentsImpl: CEDAR MONITOR IMPORTS Atom, Buttons, CIFS, Convert, Directory, EditSpanSupport, File, FileIO, InputFocus, Menus, MessageWindow, NodeProps, Process, PutGet, Rope, RTFiles, System, TEditDocument, TEditDocumentPrivate, TextEdit, TextNode, TEditImpl, TEditInput, TEditInputOps, TEditLocks, TEditProfile, TEditRefresh, TEditTouchup, TEditScrolling, TEditSelection, TIPUser, ViewerBLT, ViewerOps, ViewerTools, VirtualDesktops EXPORTS TEditImpl, TEditDocument, TEditDocumentPrivate, TiogaMenuOps SHARES Menus, ViewerClasses = BEGIN OPEN Menus, TEditDocument, TEditDocumentPrivate, ViewerClasses; fatalTiogaError: PUBLIC ERROR = CODE ; -- for RETURN WITH ERROR monitor clearing punts InitTEditDocument: PUBLIC InitProc = { data: REF ANY _ self.data; self.data _ NIL; InitViewerDoc[self, data] }; InitViewerDoc: PUBLIC PROC [self: Viewer, data: REF ANY] = BEGIN tdd: TEditDocumentData _ NARROW[self.data]; InitTddText: PROC = { IF tdd.text # NIL THEN TEditInput.FreeTree[tdd.text]; tdd.text _ NIL; IF self.file#NIL THEN tdd.text _ TextNode.NarrowToTextNode[PutGet.FromFile[self.file ! CIFS.Error => {self.newFile _ TRUE; CONTINUE}]]; IF tdd.text=NIL THEN tdd.text _ TextNode.NarrowToTextNode[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.NarrowToTextNode[TextNode.FirstChild[tdd.text]], 0] }; IF self.link#NIL THEN BEGIN otherInit: 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: Viewer _ self.link, v.link UNTIL v=self DO IF ~v.newVersion THEN {otherInit _ v; EXIT}; ENDLOOP; IF otherInit=NIL THEN InitTddText[] ELSE BEGIN -- somebody else already did the init otherTdd: TEditDocumentData _ NARROW[otherInit.data]; tdd.text _ otherTdd.text; END; InitLineTable[]; END ELSE IF data=NIL THEN BEGIN IF tdd=NIL THEN tdd _ AllocateDataForNewViewer[self]; [] _ SpinAndLock[tdd, "InitViewerDoc"]; InitTddText[]; InitLineTable[]; END ELSE WITH data SELECT FROM d: TEditDocumentData => BEGIN self.data _ tdd _ d; [] _ SpinAndLock[tdd, "InitViewerDoc"]; InitTddText[]; InitLineTable[]; END; root: TextNode.RefTextNode => BEGIN IF tdd=NIL THEN tdd _ AllocateDataForNewViewer[self]; [] _ SpinAndLock[tdd, "InitViewerDoc"]; tdd.text _ root; InitLineTable[]; END; r: Rope.ROPE => BEGIN IF tdd=NIL THEN tdd _ AllocateDataForNewViewer[self]; [] _ SpinAndLock[tdd, "InitViewerDoc"]; tdd.text _ TextNode.NarrowToTextNode[ TextEdit.DocFromNode[TextEdit.FromRope[r]]]; InitLineTable[]; END; t: REF TEXT => BEGIN IF tdd=NIL THEN tdd _ AllocateDataForNewViewer[self]; [] _ SpinAndLock[tdd, "InitViewerDoc"]; tdd.text _ TextNode.NarrowToTextNode[ TextEdit.DocFromNode[TextEdit.FromRope[Rope.FromRefText[t]]]]; InitLineTable[]; END; ENDCASE => BEGIN -- bad data IF tdd=NIL THEN tdd _ AllocateDataForNewViewer[self]; [] _ SpinAndLock[tdd, "InitViewerDoc"]; tdd.text _ TextNode.NarrowToTextNode[PutGet.FromRope["*ERROR*"]]; InitLineTable[]; END; RecordViewerForRoot[self,tdd.text]; IF HasBrackets[self.file] THEN ViewerTools.InhibitUserEdits[self] ELSE ViewerTools.EnableUserEdits[self]; Unlock[tdd]; END; HasBrackets: PROC [file: Rope.ROPE] RETURNS [BOOL] = { Has: PROC [delim: Rope.ROPE] RETURNS [BOOL] = { RETURN [Rope.Find[file, delim] # -1] }; RETURN [Has["/"] OR Has["["] OR Has["<"]] }; AllocateDataForNewViewer: PROC [self: Viewer] RETURNS [tdd: TEditDocumentData] = BEGIN tdd _ TextNode.pZone.NEW[TEditDocumentDataRec]; tdd.lineTable _ TextNode.pZone.NEW[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; END; SaveTEditDocument: PUBLIC SaveProc = BEGIN tdd: TEditDocumentData _ NARROW[self.data]; dollarCap: File.Capability _ File.nullCapability; root: TextNode.Ref; file: Rope.Text _ Rope.Flatten[self.file]; -- makes LOOPHOLE to LONG STRING OK dollarFile: Rope.Text _ Rope.Flatten[Rope.Concat[file, "$"]]; IF file.Length = 0 THEN BEGIN IF ~force THEN BEGIN MessageWindow.Append["Can't SAVE -- no file name!", TRUE]; MessageWindow.Blink[]; ViewerOps.SetNewVersion[self]; -- turn the bit back on RETURN; END; file _ Rope.Flatten[Rope.Concat[Atom.GetPName[Atom.Gensym['F]], ".SaveAllEdits"]]; dollarFile _ Rope.Flatten[Rope.Concat[file, "$"]]; END; IF tdd.readOnly THEN BEGIN MessageWindow.Append["Can't SAVE -- read only document!", TRUE]; MessageWindow.Blink[]; RETURN; END; IF ~force THEN { -- lock so no edits while saving MessageWindow.Clear[]; [] _ 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 IF FileIsMoreRecent[root, file] THEN { -- the create date of the file on the disk MessageWindow.Append[Rope.Concat[file," on disk is newer than this one."], TRUE]; MessageWindow.Blink[] }; [] _ TEditLocks.Lock[root, "SaveTEditDocument", read] } ELSE root _ tdd.text; TRUSTED { FOR dollarIndex: NAT IN [0..1000) DO dollarCap _ Directory.LookupUnlimited[ fileName: LOOPHOLE[dollarFile] ! Directory.Error => CONTINUE]; IF dollarCap#File.nullCapability THEN { IF RTFiles.IsFileInUse[dollarCap] THEN { dollarCap _ File.nullCapability; dollarFile _ Rope.Flatten[Rope.Cat[file, "$", Convert.ValueToRope[[signed[dollarIndex]]], "$"]]; IF dollarIndex = 999 THEN ERROR; LOOP; }; Directory.RemoveFile[LOOPHOLE[dollarFile], dollarCap]; }; EXIT; ENDLOOP; Directory.Rename[LOOPHOLE[file], LOOPHOLE[dollarFile] ! Directory.Error => CONTINUE]; }; [] _ PutGet.ToFile[file, root ! FileIO.OpenFailed => { IF why=illegalFileName THEN { MessageWindow.Append[file, TRUE]; MessageWindow.Append[" is an illegal file name. Operation aborted."]; MessageWindow.Blink[]; CONTINUE }}]; IF dollarCap#File.nullCapability THEN TRUSTED {Process.Detach[FORK ReclaimDollarFile[dollarCap]]}; IF ~force THEN TEditLocks.Unlock[root]; END; FileIsMoreRecent: PUBLIC PROC [root: TextNode.Ref, file: Rope.ROPE] RETURNS [BOOL] = TRUSTED { prop: REF System.GreenwichMeanTime; fh: CIFS.OpenFile; createDate: System.GreenwichMeanTime; fileCap: File.Capability; IF (prop _ NARROW[NodeProps.GetProp[root, $FileCreateDate]]) = NIL THEN RETURN [FALSE]; fh _ CIFS.Open[file, CIFS.read ! CIFS.Error => {CONTINUE}]; IF fh = NIL THEN RETURN [FALSE]; fileCap _ CIFS.GetFC[fh]; CIFS.Close[fh]; Directory.GetProperty[file: fileCap, property: PropertyTypes.tCreateDate, propertyValue: DESCRIPTOR[@createDate, SIZE[System.GreenwichMeanTime]]]; RETURN [System.SecondsSinceEpoch[prop^] < System.SecondsSinceEpoch[createDate]] }; ReclaimDollarFile: PROC [file: File.Capability] = TRUSTED { File.Delete[file ! ABORTED => CONTINUE]; }; nullNode: TextNode.RefTextNode = TextEdit.FromRope[NIL]; SetTEditDocument: SetProc = BEGIN KillSelections: PROC [self: ViewerClasses.Viewer] = { OPEN TEditSelection; IF pSel # NIL AND pSel.viewer = self THEN MakeSelection[selection: primary]; IF sSel # NIL AND sSel.viewer = self THEN MakeSelection[selection: secondary]; IF fSel # NIL AND fSel.viewer = self THEN MakeSelection[selection: feedback] }; tdd: TEditDocumentData _ NARROW[self.data]; IF self.destroyed OR tdd = NIL THEN RETURN; IF op#$SelPos AND InputFocus.GetInputFocus[].owner=self THEN InputFocus.SetInputFocus[]; IF op=NIL OR op=$TiogaContents OR op=$TiogaDocument OR op=$KeepRootStyleProps THEN BEGIN rope: Rope.ROPE; IF self.link#NIL THEN ERROR; -- not yet implemented [] _ SpinAndLock[tdd, "SetTEditDocument"]; KillSelections[self]; IF op=NIL OR op=$KeepRootStyleProps THEN { root: TextNode.RefTextNode = tdd.text; node: TextNode.RefTextNode = TextNode.NarrowToTextNode[root]; typename: TextNode.TypeName = root.typename; child: TextNode.RefTextNode = IF node=NIL THEN NIL ELSE TextNode.NarrowToTextNode[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 [BOOLEAN] = { 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; 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.typename _ typename; [] _ 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 _ TextNode.NarrowToTextNode[ 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.typename _ typename }; } } ELSE IF op=$TiogaContents THEN { info: ViewerTools.TiogaContents _ NARROW[data]; rope _ Rope.Concat[info.contents, info.formatting]; TEditInput.FreeTree[tdd.text]; tdd.text _ TextNode.NarrowToTextNode[PutGet.FromRope[rope]] } ELSE IF op=$TiogaDocument THEN { root: TextNode.RefTextNode _ NARROW[data]; TEditInput.FreeTree[tdd.text]; tdd.text _ root } ELSE ERROR; tdd.lineTable.lastLine _ 0; tdd.lineTable[0].pos _ [TextNode.NarrowToTextNode[TextNode.FirstChild[tdd.text]], 0]; RecordViewerForRoot[self,tdd.text]; self.newVersion _ FALSE; -- clear edited bit IF finalise THEN ViewerOps.PaintViewer[self, all, TRUE, TEditTouchup.fullUpdate]; Unlock[tdd]; END ELSE IF op = $SelPos THEN BEGIN OPEN TEditSelection; IF ~self.iconic THEN BEGIN -- 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 BEGIN 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}; END 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]; END; END ELSE IF op = $ReadOnly THEN BEGIN self.tipTable _ readonlyTIP; tdd.readOnly _ TRUE; END ELSE IF op = $ReadWrite THEN BEGIN self.tipTable _ tiogaTIP; tdd.readOnly _ FALSE; END ELSE ERROR; END; GetTEditDocument: GetProc = BEGIN tdd: TEditDocumentData = NARROW[self.data]; IF op=NIL THEN { node: TextNode.RefTextNode = TextNode.NarrowToTextNode[tdd.text.child]; IF node#NIL AND node.last AND node.child=NIL THEN -- simple case of only one node RETURN [node.rope]; RETURN [PutGet.WriteRopePlain[tdd.text]] }; IF op = $TiogaContents THEN { 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]; RETURN [TextNode.pZone.NEW[ViewerTools.TiogaContentsRec _ [contents, formatting]]] }; IF op = $SelChars THEN BEGIN OPEN TEditSelection; rope: Rope.ROPE; -- hack to fix compiler problem DoSelChars: PROC [root: TextEdit.Ref, tSel: Selection] = { SelConcat: PROC [node: TextNode.RefTextNode, start, len: TextNode.Offset] RETURNS [stop: BOOLEAN] = { 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]; RETURN[rope]; END ELSE IF op = $SelPos THEN BEGIN OPEN TEditSelection; sel: ViewerTools.SelPos _ TextNode.pZone.NEW[ViewerTools.SelPosRec]; DoSelPos: PROC [root: TextEdit.Ref, tSel: 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]; RETURN[sel]; END ELSE ERROR; END; DestroyTEditDocument: DestroyProc = { 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: Viewer, file: Rope.ROPE, tdd: TEditDocument.TEditDocumentData] = { 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 RecordUnsavedDocument[file, root] ELSE TEditInput.FreeTree[root]; Unlock[tdd] }; ChangeMenu: PROC [viewer: Viewer, subMenu: MenuEntry] = { menu: Menus.Menu _ viewer.menu; found: BOOLEAN _ 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; 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: MenuEntry] RETURNS [BOOL] = { Priority: PROC [m: MenuEntry] RETURNS [INTEGER] = { 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: TEditDocumentData, who: Rope.ROPE, interrupt, defer: BOOL _ FALSE] RETURNS [ok: BOOL] = TRUSTED BEGIN ENABLE UNWIND => NULL; myProcess: PROCESS = LOOPHOLE[Process.GetCurrent[]]; IF myProcess#tdd.lockProcess THEN BEGIN 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; END; tdd.lock _ tdd.lock+1; RETURN [TRUE]; END; Unlock: PUBLIC ENTRY PROC [tdd: 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 = BEGIN newTIP: TIPUser.TIPTable _ ReloadTable[tiogaTIP, "TiogaTIP", "/Indigo/Tioga/TEdit/Tioga.tip"]; IF newTIP#NIL THEN ChangeTipTables[tiogaClass.tipTable _ tiogaTIP _ newTIP, FALSE]; END; ReloadReadonlyTipTable: PUBLIC PROC = BEGIN newTIP: TIPUser.TIPTable _ ReloadTable[readonlyTIP, "ReadonlyTiogaTIP", "/Indigo/Tioga/TEdit/ReadonlyTioga.tip"]; IF newTIP # NIL THEN ChangeTipTables[readonlyTIP _ newTIP, TRUE]; END; readonlyTIP: TIPUser.TIPTable; tiogaTIP: PUBLIC TIPUser.TIPTable; ChangeTipTables: PROC [newTIP: TIPUser.TIPTable, readonly: BOOL] = { changeTip: PROC [v: Viewer] RETURNS [BOOL _ TRUE] = { SELECT v.class.flavor FROM $Text => { tdd: TEditDocumentData _ NARROW[v.data]; IF tdd.readOnly=readonly THEN v.tipTable _ newTIP }; $Container => ViewerOps.EnumerateChildren[v, changeTip]; ENDCASE }; VirtualDesktops.EnumerateViewers[changeTip] }; ReloadTable: PUBLIC PROC [oldTIP: TIPUser.TIPTable, profileKey, default: Rope.ROPE] RETURNS [newTIP: TIPUser.TIPTable] = { ok: BOOL _ TRUE; AddTable: PROC [r: Rope.ROPE] = { bad: BOOLEAN _ 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 ! CIFS.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 => BEGIN newTIP.mouseTicks _ MIN[t.mouseTicks, newTIP.mouseTicks]; x.opaque _ FALSE; x.link _ t; END; ENDLOOP; }; TEditProfile.DoList[profileKey, AddTable, default]; IF ~ok AND oldTIP=NIL THEN { -- try the default by itself ok _ TRUE; TEditProfile.DoList["", AddTable, default] }; }; tiogaClass: ViewerClass _ NEW[ViewerClassRec _ [ paint: TEditImpl.PaintTEditDocument, bltContents: top, -- use blt to copy screen contents to top of viewer when change dimensions notify: TEditImpl.TEditNotifyProc, modify: TEditSelection.InputModify, init: InitTEditDocument, set: SetTEditDocument, get: GetTEditDocument, save: SaveTEditDocument, destroy: DestroyTEditDocument, scroll: TEditScrolling.ScrollTEditDocument ]]; tiogaMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; findMenu, levelMenu, lineMenu: PUBLIC MenuEntry; --preSave: REF Menus.ClickProc = NEW[Menus.ClickProc _ PreSave]; preReset: REF Menus.ClickProc = NEW[Menus.ClickProc _ PreReset]; --preClear: REF Menus.ClickProc = NEW[Menus.ClickProc _ PreClear]; preStore: REF Menus.ClickProc = NEW[Menus.ClickProc _ 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]; Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "Clear", proc: Clear --, guarded: TRUE, documentation: preClear--]]; Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "Reset", proc: Reset, guarded: TRUE, documentation: preReset]]; Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "Get", proc: Get --, guarded: TRUE, documentation: preGet--]]; Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "GetImpl", proc: GetImpl --,guarded: TRUE, documentation: preGetImpl--]]; Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "PrevFile", proc: PreviousFile --,guarded: TRUE, documentation: preLoadPrevious--]]; Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "Store", proc: Store, guarded: TRUE, documentation: preStore]]; Menus.AppendMenuEntry[menu: tiogaMenu, entry: Menus.CreateEntry[name: "Save", proc: Save --, guarded: TRUE, documentation: preSave--]]; Menus.AppendMenuEntry[tiogaMenu, Menus.CreateEntry["Time", Time]]; Menus.AppendMenuEntry[tiogaMenu, Menus.CreateEntry["Split", Split]]; Menus.AppendMenuEntry[tiogaMenu, Menus.CreateEntry["Places", FindMenu]]; Menus.AppendMenuEntry[tiogaMenu, Menus.CreateEntry["Levels", LevelMenu]]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Reselect", Reselect], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["PrevPlace", JumpToPrevious], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Normalize", Normalize], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Position", Position], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Def", FindDef], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Word", FindWord], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["Find", Find], 1]; findMenu _ Menus.GetLine[tiogaMenu, 1]; Menus.SetLine[tiogaMenu, 1, NIL]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["AllLevels", AllLevels], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["FewerLevels", FewerLevels], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["MoreLevels", MoreLevels], 1]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["FirstLevelOnly", 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: NewButton, fork: FALSE]; [] _ Buttons.Create[info: [name: "Open"], proc: OpenButton]; END. PreSave: PUBLIC Menus.MenuProc = { viewer: ViewerClasses.Viewer = NARROW[parent]; tdd: TEditDocumentData _ NARROW[viewer.data]; root: TextNode.Ref; file: Rope.ROPE = viewer.name; IF file=NIL THEN BEGIN MessageWindow.Append["Can't SAVE -- no file name!", TRUE]; MessageWindow.Blink[]; ViewerOps.SetNewVersion[viewer]; -- turn the bit back on RETURN; END; IF tdd.readOnly THEN BEGIN MessageWindow.Append["Can't SAVE -- read only document!", TRUE]; MessageWindow.Blink[]; RETURN; END; [] _ SpinAndLock[tdd, "PreSave"]; -- may have other operation in progress root _ tdd.text; Unlock[tdd]; IF FileIsMoreRecent[root, file] THEN { -- the create date of the file on the disk MessageWindow.Append[Rope.Concat[file," on disk is newer than this one."], TRUE]; MessageWindow.Blink[] } ELSE MessageWindow.Append[Rope.Concat["Confirm save of ", file], TRUE]; }; †-- TEditDocumentsImpl.mesa; Written by S. McGregor -- Edited by McGregor on March 28, 1983 9:21 am -- Edited by Paxton on June 2, 1983 10:39 am -- Edited by Maxwell, January 6, 1983 11:03 am Last Edited by: Plass, April 20, 1983 5:02 pm -- make sure another link didn't already init -- and if so, copy that data -- for now, just hack in a conservative size line table RRA: The file is in the directory, but we must check to see if the file is in use as far as the system is concerned, so we do not delete a file out from under someone. Returns true if the file on the disk has a more recent create date than the root file create date This duplicates a procedure in TEditDocuments2Impl. Someday when you can change an interface, export it from there rather than copying it. -- Bill Should really have a write lock on the document for this. change for Tioga2. Need to pass self.file as variable because it will be set to NIL Menus.ChangeNumberOfLines[menu, newLast-1]; higher priority means goes above in series of submenus looks at rope for first item to identify the subMenu. Menus.ChangeNumberOfLines[menu, newLast+1]; ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh]; ViewerOps.PaintViewer[viewer, all]; -- tipTable set below -- first row -- build the places menu -- build the levels menu don't fork since will change the selection; want to capture following typein ʘJšÏc2™2Jš/™/Jš,™,Jš.™.Jš2˜2J™-šÏk ˜ Jšœžœ˜Jšœžœ˜#Jšžœžœ-˜7Jšœžœ˜Jšœ žœ;˜JJšœžœ&˜0Jšœžœ˜Jšœ žœ ˜0Jšœ ˜Jšœžœ˜+Jšœ žœ˜-Jšœžœ˜#Jšœžœ˜"Jšœžœ6˜BJšœžœ ˜Jšœžœ:žœ˜XJšœžœ˜Jšœžœ(˜4Jšœ žœ˜,Jšœ žœ ˜Jšœ žœ£˜±Jšœ žœ˜'Jšœžœg˜zJ˜Jšœ žœ'˜6Jšœ žœ ˜Jšœžœ˜$Jšœ žœ0˜@Jšœ žœ ˜Jšœ žœ˜&Jšœžœ˜+Jšœžœ=˜QJšœ žœ˜ Jšœ˜"Jšœžœ2˜?Jšœ žœ˜&Jšœ žœZ˜iJšœžœc˜vJšœ žœY˜jJšœžœ˜)J˜—Jšœž ˜!J˜Jšžœžœû˜–Jšžœ=˜DJšžœ˜J˜Jšžœžœ;˜EJ˜Jšœžœžœžœ/˜VJ˜šœžœ ˜&Jšœžœžœ ˜Jšœ žœ˜Jšœ˜—J˜š Ïn œžœžœžœžœž˜@Jšœžœ ˜+J˜šŸ œžœ˜Jšžœ žœžœ˜5Jšœ žœ˜šžœ žœžœ ˜ ˜3Jšœžœžœžœ˜2——šžœ žœžœ ˜J˜I——J˜šŸ œžœ˜Jšžœ2žœžœ˜OJ˜J˜WJ˜—šžœ žœžœž˜Jš-™-Jš™Jšœžœ˜Jš žœžœžœžœ M˜~Jšœ'˜'šžœžœž˜1Jšžœžœžœ˜,Jšžœ˜—Jšžœ žœžœ˜#šžœžœ%˜0Jšœžœ˜5J˜Jšžœ˜—J˜Jšž˜—š žœžœžœžœž˜Jšžœžœžœ&˜5Jšœ'˜'J˜J˜Jšž˜—šžœžœžœž˜šœž˜J˜Jšœ'˜'J˜J˜Jšžœ˜—šœž˜#Jšžœžœžœ&˜5Jšœ'˜'J˜J˜Jšžœ˜—šœžœž˜Jšžœžœžœ&˜5Jšœ'˜'˜%J˜,—J˜Jšžœ˜—šœžœžœž˜Jšžœžœžœ&˜5Jšœ'˜'˜%J˜>—J˜Jšžœ˜—šžœžœ ˜Jšžœžœžœ&˜5Jšœ'˜'J˜AJ˜Jšžœ˜——J˜#Jšžœžœ#˜AJšžœ#˜'Jšœ ˜ Jšžœ˜J˜—š Ÿ œžœ žœžœžœ˜6š Ÿœžœžœžœžœ˜/Jšžœ!˜'—Jšžœ žœ žœ ˜,J˜—šŸœžœžœž˜VJšœžœ˜/Jš7™7šœžœžœ2˜eJšœžœ˜&—Jš žœžœ žœžœ$žœ˜YJ˜Jšžœ˜—J˜šœžœ ž˜*Jšœžœ ˜+J˜1J˜Jšœ,#˜OJšœ=˜=šžœžœž˜šžœžœžœž˜Jšœ4žœ˜:J˜Jšœ˜6Jšžœ˜Jšžœ˜—JšœR˜RJšœ2˜2Jšžœ˜—šžœžœž˜Jšœ:žœ˜@J˜Jšžœ˜Jšžœ˜—šžœžœ ˜1J˜Jšœ,'˜SJ˜Jšœ 5˜Bšžœžœ*˜QJšœKžœ˜QJšœ˜—Jšœ7˜7—Jšžœ˜šžœ˜ šžœžœžœ ž˜$šœ&˜&Jšœ žœ ˜Jšœžœ˜—šžœžœ˜'Jšœ§™§šžœ žœ˜(Jšœ ˜ šœ ˜ JšœS˜S—Jšžœžœžœ˜ Jšžœ˜Jšœ˜—Jšœžœ˜6J˜—Jšžœ˜Jšžœ˜—šœžœžœ ˜5Jšœžœ˜—J˜—šœ6˜6šžœžœ˜Jšœžœ˜!JšœF˜FJšœ˜Jšžœ˜ ——šžœž˜%Jšžœžœ ˜<—Jšžœžœ˜'Jšžœ˜J˜—šŸœžœžœ!žœžœžœžœ˜^J™aJšœžœ˜#Jšœžœ ˜Jšœ%˜%Jšœ˜Jš žœ žœ.žœžœžœžœ˜WJš œžœ žœžœ žœ˜;Jš žœžœžœžœžœ˜ Jšœ žœ ˜Jšžœ ˜šœI˜IJšœž œžœ˜H—JšžœL˜RJ˜—šŸœžœžœ˜;Jšœžœžœ˜(Jšœ˜—J˜Jšœ3žœ˜8J˜šœž˜!šŸœžœ"žœ˜JJ™“Jšžœžœžœžœ#˜LJšžœžœžœžœ%˜NJšžœžœžœžœ&˜O—Jšœžœ ˜+Jš žœžœžœžœžœ˜+Jšžœ žœ'žœ˜Xš žœžœžœžœžœžœž˜XJšœ žœ˜Jš žœ žœžœžœ˜3Jšœ*˜*Jšœ˜šžœžœžœžœ˜*J˜&J˜=Jšœ,˜,šœ˜Jš žœžœžœžœžœ'˜@—Jš œžœžœžœžœžœ˜-š žœžœžœ žœ žœžœ˜Vš Ÿ œžœžœ žœžœžœ˜@šžœžœ˜'šžœžœžœ˜AJšžœžœ˜(——Jšžœžœ˜—Jšœ˜J™MJšœ˜JšœB˜BJšœ&˜&Jšœžœ˜1Jšžœžœ˜:Jšœ+žœžœ˜:—šžœ˜Jšœžœ˜šžœžœ˜ J˜*J˜.—Jšœ˜˜%J˜/—šžœžœ˜ Jšžœžœžœ.˜@Jšžœ žœžœ0˜CJšœ˜—Jšœ˜—J˜—šžœžœžœ˜ Jšœ"žœ˜/J˜3J˜J˜=—šžœžœžœ˜ Jšœžœ˜*J˜J˜—Jšžœžœ˜ J˜J˜UJ˜#Jšœžœ˜,Jšžœ žœ"žœ˜QJšœ ˜ Jšž˜—š žœžœžœžœžœ˜4šžœžœžœ˜:Jšœ7˜7Jšœžœ˜'Jšœžœ˜J˜=šžœžœžœž˜šžœ žœžœ'˜@šœ*˜FJšœ8žœ˜>—J˜ —šžœ1˜8J˜ šœ*˜GJšœ8žœ˜A——Jšž˜—šžœ˜Jšœ žœ8˜GJ˜Jšžœ žœ$˜IJ˜—JšœQžœ˜W˜$Jšœ!žœ!žœ˜K—Jšœ ˜ Jšœžœ žœžœ˜4J˜J˜Jšœžœ žœžœ˜4Jšœžœ˜J˜J˜(J˜Jšœ$žœ˜+Jšžœ˜—Jšž˜—šžœžœžœž˜!J˜Jšœžœ˜Jšž˜—šžœžœžœž˜"J˜Jšœžœ˜Jšž˜—Jšžœžœ˜ Jšžœ˜J˜—šœž˜!Jšœžœ ˜+šžœžœžœ˜J˜Gš žœžœžœ žœ žœžœ˜QJšžœ ˜—Jšžœ%˜+—šžœžœ˜Jšœžœ˜ Jšœžœ˜J˜5J˜MJ˜?Jšžœžœ;˜U—šžœžœžœžœ˜1Jšœ žœ˜0šŸ œžœ*˜:šŸ œžœ:˜IJšžœžœ˜šœžœžœžœ#˜—Jšžœžœ˜—šžœžœžœžœ˜7JšœA˜A—Jšžœžœžœ ˜—J˜.Jšžœ˜ Jšž˜—š žœžœžœžœžœ˜4Jšœ)žœ˜DšŸœžœ*˜8JšœEžœ˜K˜ Jšœ<žœ˜D——Jšœ,˜,Jšžœ˜ Jšž˜—Jšžœžœ˜ Jšžœ˜J˜—šœ%˜%Jšœ=˜QJš žœ žœžœžœ,˜Lšžœžœ&žœ˜YJ™@——J˜šŸœžœžœ)˜cJ˜Jš žœžœžœžœ*˜DJšœ.˜.Jšœ˜š žœžœžœžœž˜?Jšœ!˜!—Jšžœ˜Jšœ˜—J˜šŸ œžœ)˜9J˜Jšœžœžœ˜Jšœ8˜8Jšœ$˜$šžœžœžœ%˜Ošžœ6žœ˜Sšžœžœž˜)Jšœ1˜1Jšžœ˜—Jšœ+™+Jšœ˜Jšœžœžœ˜—Jšžœ˜—šžœžœ:˜KšŸ œžœžœžœ˜7šŸœžœžœžœ˜3J™6J™5šžœžœžœž˜Jšœ ˜ Jšœ*˜*Jšžœ!œ˜4——Jšžœ!˜'—Jšœžœ žœ˜>Jšœ˜Jšœ+™+šžœžœž˜)šžœ-žœ˜Dšžœž œžœž˜3Jšœ2žœ˜:—Jšœ ˜ Jšœžœžœ˜—Jšžœ˜—Jšžœžœ'˜5J˜—J™VJ™#Jšœ0˜0J˜J˜—Jšœ žœžœ˜MJ˜Jšœ žœžœ˜KJ˜Jšœ ž œ˜J˜š Ÿ œžœžœžœ$žœ˜GJšœžœžœ˜Jšžœžœžœž˜"Jšžœžœžœ˜Jšœ žœžœ˜4šžœžœž˜'šžœ žœ˜Jš žœžœžœžœžœ˜3Jšœ"˜"—Jšžœ žœžœ žœ˜+J˜Jšœ'˜6Jšžœ žœ!˜2Jšžœ˜—J˜Jšžœžœ˜Jšžœ˜J˜—š Ÿœžœžœžœžœ˜>Jšžœžœžœ˜Jšžœ(žœžœ˜5Jšžœžœž œ ˜9J˜—šŸœžœžœž˜#šœ˜JšœC˜C—Jšžœžœžœ:žœ˜SJšžœ˜J˜—šŸœžœžœž˜+šœ˜JšœV˜V—Jšžœ žœžœ'žœ˜AJšžœ˜J˜—Jšœ˜Jšœ žœ˜"J˜šŸœžœ&žœ˜Dš œ žœ žœžœžœ˜5šžœž˜˜ Jšœžœ ˜(Jšžœžœ˜4—J˜8Jšžœ˜ ——J˜.J˜—šŸ œžœžœ6žœ˜SJšžœ˜&Jšœžœžœ˜šŸœžœ žœ˜!Jšœžœžœ˜J˜Jšœ žœ˜Jšžœžœžœ˜šžœžœžœ˜'Jšœ+˜+Jšžœ˜ —˜&šžœ ˜Jšœžœ˜ J˜5Jšžœ˜ —šœ˜Jšœžœ˜ J˜;Jšžœ˜ ——Jš žœžœžœžœžœ˜DJšžœžœžœžœ˜*šžœ&žœžœž˜7šžœ!žœžœž˜2Jšžœžœžœ˜Jšœžœ˜ JšœLžœ˜RJšžœ˜Jšžœ˜—Jšžœ˜—šžœ&žœžœž˜<šžœžœž˜Jšœžœ"˜9Jšœ žœ˜J˜ Jšžœ˜—Jšžœ˜—Jšœ˜—Jšœ3˜3š žœžœžœžœ˜9Jšœžœ/˜8—Jšœ˜J˜—šœžœ˜0J˜$JšœJ˜]J˜"J˜#J˜J˜J˜J˜J˜J˜*Jš™J˜J˜—šœ žœ!˜2J˜—Jšœžœ ˜0J˜Jšœ žœžœ˜@Jšœ žœžœ˜@Jšœ žœžœ˜BJšœ žœžœ˜@Jšœžœžœ$˜PJšœ žœžœ˜>Jšœžœžœ˜FJ˜Jš ™ šœZ˜ZJšœ žœ˜/—šœ&˜&Jšœ>žœ˜^—šœV˜VJšœ žœ˜-—šœ&˜&šœ7˜7Jšœ žœ ˜0——šœ&˜&šœ=˜=Jšœ žœ%˜5——šœ&˜&Jšœ>žœ˜^—šœX˜XJšœ žœ˜.—J˜BJ˜DJ˜HJ˜IJ˜Jš™JšœM˜MJšœT˜TJšœO˜OJšœM˜MJšœG˜GJšœI˜IJšœE˜EJ˜Jšœ'˜'Jšœžœ˜!J˜Jš™JšœO˜OJšœS˜SJšœQ˜QJšœY˜YJ˜Jšœ(˜(Jšœžœ˜!J˜Jš˜Jšœ)#˜LJ˜J˜+J˜J˜2J˜šœ@žœ˜GJ™L—J˜J˜