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 { 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 { 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 => { IF tdd=NIL THEN tdd _ AllocateDataForNewViewer[self]; [] _ SpinAndLock[tdd, "InitViewerDoc"]; tdd.text _ PutGet.FromRope["*ERROR*"]; InitLineTable[]; }; TEditDocument.RecordViewerForRoot[self,tdd.text]; ViewerTools.EnableUserEdits[self]; Unlock[tdd]; }; AllocateDataForNewViewer: PROC [self: ViewerClasses.Viewer] RETURNS [tdd: TEditDocument.TEditDocumentData] = { tdd _ NEW[TEditDocument.TEditDocumentDataRec]; 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: 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 FS.Error => { errorMessage _ error.explanation; CONTINUE }; defaultKeep: INT _ UserProfile.Number["Tioga.defaultKeep", 2]; file: FS.OpenFile ~ FS.Create[name: RemoveVersionNumber[name], setPages: FALSE, setKeep: FALSE, keep: defaultKeep]; newFile: ROPE ~ FS.GetName[file].fullFName; newName: ROPE ~ RemoveVersionNumber[newFile]; [] _ PutGet.ToFileC[file, root]; FOR v: ViewerClasses.Viewer _ self, v.link DO v.name _ newName; v.file _ newFile; SELECT v.link FROM NIL, self => EXIT; ENDCASE; ENDLOOP; }; 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] = { 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; 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 { 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 _ 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 { ViewerForkers.ForkPaint[self, hint, TRUE, TEditTouchup.fullUpdate, TRUE]; }; }; $SelPos => IF ~self.iconic AND ~ 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 { IF tdd.tsInfo#NIL THEN { start _ TextNode.LocNumber[ at: TextNode.LastLocWithin[tdd.text], skipCommentNodes: TRUE]; length _ 0 } ELSE { start _ 0; length _ TextNode.LocNumber[ 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 = { 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 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]; 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 ~found THEN { GoesBefore: PROC [m1, m2: Menus.MenuEntry] RETURNS [BOOL] = { 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 ~found THEN Menus.SetLine[menu, newLast, subMenu]; }; ViewerBLT.ChangeNumberOfLines[viewer, newLines]; }; LevelMenu: PUBLIC Menus.MenuProc = { ChangeMenu[NARROW[parent], levelMenu] }; FindMenu: PUBLIC Menus.MenuProc = { ChangeMenu[NARROW[parent], findMenu] }; unlocked: CONDITION; SpinAndLock: PUBLIC ENTRY PROC [tdd: TEditDocument.TEditDocumentData, who: Rope.ROPE, interrupt, defer: BOOL _ FALSE] RETURNS [ok: BOOL] = TRUSTED { ENABLE UNWIND => NULL; myProcess: PROCESS = LOOPHOLE[Process.GetCurrent[]]; IF myProcess#tdd.lockProcess THEN { IF interrupt THEN { IF defer AND tdd.interrupt > 0 THEN RETURN [FALSE]; tdd.interrupt _ tdd.interrupt+1 }; WHILE tdd.lock>0 DO WAIT unlocked; ENDLOOP; tdd.lockProcess _ myProcess; tdd.who _ who; -- save the first guy who got the lock IF interrupt THEN tdd.interrupt _ tdd.interrupt-1; }; tdd.lock _ tdd.lock+1; RETURN [TRUE]; }; Unlock: PUBLIC ENTRY PROC [tdd: TEditDocument.TEditDocumentData] = TRUSTED { ENABLE UNWIND => NULL; IF tdd.lockProcess # Process.GetCurrent[] THEN ERROR; IF (tdd.lock _ tdd.lock-1) = 0 THEN BROADCAST unlocked }; ReloadTipTable: PUBLIC PROC = { newTIP: TIPUser.TIPTable _ ReloadTable[tiogaTIP, "TiogaTIP", "[]<>Tioga.tip"]; IF newTIP # NIL THEN tiogaClass.tipTable _ tiogaTIP _ newTIP; }; ReloadReadonlyTipTable: PUBLIC PROC = { newTIP: TIPUser.TIPTable _ ReloadTable[readonlyTIP, "ReadonlyTiogaTIP", "[]<>ReadonlyTioga.tip"]; IF newTIP # NIL THEN readonlyTIP _ newTIP; }; readonlyTIP: TIPUser.TIPTable; tiogaTIP: PUBLIC TIPUser.TIPTable; ChangeTipTables: PROC [newTIP, oldTIP: TIPUser.TIPTable] = { WithViewerTreeLocked: PROC ~ { changeTip: PROC [v: ViewerClasses.Viewer] RETURNS [BOOL _ TRUE] = { WITH v.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { IF v.tipTable = oldTIP THEN v.tipTable _ newTIP; }; ENDCASE => NULL; ViewerOps.EnumerateChildren[v, changeTip]; }; typescriptClass: ViewerClasses.ViewerClass ~ ViewerOps.FetchViewerClass[$Typescript]; ViewerOps.EnumerateViewers[changeTip]; IF tiogaClass.tipTable = oldTIP THEN tiogaClass.tipTable _ newTIP; IF tiogaTIP = oldTIP THEN tiogaTIP _ newTIP; IF readonlyTIP = oldTIP THEN readonlyTIP _ newTIP; IF typescriptClass#NIL AND typescriptClass.tipTable = oldTIP THEN typescriptClass.tipTable _ newTIP; }; ViewerLocks.CallUnderViewerTreeLock[WithViewerTreeLocked]; }; ReloadTable: PUBLIC PROC [oldTIP: TIPUser.TIPTable, profileKey, default: Rope.ROPE] RETURNS [newTIP: TIPUser.TIPTable] = { ok: BOOL _ TRUE; AddTable: PROC [r: Rope.ROPE] = { bad: BOOL _ FALSE; t: TIPUser.TIPTable; msg: Rope.ROPE; IF ~ok THEN RETURN; IF Rope.Equal[r,"default",FALSE] THEN { TEditProfile.DoList["", AddTable, default]; RETURN }; t _ TIPUser.InstantiateNewTIPTable[r ! FS.Error => { bad _ TRUE; msg _ Rope.Concat["Cannot read TIP table file: ", r]; CONTINUE }; TIPUser.InvalidTable => { bad _ TRUE; msg _ Rope.Concat["Error(s) saved on TIP.Errors for: ", r]; CONTINUE }]; IF bad THEN { ok _ FALSE; MessageWindow.Append[msg, TRUE]; RETURN }; IF newTIP=NIL THEN { newTIP _ t; RETURN }; FOR x: TIPUser.TIPTable _ newTIP, x.link UNTIL x=NIL DO FOR y: TIPUser.TIPTable _ t, y.link UNTIL y=NIL DO IF x # y THEN LOOP; ok _ FALSE; MessageWindow.Append[Rope.Concat["Loop in TIP table layers caused by ", r], TRUE]; RETURN; ENDLOOP; ENDLOOP; FOR x: TIPUser.TIPTable _ newTIP, x.link UNTIL x.link=NIL DO REPEAT FINISHED => { newTIP.mouseTicks _ MIN[t.mouseTicks, newTIP.mouseTicks]; x.opaque _ FALSE; x.link _ t; }; ENDLOOP; }; TEditProfile.DoList[profileKey, AddTable, default]; IF ~ok AND oldTIP=NIL THEN { ok _ TRUE; TEditProfile.DoList["", AddTable, default] }; IF oldTIP # NIL AND newTIP # NIL THEN TRUSTED { Process.Detach[FORK ChangeTipTables[newTIP, oldTIP]]; }; }; alwaysInvalid: BOOL _ FALSE; LocalAdjust: ViewerClasses.AdjustProc = { 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 ]]; tiogaMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; findMenu, levelMenu, lineMenu: PUBLIC Menus.MenuEntry; preReset: REF Menus.ClickProc = NEW[Menus.ClickProc _ TEditDocumentPrivate.PreReset]; preStore: REF Menus.ClickProc = NEW[Menus.ClickProc _ TEditDocumentPrivate.PreStore]; 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]; 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]]; Menus.InsertMenuEntry[tiogaMenu, Menus.CreateEntry["StyleKind", StyleKindClick], 1]; 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]; 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]; [] _ Buttons.Create[info: [name: "Open"], proc: TEditDocumentPrivate.OpenButton]; END. äTEditDocumentsImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Michael Plass, July 19, 1985 2:18:22 pm PDT Russ Atkinson (RRA) August 19, 1985 4:07:47 pm PDT Doug Wyatt, July 15, 1986 4:43:39 pm PDT make sure another link didn't already init and if so, copy that data somebody else already did the init bad data old heuristic: IF HasBrackets[self.file] THEN ViewerTools.InhibitUserEdits[self] ELSE for now, just hack in a conservative size line table 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. 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 for typescripts, NIL => caret at end includes 1 for root, so must subtract 1 for other text viewers, NIL => entire contents includes 1 for root, so must subtract 1 [self: ViewerClasses.Viewer, op: ATOM _ NIL] RETURNS [data: REF ANY] simple case of only one node 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 preSave: REF Menus.ClickProc = NEW[Menus.ClickProc _ PreSave]; preClear: REF Menus.ClickProc = NEW[Menus.ClickProc _ PreClear]; 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) Menu creation build the places menu StyleKind in the places menu is flakey, but there's room build the levels menu don't fork since will change the selection; want to capture following typein ʳ˜codešœ™Kšœ Ïmœ7™BKšœ+™+K™2Kšœ(™(—K™šÏk ˜ Kšœžœ ˜Kšœžœ˜Kšœžœ ˜Kšžœžœ>˜FKšœžœ¦˜±Kšœžœ˜+Kšœ žœ˜-Kšœžœ˜#KšœžœG˜SKšœžœ4žœ˜TKšœ žœ˜'Kšœžœr˜…Kšœžœ”˜®Kšœ žœ'˜6Kšœ žœ2˜BKšœžœ˜$Kšœ žœ0˜@Kšœ žœ ˜Kšœ žœ˜&Kšœžœ˜+Kšœžœ+˜?Kšœ žœ˜ Kšœ žœ˜'Kšœ žœ ˜Kšœ žœz˜ˆKšœ žœÏc˜+Kšœžœ2˜?Kšœ žœ ˜Kšœ žœ˜&Kšœžœy˜ŒKšœžœ ˜ Kšœ žœ˜,Kšœ žœƒ˜’Kšœ žœG˜X—K˜šÐblœžœž˜!Kšžœ$žœÑ˜þKšžœ=˜DKšžœ˜Kšœž˜K˜Kšžœžœžœ˜KšœžœžœžœŸ/˜Ušœžœ˜4Kšœžœžœ ˜Kšœ žœ˜K˜K˜K˜—š Ïn œžœžœ$žœžœ˜JKšœ(žœ˜EK˜—K˜š ¡œžœžœ$žœžœžœ˜eKšœ'žœ ˜9K˜š¡ œžœ˜Kšžœ žœžœ˜5Kšœ žœ˜šžœžœžœ˜Kš žœ žœžœžœžœ žœ˜GK˜—šžœžœžœ˜Kšœ žœ˜'˜"Kšžœžœžœ˜.˜KšœJžœ˜PKšœ žœ˜'K˜Kšžœ˜ K˜—K˜—K˜—šžœ žœžœ˜K˜7K˜—K˜—K˜š¡ œžœ˜Kšžœ2žœžœ˜OK˜K˜:K˜—K˜šžœ žœžœ˜Kšœ*™*Kšœ™Kšœ"žœ˜&Kš žœžœžœžœ ŸM˜~K˜'šžœ-žœž˜?Kšžœžœžœ˜,Kšžœ˜—šžœ ž˜Kšžœ˜šžœ˜Kšœ"™"Kšœ,žœ˜CK˜K˜——K˜K˜—šžœžœžœžœ˜Kšžœžœžœ&˜5K˜'K˜K˜K˜—šžœžœžœž˜˜'K˜K˜'K˜K˜K˜—˜Kšžœžœžœ&˜5K˜'K˜K˜K˜—šœžœ˜Kšžœžœžœ&˜5K˜'K˜6K˜K˜—šœžœžœ˜Kšžœžœžœ&˜5K˜'K˜HK˜K˜—šžœ˜ Kšœ™Kšžœžœžœ&˜5K˜'K˜&K˜K˜——K˜1KšœU™UK˜"K˜ K˜—K˜š¡œžœžœ+˜nKšœžœ%˜.Kšœ4™4šœžœžœ2˜dKšœžœ˜&—Kš žœžœ žœžœ$žœ˜YK˜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šžœžœ.žœ˜BKšœ žœ.˜>Kš œžœ žœ4žœ žœ˜tKšœ žœžœ˜+Kšœ žœ ˜-K˜ šžœ(ž˜-K˜K˜šžœž˜Kšžœ žœ˜Kšžœ˜—Kšžœ˜—K˜—K™Kšœ3™3Kšžœžœ˜'Kšžœžœžœžœ)˜GK˜—K˜š ¡œžœžœžœžœ˜7Kšžœ3˜9K˜—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šœ™š ¡ œžœžœ žœžœžœ˜=Kšžœžœžœžœžœžœžœ˜’Kšžœžœ˜K˜—K˜KšœM™MK˜K˜BK˜&Kšœžœ˜1Kšžœžœ˜>Kšœ+žœžœ˜9K˜—šžœ˜Kšœžœ˜šžœžœ˜ K˜*K˜,K˜—K˜K˜9šžœžœ˜ Kšžœžœžœ.˜@Kšžœ žœžœ0˜CK˜!K˜—K˜——K˜—šœ˜Kšœ"žœ˜/K˜3K˜K˜!K˜—šœ˜Kšœžœ˜*K˜K˜K˜—Kšžœ˜—K˜K˜:K˜1KšœžœŸ˜,K˜ šžœ žœ˜Kšœ™Kšœ$žœžœ˜IK˜—K˜—šœ ˜ šžœžœžœ˜+Kšœ™K˜7Kšœžœ˜'Kšœžœ˜K˜=šžœžœžœ˜šžœ žœžœ˜Kšœžœ™$šœ˜Kšœ'™'Kšœ8žœ˜>—K˜ K˜—šžœ˜Kšœžœ™.K˜ šœ˜Kšœ'™'Kšœ8žœ˜?—K˜—K˜—šžœ˜Kšœ žœ8˜GK˜Kšžœ žœŸ$˜IK˜K˜—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˜,š žœžœžœ žœ ž˜,šžœ˜Kšœ™—Kšžœ)˜-—K˜—šœ˜Kšœžœ˜ Kšœžœ˜K˜5K˜MK˜?Kšœžœ8˜BK˜—šœ˜Kšœ žœŸ˜0š¡ œžœ@˜Pš¡ œžœ;žœžœ˜bšœžœž˜Kšžœ#˜'Kšžœ:˜>—Kšžœžœ˜K˜—Kšžœžœžœžœ˜7K˜AKšžœžœžœ ˜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š¡ œžœžœžœ˜=š¡œžœžœžœ˜9Kšœ6™6Kšœ5™5šžœžœžœž˜K˜ K˜*KšžœŸ!œ˜2—K˜—Kšžœ˜%K˜—Kšœžœ žœ˜>K˜šžœžœž˜)šžœ-žœ˜5Kšœ ™ šžœž œžœž˜3Kšœ2žœ˜:—K˜ Kšœžœžœ˜K˜—Kšžœ˜—Kšžœžœ'˜5K˜—K˜0K˜—K˜Kšœ žœžœ˜MKšœ žœžœ˜KKšœ ž œ˜š¡ œžœžœžœ2žœžœžœžœžœžœ˜”Kšžœžœžœ˜Kšœ žœžœ˜4šžœžœ˜#šžœ žœ˜Kš žœžœžœžœžœ˜3K˜ K˜—Kšžœ žœžœ žœ˜+K˜KšœŸ'˜6Kšžœ žœ!˜2K˜—K˜Kšžœžœ˜K˜—K˜š ¡œžœžœžœ*žœ˜LKšžœžœžœ˜Kšžœ(žœžœ˜5Kšžœžœž œ ˜7K˜—K˜š¡œžœžœ˜K˜NKšžœ žœžœ)˜=K˜—K˜š¡œžœžœ˜'˜K˜F—Kšžœ žœžœ˜*K˜—K˜K˜Kšœ žœ˜"š¡œžœ'˜<š¡œžœ˜š œ žœžœžœžœ˜Cšžœžœž˜˜)Kšžœžœ˜0K˜—Kšžœžœ˜—K˜*K˜—KšœU˜UK˜&™8Kšžœžœ˜BKšžœžœ˜,Kšžœžœ˜2Kšžœžœžœ#žœ#˜d—K˜—K˜:K˜—K˜š ¡ œžœžœ6žœžœ˜zKšœžœžœ˜š¡œžœ žœ˜!Kšœžœžœ˜K˜Kšœ žœ˜Kšžœžœžœ˜šžœžœžœ˜'K˜+Kšžœ˜K˜—˜$˜šžœ ˜ Kšœžœ˜ K˜5Kšž˜K˜—˜Kšœžœ˜ K˜;Kšž˜K˜———Kš žœžœžœžœžœ˜DKšžœžœžœžœ˜*šžœ&žœžœž˜7šžœ!žœžœž˜2Kšžœžœžœ˜Kšœžœ˜ KšœLžœ˜RKšžœ˜Kšžœ˜—Kšžœ˜—šžœ&žœžœž˜<šžœžœ˜Kšœžœ"˜9Kšœ žœ˜K˜ K˜—Kšžœ˜—K˜—K˜3šžœžœžœžœ˜Kšœ™Kšœžœ-˜6K˜—š žœ žœžœ žœžœžœ˜/Kšœžœ"˜5K˜—K˜K˜—K–A -- [self: ViewerClasses.Viewer] RETURNS [adjusted: BOOL _ FALSE]šœžœžœ˜šœ)˜)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šœ žœ!˜2K˜Kšœžœ˜6Kšœ>™>Kšœ žœžœ2˜UKšœ@™@Kšœ žœžœ2˜UKšœN™NKšœ<™