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 { 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 { 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]; 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 { MessageWindow.Clear[]; TEditDocumentPrivate.KillSelections[self]; [] ¬ 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; { ENABLE { PFS.Error => { errorMessage ¬ error.explanation; CONTINUE }; IO.Error => { errorMessage ¬ IF msg#NIL THEN msg ELSE "IO.Error"; CONTINUE }; }; 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; }; 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] = { RETURN [FALSE]; }; nullNode: TextNode.RefTextNode = TextEdit.FromRope[NIL]; SetTEditDocument: ViewerClasses.SetProc = { 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 { 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; 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 { ViewerForkers.ForkPaint[self, hint, TRUE, TEditTouchup.fullUpdate, TRUE]; }; }; $SelPos => IF NOT self.iconic AND NOT self.destroyed THEN { 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 = { 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]; 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] = { 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 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 NOT found THEN { GoesBefore: PROC [m1, m2: Menus.MenuEntry] RETURNS [BOOL] = INLINE --gfi saver-- { Priority: PROC [m: Menus.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 { 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]; 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 { 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 = { 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 ]]; tiogaMenu: PUBLIC Menus.Menu ¬ NIL; findMenu, levelMenu, lineMenu: PUBLIC Menus.MenuEntry ¬ NIL; preReset: REF Menus.ClickProc = NEW[Menus.ClickProc ¬ TEditDocumentPrivate.PreReset]; preStore: REF Menus.ClickProc = NEW[Menus.ClickProc ¬ TEditDocumentPrivate.PreStore]; CreateTiogaMenu: PROC ~ { Append: PROC [line: Menus.MenuLine, name: LONG STRING, proc: Menus.ClickProc, guard: REF Menus.ClickProc ¬ NIL] ~ { nameRope: ROPE ~ ConvertUnsafe.ToRope[name]; entry: Menus.MenuEntry ~ Menus.CreateEntry[name: nameRope, proc: proc, guarded: (guard#NIL), documentation: guard]; Menus.AppendMenuEntry[menu: tiogaMenu, entry: entry, line: line]; }; tiogaMenu ¬ Menus.CreateMenu[]; Append[0, "Clear", TEditDocumentPrivate.Clear]; Append[0, "Reset", TEditDocumentPrivate.Reset, preReset]; Append[0, "Get", TEditDocumentPrivate.Get]; Append[0, "GetImpl", TEditDocumentPrivate.GetImpl]; Append[0, "PrevFile", TEditDocumentPrivate.PreviousFile]; Append[0, "Store", TEditDocumentPrivate.Store, preStore]; Append[0, "Save", TEditDocumentPrivate.Save]; Append[0, "Time", TEditDocumentPrivate.Time]; Append[0, "Split", TEditDocumentPrivate.Split]; Append[0, "Places", FindMenu]; Append[0, "Levels", LevelMenu]; Append[1, "Find", TEditDocumentPrivate.Find]; Append[1, "Word", TEditDocumentPrivate.FindWord]; Append[1, "Def", TEditDocumentPrivate.FindDef]; Append[1, "Position", TEditDocumentPrivate.Position]; Append[1, "Normalize", TEditDocumentPrivate.Normalize]; Append[1, "PrevPlace", TEditDocumentPrivate.JumpToPrevious]; Append[1, "Reselect", TEditDocumentPrivate.Reselect]; Append[1, "StyleKind", StyleKindClick]; Append[2, "FirstLevelOnly", TEditDocumentPrivate.FirstLevelOnly]; Append[2, "MoreLevels", TEditDocumentPrivate.MoreLevels]; Append[2, "FewerLevels", TEditDocumentPrivate.FewerLevels]; Append[2, "AllLevels", TEditDocumentPrivate.AllLevels]; findMenu ¬ Menus.GetLine[tiogaMenu, 1]; levelMenu ¬ Menus.GetLine[tiogaMenu, 2]; Menus.SetLine[tiogaMenu, 1, NIL]; Menus.SetLine[tiogaMenu, 2, NIL]; Menus.ChangeNumberOfLines[tiogaMenu, 1]; -- only show the top level to start }; 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]; CreateTiogaMenu[]; ReloadTipTable[]; ReloadReadonlyTipTable[]; ViewerOps.RegisterViewerClass[$Text, tiogaClass]; [] ¬ Buttons.Create[info: [name: "New"], proc: TEditDocumentPrivate.NewButton, fork: FALSE]; [] ¬ Buttons.Create[info: [name: "Open"], proc: TEditDocumentPrivate.OpenButton]; END. Ü TEditDocumentsImpl.mesa Copyright Ó 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) August 19, 1985 4:07:47 pm PDT Bier, January 10, 1989 11:50:21 am PST Michael Plass, March 23, 1989 3:09:02 pm PST Willie-s, February 13, 1991 3:32 pm PST Doug Wyatt, March 26, 1993 4:06 pm PST make sure another link didn't already init and if so, copy that data somebody else already did the init old heuristic: IF HasBrackets[self.file] THEN ViewerTools.InhibitUserEdits[self] ELSE lock so no edits while saving We want to kill off selections so we don't lock up the world while saving. Save the file. defaultKeep: INT ~ UserProfile.Number["Tioga.defaultKeep", 2]; Unlock the document if it was locked to begin with. 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. [self: ViewerClasses.Viewer, data: REF ANY, finalise: BOOL _ TRUE, op: ATOM _ NIL] reuse rather than reallocate Should really have a write lock on the document for this. change for Tioga2. Need a repaint no iconic selections please [self: ViewerClasses.Viewer, op: ATOM _ NIL] RETURNS [data: REF ANY] this also fixes up the Root => Viewer mapping if necessary Need to pass self.file as variable because it will be set to NIL see if already showing the submenu Menus.ChangeNumberOfLines[menu, newLast-1]; add it. do insertion sort to get it in the right place higher priority means goes above in series of submenus looks at rope for first item to identify the subMenu. put it here Would really like to enumerate viewer classes here . . . try the default by itself [self: ViewerClasses.Viewer] RETURNS [adjusted: BOOL _ FALSE] tipTable set below Menu creation DKW: this crock reduces the number of rope literals in this module, to avoid breaking the C compiler's optimizer. Change name to a ROPE if this is ever fixed. First line: Places menu: StyleKind in the places menu is flakey, but there's room Levels menu: Stuff for StyleKind button (should live in TEditDocuments3Impl, but I didn't want to change or add interfaces.  MFP) don't fork since will change the selection; want to capture following typein ÊÞ•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ Ïeœa™lK™2K™&Kšœ,™,K™'K™&K™—šÏk ˜ Kšœžœ ˜Kšœžœ˜Kšœžœ ˜Kšœžœ ˜Kšžœžœ ˜Kšœžœ•˜ Kšœžœ˜+Kšœ žœ˜#Kšžœ˜Kšœ žœ˜$Kšœžœ˜#Kšœžœ4žœ ˜LKšœ žœ˜'Kšœžœr˜…Kšœžœ”˜®Kšœ žœ2˜BKšœžœ˜$Kšœ žœ0˜@Kšœ žœ'˜9Kšœ žœ ˜Kšœ žœ˜&Kšœžœ˜+Kšœžœ+˜?Kšœ žœ˜ Kšœ žœ!˜/Kšœžœ ˜Kšœ žœ ˜Kšœ žœg˜uKšœ˜Kšœ žœ˜Kšœ žœ ˜Kšœ žœ ˜Kšœžœ(˜5Kšœ žœ˜&Kšœžœs˜†Kšœžœ ˜ Kšœ žœ˜,Kšœ žœƒ˜’Kšœ žœG˜X—K˜šÏnœžœž˜!Kšžœ3žœ#žœÌ˜®Kšžœ@˜GKšžœ˜Kšœž˜K˜Kšžœžœžœ˜K˜Kšœ žœ˜#K˜šœžœžœ˜Kšœžœ˜Kš œžœžœžœžœžœžœ˜FK˜—Kšœžœ žœ ˜"K˜šœžœžœžœÏc/˜UK˜—šŸœžœ˜4Kšœžœžœ ˜Kšœ žœ˜K˜K˜K˜—š Ÿœžœžœ žœžœ˜NKšžœ"˜(Kšœ˜K˜—š Ÿ œžœžœ$žœžœ˜JKšœ(žœ˜FK˜—K˜š Ÿœžœžœ$žœžœžœ˜fKšœ'žœ ˜9Kšœžœžœ˜šžœ ž˜šžœ˜Kš žœžœžœžœ  M˜~Kšœ˜—šžœ˜šžœžœž˜K˜>Kšžœžœžœžœ*˜D—Kšœ˜——K˜'šžœ ž˜šžœ˜Kšœ*™*Kšœ™Kšœ"žœ˜&šžœ-žœž˜?Kšžœžœžœžœ˜/Kšžœ˜—šžœ ž˜Kšžœžœ˜šžœ˜Kšœ"™"Kšœ,žœ˜CK˜K˜——K˜—šžœ˜šžœžœž˜Kšœ:žœ˜AKšœ2˜2Kšœžœ1žœ˜DKšœžœžœCžœ˜Ušžœ˜ šžœž˜ Kšžœžœ˜Kšžœ  œ2˜D—Kšœ˜——Kšœ˜——šžœžœ˜šžœ˜šžœ ˜Kšœžœ˜KšœMžœ˜SK˜Kšžœ˜ Kšœ˜—šœ˜KšœJžœ˜PKšœ žœ˜'K˜Kšžœ˜ K˜—K˜—Kšœ žœžœžœ˜Kšœžœ žœ˜0Kšžœ žœžœ-žœ˜Išžœžœ ˜Kš žœžœ žœžœ žœ˜EKšžœ3žœ˜I—šžœ žœžœ˜Kšœ žœžœ žœ ˜,K˜ƒKšœ žœ˜(K˜—Kšœ˜—Kšžœ žœžœ-žœ˜FKšžœ2žœžœ˜OK˜K˜:K˜2KšœU™UK˜"K˜ K˜—K˜šŸœžœžœ*žœ˜uKšœžœ%˜.šœ 7˜9Kšœ žœžœ0˜CKšœ%žœ'˜OKšœ,˜,Kšœ˜K˜—Kš žœžœ žœžœ$žœ˜YK˜K˜K˜—Kšœžœ˜šŸœžœ˜4Kšœ'žœ ˜9K˜Kšœžœ ˜Kšœžœžœ˜K˜šžœžœ˜Kšžœž œ,˜@šœ˜Kšœ˜Kšœ)˜)K˜Kšœ˜—K˜—K˜šžœž˜Kšžœ2˜7—K˜šžœ˜ šžœ˜Kšœ™K˜˜*KšœJ™J—Kšœ, '˜SK˜Kšœ  5˜BK˜6K˜—Kšžœ˜—K˜Kšœ™˜šžœ˜Kšžœ.žœ˜Kšœ žœžœžœ˜IKšœ žœžœ,˜?Kšœ žœžœ˜,Kšœ žœžœ6˜Išžœ(ž˜-K˜K˜šžœž˜Kšžœ žœ˜Kšžœ˜—Kšžœ˜—K˜—K™Kšœ3™3Kšžœžœ˜'Kšžœžœžœžœ)˜GK˜—K˜K˜š Ÿœžœžœ!žœžœžœ˜VKšœ®™®Kšžœžœ˜K˜—K˜Kšœ3žœ˜8•StartOfExpansionV -- [self: ViewerClasses.Viewer, data: REF ANY, finalise: BOOL _ TRUE, op: ATOM _ NIL]šŸœ˜+KšÐckR™Ršžœžœž˜šžœ žœž˜šœ)˜)šžœž˜šžœ:˜=K˜#Kšœ žœ˜Kš žœ žœžœžœ ˜3K˜*K˜*šžœž˜šžœ˜K˜&K˜"Kšœžœ˜Kš œžœžœžœžœžœ ˜CKš œžœžœžœžœžœ˜-K˜Bš ÐkyÏy¢£¢£ ¢£ ¢˜/š¢£˜Kš£™š Ðny £¢£¢£ ¢£¢£¢£˜=Kš¢£¢£¢£¢£¢£¢£¢£˜’Kš¢£¢£˜Kš£˜—Kš£˜Kš£M™MKš£˜Kš£B˜BKš£&˜&Kš£¢£˜1Kš¢£¢£˜>Kš£+¢£¢£˜9Kš£˜—Kš¢˜˜Kšœ˜Kšœžœ˜šžœžœ˜ K˜*K˜,K˜—K˜Kšœ0žœ˜5šžœžœ˜ Kšžœžœžœ.˜@Kšžœ žœžœ0˜CKšœ˜K˜—K˜——K˜—šœ˜Kšœ"žœ˜/K˜K˜>K˜—šœ˜Kšœžœ˜*K˜K˜K˜—Kšžœ˜—K˜K˜:K˜1Kšœžœ ˜,K˜ šžœ žœ˜Kšœ™Kšœ$žœžœ˜IK˜—K˜—šœ ˜ š žœžœ žœžœžœ˜0Kšœ™K˜7Kšœžœ˜'Kšœžœ˜K˜=šžœž˜ šžœ˜K˜=K˜;KšœžœAžœ˜Ršžœ žœ˜Kšžœ   ˜DKšžœ! ˜<—K˜—Kšžœ,˜0—KšœQžœ˜W˜$Kšœ!žœ!žœ˜K—K˜ Kšœžœ žœžœ˜4K˜K˜Kšœžœ žœžœ˜4Kšœžœ˜K˜K˜(K˜Kšœ$žœ˜+K˜——šœ˜K˜=K˜Kšœžœ˜K˜ K˜—šœ˜K˜=K˜Kšœžœ˜K˜ K˜—Kšžœ˜—K˜—Kšžœ˜——K˜—K˜–H -- [self: ViewerClasses.Viewer, op: ATOM _ NIL] RETURNS [data: REF ANY]šŸœ˜+Kš¡D™DKšœžœ˜ šžœ žœž˜šœ'˜'šžœž˜Kšžœ.˜1šœ˜K˜.KšœžœB˜LK˜—šœ˜Kšœ žœžœ˜šŸ œžœ@˜Pšžœžœžœžœ˜8Kšœ žœžœ#˜3š Ÿ œžœ*žœžœžœ˜VKšœ žœ8˜HKš œžœžœžœžœ ˜BKšžœžœ˜K˜—K˜AK˜—K˜—K˜.Kšžœžœžœ ˜Kšœ ˜ K˜—šœ˜Kšœžœ˜5šŸœžœ@˜NKšœEžœ˜K˜ Kšœ<žœ˜B—K˜—K˜,Kšœ ˜ K˜—Kšžœ˜—Kšžœ˜——K˜—K˜šŸœ˜3šœ!˜!Kšœ:™:—Kš žœ žœžœžœ ,˜LKšžœžœ&žœ˜WK˜K˜—šŸœžœ)žœ+˜qKšœ@™@K˜Kš žœžœžœžœ *˜DK˜.K˜Kš žœžœžœžœž˜?K˜6Kšžœ˜K˜ K˜K˜—šŸ œžœ=˜MK˜Kšœžœžœ˜K˜8K˜$šžœžœž˜)Kšœ"™"šžœ6žœ ˜Sšžœžœž˜)K˜1Kšžœ˜—Kšœ+™+K˜Kšœžœžœ˜K˜—Kšžœ˜—šžœžœžœ˜Kšœ7™7š Ÿ œžœžœžœžœ  œ˜RšŸœžœžœžœ˜9Kšœ6™6Kšœ5™5šžœžœžœž˜K˜ K˜*Kšžœ !œ˜2—K˜—Kšžœ˜%K˜—Kšœžœ žœ˜>K˜šžœžœž˜)šžœ-žœ˜5Kšœ ™ šžœž œžœž˜3Kšœ2žœ˜:—K˜ Kšœžœžœ˜K˜—Kšžœ˜—Kšžœžœžœ'˜8K˜—K˜0K˜—K˜Kšœ žœžœ˜MKšœ žœžœ˜KKšœ ž œ˜šŸ œžœžœžœ2žœžœžœžœžœžœ˜”Kšžœžœžœ˜Kšœ žœžœ˜4šžœžœ˜#šžœ žœ˜Kš žœžœžœžœžœ˜3K˜ K˜—Kšžœ žœžœ žœ˜+K˜Kšœ '˜6Kšžœ žœ!˜2K˜—K˜Kšžœžœ˜K˜—K˜šŸœžœžœžœ+˜DKšžœžœžœ˜Kšžœ(žœžœ˜5Kšžœžœž œ ˜7K˜—K˜šœ žœžœžœ ˜2K˜—š Ÿ œžœžœžœžœ˜/Kšžœ%˜+K˜—K˜šŸœžœžœ˜K˜IKšžœ žœžœ)˜=K˜—K˜šŸœžœžœ˜'K˜\Kšžœ žœžœ˜*K˜—K˜K˜šœ žœ ˜K˜—šŸœžœ˜4šŸœžœ˜š œ žœžœžœžœ˜Cšžœžœž˜˜)Kšžœžœ˜0K˜—Kšžœžœ˜—K˜*K˜—KšœU˜UK˜&™8Kšžœžœ˜BKšžœžœ˜,Kšžœžœ˜2Kšžœžœžœ#žœ#˜d—K˜—K˜:K˜—K˜š Ÿ œžœžœ.žœžœ˜jKšœžœžœ˜šŸœžœ žœ˜!Kšœžœžœ˜K˜ Kšœ žœ˜Kšžœžœžœžœ˜šžœžœžœ˜'K˜+Kšžœ˜K˜—˜$˜šžœ ˜Kšœžœ˜ K˜5Kšž˜K˜—˜Kšœžœ˜ K˜;Kšž˜K˜———Kš žœžœžœžœžœ˜DKšžœžœžœžœ˜*šžœžœžœ˜*Kšœžœ˜ KšœLžœ˜RK˜—K˜—K˜3šžœžœžœžœ˜Kšœ™Kšœžœ-˜6K˜—šžœžœ žœžœ žœžœžœ˜6Kšœžœ"˜5K˜—K˜K˜—–A -- [self: ViewerClasses.Viewer] RETURNS [adjusted: BOOL _ FALSE]šœžœžœ˜K˜—šŸ œ˜)Kš¡=™=K–w[viewer: ViewerClasses.Viewer, hint: ViewerOps.PaintHint, clearClient: BOOL _ TRUE, whatChanged: REF ANY _ NIL]šžœžœžœ žœžœžœžœžœ˜Zšžœ žœž˜˜)Kšœžœ ˜Kšœ/˜/šžœ žœž˜šžœžœžœž˜$šžœž˜šžœžœž˜/Kšœžœ˜——Kšžœ˜——K˜—Kšžœ˜—Kšžœžœ˜K˜K˜—šœ(žœ!˜LK˜'Kšœ  G˜RKšœ  ˜'K˜%K˜#K˜K˜K˜K˜K˜K˜K˜*Kšœ™K˜—K˜K˜—™ K˜Kšœ žœžœ˜#Kšœžœžœ˜