<> <> <> <> <> <<>> 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], 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 = { <<[self: ViewerClasses.Viewer, data: REF ANY, finalise: BOOL _ TRUE, op: ATOM _ NIL]>> IF NOT self.destroyed THEN WITH self.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { SELECT op FROM NIL, $TiogaContents, $TiogaDocument, $KeepRootStyleProps => { hint: ViewerOps.PaintHint _ client; rope: Rope.ROPE; IF self.link#NIL THEN ERROR; -- not yet implemented TEditDocumentPrivate.KillSelections[self]; [] _ SpinAndLock[tdd, "SetTEditDocument"]; SELECT op FROM NIL, $KeepRootStyleProps => { root: TextNode.RefTextNode = tdd.text; node: TextNode.RefTextNode = root; formatName: ATOM = root.formatName; child: TextNode.RefTextNode = IF node=NIL THEN NIL ELSE node.child; rope _ IF data=NIL THEN "" ELSE NARROW[data]; IF child#NIL AND child.last AND child.child=NIL THEN { <> 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 { < caret at end>> start _ TextNode.LocNumber[ <> at: TextNode.LastLocWithin[tdd.text], skipCommentNodes: TRUE]; length _ 0 } ELSE { < entire contents>> 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 = { <<[self: ViewerClasses.Viewer, op: ATOM _ NIL] RETURNS [data: REF ANY]>> data _ NIL; WITH self.data SELECT FROM tdd: TEditDocument.TEditDocumentData => SELECT op FROM NIL => { node: TextNode.RefTextNode = tdd.text.child; IF node#NIL AND node.last AND node.child=NIL THEN data _ node.rope <> 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]; < 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] = { <> 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 = { <<[self: ViewerClasses.Viewer] RETURNS [adjusted: BOOL _ FALSE]>> IF self = NIL OR self.iconic OR self.destroyed OR self.paintingWedged THEN RETURN [FALSE]; WITH self.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { ch: INTEGER _ self.ch; lines: TEditDocument.LineTable _ tdd.lineTable; IF lines # NIL THEN FOR i: INT IN [0..lines.lastLine] DO IF lines[i].valid THEN IF alwaysInvalid OR lines[i].yOffset >= ch THEN lines[i].valid _ FALSE; ENDLOOP; }; ENDCASE; RETURN [TRUE]; }; tiogaClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: TEditImpl.PaintTEditDocument, bltV: top, -- use blt to copy screen contents to top of viewer when height changes bltH: none, -- but not if width changes notify: TEditImpl.TEditNotifyProc, modify: TEditSelection.InputModify, adjust: LocalAdjust, init: InitTEditDocument, set: SetTEditDocument, get: GetTEditDocument, save: SaveTEditDocument, destroy: DestroyTEditDocument, scroll: TEditScrolling.ScrollTEditDocument <> ]]; 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.