DIRECTORY BasicTime USING [FromPupTime], Buttons USING [Button, Create], Containers USING [ChildXBound, ChildYBound, Container, Create], FS USING [Error, Open, OpenFile], GVBasics USING [Timestamp], GVPDefs, IO USING [Flush, PutF, PutFR, PutText, Reset, SP, STREAM, TAB], Labels USING [Create, Set], Loader USING [Error, Start, Instantiate, IRItem], ObjectDirDefs USING [ObjectType], PrincOps USING [ControlModule], Rope USING [Concat, Fetch, FromChar, Length], ViewerIO USING [CreateViewerStreams], ViewerOps USING [CreateViewer, MoveViewer, PaintViewer], ViewerTools USING [GetContents, GetSelectedViewer, GetSelection, MakeNewTextViewer, SelPos, SelPosRec, SetSelection]; GVPEditor: CEDAR MONITOR LOCKS lock IMPORTS BasicTime, Buttons, Containers, FS, GVPDefs, IO, Labels, Loader, Rope, ViewerIO, ViewerOps, ViewerTools EXPORTS GVPDefs SHARES ObjectDirDefs = BEGIN OPEN GVPDefs; index: CARDINAL; bold: ROPE = " fb"; fixed: ROPE = " f"; EditDisplayMode: TYPE = {wordMode, charMode, stringMode}; EditModeSeq: TYPE = RECORD[SEQUENCE length: CARDINAL OF EditDisplayMode]; editModeVec: REF EditModeSeq _ NIL; values: REF WordSeq _ NIL; thisPageChanged, pageHere: BOOLEAN _ FALSE; pageWords: REF PageWordVec _ NIL; pageBytes: REF PageByteVec _ NIL; bufferLength, firstMarked: CARDINAL _ 0; editorButtons, editorText, valueInput, editMsg: Viewer; MakeEditorButtons: PUBLIC PROC[h: GVPRef] = BEGIN SBSize: CARDINAL = 8*char; col5a: CARDINAL = col4+10*char; col6a: CARDINAL = col5a+10*char; col7a: CARDINAL = col6a+10*char; editorText _ ViewerOps.CreateViewer[flavor: $Typescript, info: [parent: h.root, border: TRUE, iconic: FALSE, wy: offScreen]]; h.editorStream _ ViewerIO.CreateViewerStreams[viewer: editorText, name: NIL].out; editorButtons _ Containers.Create[[name: NIL, parent: h.root, wx: 0, wy: offScreen, wh: 4*rowSize, border: FALSE, scrollable: FALSE]]; valueInput _ ViewerTools.MakeNewTextViewer[[parent: editorButtons, wx: col5, wy: row1+2, ww: 40*char, wh: rowHeight, scrollable: FALSE, border: FALSE]]; [] _ Buttons.Create[info: [name: "BROWSER", wx: col1, wy: row3, wh: rowSize+rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: BrowserButton]; [] _ Buttons.Create[info: [name: "SERVER", wx: col1, wy: row1, wh: rowSize+rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: ServerButton]; [] _ Buttons.Create[info: [name: "reset page", wx: col2, wy: row1, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: ResetPageButton]; [] _ Buttons.Create[info: [name: "get next", wx: col2, wy: row2, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: GetNextButton]; [] _ Buttons.Create[info: [name: "get prev", wx: col2, wy: row3, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: GetPrevButton]; [] _ Buttons.Create[info: [name: "get page", wx: col2, wy: row4, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: GetPageButton]; [] _ Buttons.Create[info: [name: "put page", wx: col3, wy: row1, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: PutPageButton]; [] _ Buttons.Create[info: [name: "chars", wx: col3, wy: row2, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: CharsButton]; [] _ Buttons.Create[info: [name: "words", wx: col3, wy: row3, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: WordsButton]; [] _ Buttons.Create[info: [name: "show page", wx: col3, wy: row4, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: ShowPageButton]; [] _ Buttons.Create[info: [name: "Values >", wx: col4, wy: row1, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: NewValueButton]; [] _ Buttons.Create[info: [name: "replace", wx: col4, wy: row2, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: ReplaceButton]; [] _ Buttons.Create[info: [name: "RUN extern", wx: col4, wy: row3, wh: rowHeight, ww: buttonSize, parent: editorButtons], clientData: h, proc: ExternalButton]; [] _ Buttons.Create[info: [name: "Object", wx: col4, wy: row4, wh: rowHeight, ww: SBSize, parent: editorButtons], clientData: h, proc: ObjectButton]; [] _ Buttons.Create[info: [name: "RName", wx: col5a, wy: row4, wh: rowHeight, ww: SBSize, parent: editorButtons], clientData: h, proc: RNameButton]; [] _ Buttons.Create[info: [name: "Time", wx: col6a, wy: row4, wh: rowHeight, ww: SBSize, parent: editorButtons], clientData: h, proc: TimeStampButton]; [] _ Buttons.Create[info: [name: "Size", wx: col7a, wy: row4, wh: rowHeight, ww: SBSize, parent: editorButtons], clientData: h, proc: SizeButton]; editMsg _ Labels.Create[[parent: editorButtons, wx: col5, wy: row3, wh: rowHeight, border: FALSE, ww: 40*char]]; END; EditorButton: PUBLIC ENTRY ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; r: ROPE = CheckStructure[h]; IF r#NIL THEN {Flash[r]; RETURN}; ToFocus[]; ViewerOps.MoveViewer[viewer: h.currentButtons, x:0, y: offScreen, w: 0, h: 0, paint: FALSE]; ViewerOps.MoveViewer[viewer: editorButtons, x:0, y: 0, w: 0, h: 4*rowSize, paint: FALSE]; ViewerOps.MoveViewer[viewer: h.currentText, x:0, y: offScreen, w: 0, h: 0, paint: FALSE]; ViewerOps.MoveViewer[viewer: editorText, x:0, y: row6, w: 0, h: 0, paint: FALSE]; Containers.ChildXBound[h.root, editorText]; Containers.ChildXBound[h.root, editorButtons]; Containers.ChildYBound[h.root, editorText]; ViewerOps.PaintViewer[h.root, all]; h.currentButtons _ editorButtons; h.currentText _ editorText END; NewValueButton: ButtonProc = BEGIN -- put selection in input viewer ViewerTools.SetSelection[valueInput] END; SelectIndex: PROC RETURNS[first, last: CARDINAL] = BEGIN editSelPos: ViewerTools.SelPos; first _ 1; last _ 0; IF editorText#ViewerTools.GetSelectedViewer[] THEN {Flash["Wrong viewer"]; RETURN}; IF NOT PageOK[] THEN RETURN; editSelPos _ ViewerTools.GetSelection[editorText]; first _ editSelPos.start; last _ first + editSelPos.length; IF first<9 THEN first _ 9; IF last<9 THEN last _ 9; first _ ((first-9) / 63)*8 + ((first-9) MOD 63)/7; last _ ((last-9) / 63)*8 + ((last-9) MOD 63)/7; IF first>=pageWordSize THEN first _ pageWordSize-1; IF last>=pageWordSize THEN last _ pageWordSize-1; IF first=last THEN Set[IO.PutFR["Selected word is %d", [cardinal[first]]]] ELSE Set[IO.PutFR["Selected words are %d thru %d", [cardinal[first]], [cardinal[last]]]] END; IndexSelect: PROC[first, last: CARDINAL] = BEGIN selection: ViewerTools.SelPos _ NEW[ViewerTools.SelPosRec]; IF first>last THEN RETURN; IF NOT first IN [0..pageWordSize) THEN ERROR; IF NOT last IN [0..pageWordSize) THEN ERROR; selection.start _ 8 + ((first/8)*63) + (first MOD 8)*7; selection.length _ 8 + ((last/8)*63) + (last MOD 8)*7 + 6 - selection.start; ViewerTools.SetSelection[editorText, selection] END; PageOK: PROC RETURNS[ok: BOOLEAN] = BEGIN IF NOT pageHere THEN Flash["Get the page first"]; ok _ pageHere END; ResetPageButton: ENTRY ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; IF Failed[ ReadServerPage[h, h.pPage, pageBytes] ] THEN RETURN; NewPage[h] END; GetPageButton: ENTRY ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; IF Failed[ ReadHeapPage[h, h.pPage, pageBytes] ] THEN RETURN; NewPage[h] END; GetNextButton: ENTRY ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; IF NOT NextPage[h] THEN {Flash["Couldn't"]; RETURN}; IF Failed[ ReadHeapPage[h, h.pPage, pageBytes] ] THEN {[] _ PrevPage[h]; RETURN}; NewPage[h] END; GetPrevButton: ENTRY ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; IF NOT PrevPage[h] THEN {Flash["Couldn't"]; RETURN}; IF Failed[ ReadHeapPage[h, h.pPage, pageBytes] ] THEN {[] _ NextPage[h]; RETURN}; NewPage[h] END; PutPageButton: ENTRY ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; IF NOT PageOK[] THEN RETURN; IF NOT thisPageChanged THEN {Flash["Page not changed"]; RETURN}; IF Failed[ WriteLocalPage[h, h.pPage, pageBytes] ] THEN RETURN; h.logStream.PutF["\nEdit buffer copied to local page %d\n", [cardinal[h.pPage]]]; thisPageChanged _ FALSE; EditInfo[h] END; NewPage: PROC[h: GVPRef] = BEGIN FOR i: CARDINAL IN [0..pageWordSize) DO editModeVec[i] _ wordMode ENDLOOP; thisPageChanged _ FALSE; pageHere _ TRUE; firstMarked _ 0; bufferLength _ 0; ShowPageProc[h]; Set[PositionRope[h]] END; ShowPageButton: ENTRY ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; ShowPageProc[h]; Set[PositionRope[h]] END; ObjectButton: ButtonProc = TRUSTED BEGIN first, last: CARDINAL; objH: LONG POINTER TO ObjectHeader; [first, last] _ SelectIndex[]; IF first>last THEN RETURN; objH _ LOOPHOLE[pageWords, LONG POINTER]+first; last _ first+objHdrSize+objH.size; IF first>pageWordSize-objHdrSize THEN Flash["Sorry"] ELSE BEGIN r: ROPE _ IO.PutFR["Size=%d. Page=%bB, Index=%bB, Type=%g, (Fill=%bB).", [cardinal[objH.size]], [cardinal[objH.number.page]], [cardinal[objH.number.index]], [rope[ObjectRope[LOOPHOLE[objH.number.type, CARDINAL], FALSE]]], [cardinal[objH.number.fill]]]; r _ Rope.Concat[r, IO.PutFR[" Words %06bB %06bB %06bB", [cardinal[pageWords[first]]], [cardinal[pageWords[first+1]]], [cardinal[pageWords[first+2]]]]]; Set[r]; last _ IF last < pageWordSize THEN last-1 ELSE pageWordSize-1; IndexSelect[first, last] END END; TimeStampButton: ButtonProc = TRUSTED BEGIN first, last: CARDINAL; stamp: LONG POINTER TO GVBasics.Timestamp; [first, last] _ SelectIndex[]; IF first>last THEN RETURN; IF first+SIZE[GVBasics.Timestamp]>pageWordSize THEN {Flash["Sorry"]; RETURN}; stamp _ LOOPHOLE[pageWords, LONG POINTER]+first; Set[IO.PutFR["Net address %b#%b#, time %t", [cardinal[stamp.net]], [cardinal[stamp.host]], [time[BasicTime.FromPupTime[stamp.time]]]]]; IndexSelect[first, first+SIZE[GVBasics.Timestamp]-1] END; SizeButton: ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; first, last, pages: CARDINAL _ 0; [first, last] _ SelectIndex[]; IF first>last THEN RETURN; last _ first+pageWords[first]; pages _ last / pageWordSize; last _ IF last>=pageWordSize THEN pageWordSize-1 ELSE last; IF pages=0 THEN Set[IO.PutFR["Last word is word %d on this page", [cardinal[last]]]] ELSE Set["Word is beyond this page"]; IndexSelect[first, last] END; RNameButton: ButtonProc = BEGIN first, last: CARDINAL; len: CARDINAL; nWords: CARDINAL; r: ROPE; [first, last] _ SelectIndex[]; IF first>last THEN RETURN; IF first>pageWordSize-2 THEN {Flash["Sorry"]; RETURN}; len _ pageWords[first]; nWords _ (len+1)/2; r _ IO.PutFR["Len (%g, %g) \"", [cardinal[len]], [cardinal[pageWords[first+1]]]]; editModeVec[first] _ stringMode; editModeVec[first+1] _ wordMode; last _ first+2; FOR i: CARDINAL IN [0..nWords) DO c1, c2: CHAR; IF last >= pageWordSize THEN EXIT; c1 _ LOOPHOLE[pageWords[last]/256]; c2 _ LOOPHOLE[pageWords[last] MOD 256]; editModeVec[last] _ charMode; r _ Rope.Concat[r, Rope.FromChar[c1]]; len _ len - 1; last _ last + 1; IF len=0 THEN EXIT; r _ Rope.Concat[r, Rope.FromChar[c2]]; len _ len - 1 ENDLOOP; Set[Rope.Concat[r, "\""]]; IndexSelect[first, last-1] END; ReplaceButton: ENTRY ButtonProc = BEGIN h: GVPRef = NARROW[clientData]; n: CARDINAL = ParseValues[values]; first, last, j: CARDINAL _ 0; [first, last] _ SelectIndex[]; IF first>last THEN RETURN; IF n=lastCard OR n=0 THEN {Flash["Bad data for replace"]; RETURN}; j _ MIN[last-first+1, n]; h.logStream.PutF["\nEdits made on page %d in words [%d..%d] (old_new)\n", [cardinal[h.pPage]], [cardinal[first]], [cardinal[last]]]; FOR i: CARDINAL IN [0..j) DO IF pageWords[first+i]#values[i] THEN h.logStream.PutF["Word %d: %06b_%06b\n", [cardinal[first+i]], [cardinal[pageWords[first+i]]], [cardinal[values[i]]]]; pageWords[first+i] _ values[i]; ENDLOOP; h.logStream.PutText["\n"]; IF j=1 THEN Set[IO.PutFR["Updated word %d", [cardinal[first]]]] ELSE Set[IO.PutFR["Updated words %d thru %d (%d words)", [cardinal[first]], [cardinal[first+j-1]], [cardinal[j]]]]; thisPageChanged _ TRUE; ShowPageProc[h]; IndexSelect[first, first+j-1] END; CharsButton: ENTRY ButtonProc = BEGIN first, last: CARDINAL; [first, last] _ SelectIndex[]; IF first>last THEN RETURN; FOR i: CARDINAL IN [first..last] DO editModeVec[i] _ charMode ENDLOOP; IndexSelect[first, last] END; WordsButton: ENTRY ButtonProc = BEGIN first, last: CARDINAL; [first, last] _ SelectIndex[]; IF first>last THEN RETURN; FOR i: CARDINAL IN [first..last] DO editModeVec[i] _ wordMode ENDLOOP; IndexSelect[first, last] END; ExternalButton: ENTRY ButtonProc = TRUSTED BEGIN code: PrincOps.ControlModule; ubi: LIST OF Loader.IRItem; bcd: FS.OpenFile = FS.Open["GVPatchExternal.bcd" ! FS.Error => BEGIN Flash["Unable to open external file GVPatchExternal.bcd"]; GOTO fail END]; [code, ubi] _ Loader.Instantiate[bcd, 1 ! Loader.Error => BEGIN Flash[Rope.Concat["Error while loading bcd - ", message]]; GOTO fail END]; IF ubi # NIL THEN Flash["There are unbound imports"]; Set["Starting"]; Loader.Start[code] EXITS fail => NULL END; GetEditBuffer: PUBLIC PROC RETURNS[buffer: REF WordSeq _ NIL] = BEGIN first, last: CARDINAL; [first, last] _ SelectIndex[]; IF first>last THEN RETURN; firstMarked _ first; bufferLength _ last-first+1; buffer _ NEW[WordSeq[bufferLength]]; FOR i: CARDINAL IN [0..buffer.length) DO buffer[i] _ pageWords[first+i] ENDLOOP END; PutEditBuffer: PUBLIC PROC[buffer: REF WordSeq, r: ROPE _ NIL] = BEGIN h: GVPRef = GetHandle[]; last, len: CARDINAL; IF buffer=NIL THEN {Flash["No words changed"]; RETURN}; len _ MIN[buffer.length, bufferLength]; last _ firstMarked+len-1; h.logStream.PutF["\nExternal edits made on page %d in words [%d..%d], (old_new) %g\n", [cardinal[h.pPage]], [cardinal[firstMarked]], [cardinal[last]], [rope[r]]]; FOR i: CARDINAL IN [0..len) DO IF pageWords[firstMarked+i]#buffer[i] THEN h.logStream.PutF["Word %d: %06b_%06b\n", [cardinal[firstMarked+i]], [cardinal[pageWords[firstMarked+i]]], [cardinal[buffer[i]]]]; pageWords[firstMarked+i] _ buffer[i] ENDLOOP; Set[IO.PutFR["External edit, words in [%d..%d] altered", [cardinal[firstMarked]], [cardinal[last]]]]; thisPageChanged _ TRUE; ShowPageProc[h]; IndexSelect[firstMarked, last] END; EditInfo: PROC[h: GVPRef] = BEGIN Labels.Set[editMsg, IO.PutFR["Page %d, %g, %g", [cardinal[h.pPage]], [rope[IF thisPageChanged THEN "edits pending" ELSE "no edits"]], [rope[IF GetPageState[h.pPage]=alteredPage THEN "altered" ELSE "unchanged"]]]] END; ShowPageProc: PROC[h: GVPRef] = BEGIN out: IO.STREAM = h.editorStream; IF NOT PageOK[] THEN RETURN; ToFocus[]; out.Reset[]; out.PutText["\n"]; Look[out, fixed]; EditInfo[h]; index _ 0; FOR i: CARDINAL IN [0..pageHdrSize) DO Break[out, index, fixed]; out.PutF["%06b\t", [cardinal[pageWords[index]]]]; index _ index+1 ENDLOOP; WHILE index < pageWordSize DO TRUSTED BEGIN ptr: LONG POINTER TO ObjectHeader = LOOPHOLE[pageBytes, LONG POINTER]+index; limit: CARDINAL; ShowHeader[out, ptr]; limit _ index+ptr.size; IF limit > pageWordSize THEN limit _ pageWordSize; Look[out, fixed]; FOR i: CARDINAL IN [index..limit) DO PutNum[out, pageWords[i], fixed] ENDLOOP; END; ENDLOOP; out.Flush[] END; ShowHeader: PROC [out: IO.STREAM, ptr: LONG POINTER TO ObjectHeader] = TRUSTED BEGIN bad: BOOLEAN = ptr.number.page>256 OR ptr.number.index>85 OR ptr.number.fill#0 OR ptr.size+index+objHdrSize>pageWordSize; Look[out, bold]; -- do a header IF index > pageWordSize-objHdrSize THEN WHILE index out.PutF["%06b\t", [cardinal[number]]]; stringMode => out.PutF["RN-%03b\t", [cardinal[number MOD 256]]]; charMode => BEGIN c1: CARDINAL = number/256; c2: CARDINAL = number MOD 256; c1c: BOOLEAN = c1>=040B AND c1<=177B; c2c: BOOLEAN = c2>=040B AND c2<=177B; SELECT TRUE FROM c1c AND c2c => out.PutF[" %g %g \t", [character[LOOPHOLE[c1]]], [character[LOOPHOLE[c2]]]]; NOT c1c AND NOT c2c => out.PutF["%06b\t", [cardinal[number]]]; c1c AND NOT c2c => out.PutF[" %g %03b\t", [character[LOOPHOLE[c1]]], [cardinal[c2]]]; NOT c1c AND c2c => out.PutF["%03b %g \t", [cardinal[c1]], [character[LOOPHOLE[c2]]]]; ENDCASE => ERROR END; ENDCASE => ERROR; index _ index + 1; END; ParseValues: PROC[values: REF WordSeq] RETURNS[number: CARDINAL] = BEGIN r: ROPE = ViewerTools.GetContents[valueInput]; len: CARDINAL = Rope.Length[r]; word: CARDINAL; i: CARDINAL _ 0; number _ 0; WHILE i BEGIN -- octal number word _ 0; WHILE c IN ['0..'7] DO word _ word*8 + c -'0; i _ i+1; IF i=len THEN EXIT; c _ Rope.Fetch[r, i] ENDLOOP; values[number] _ word; number _ number+1 END; c='" => BEGIN i _ i+1; -- char after quote WHILE i i _ i+1; ENDCASE => {number _ lastCard; EXIT} ENDLOOP END; EditorInit: PUBLIC PROC = BEGIN editModeVec _ NEW[EditModeSeq[pageWordSize]]; values _ NEW[WordSeq[40]]; pageWords _ NEW[PageWordVec]; TRUSTED { pageBytes _ LOOPHOLE[pageWords]; }; pageHere _ FALSE; thisPageChanged _ FALSE; firstMarked _ 0; bufferLength _ 0 END; EditorTidyUp: PUBLIC PROC = BEGIN pageWords _ NIL; pageBytes _ NIL; editModeVec _ NIL; values _ NIL END; END. jGVPEditor.mesa HGM, May 24, 1984 0:20:54 am PDT Steve Temple, November 19, 1982 1:53 pm Schroeder, March 31, 1983 9:59 am This module implements the page editing functions of GVPatch. The editor mode viewer is created here and all its button procs live here. The edit buffer is a 256 word zone addressable by byte or word. It is loaded with data from the current page in the local heap and edits are performed on the words in it. The edited page may be written back into the local heap at any stage and it is also possible to fetch the current page from the server to undo edits made to the local heap. Underlying the edit buffer is a SEQUENCE of 256 entries which control how the corresponding word in the edit buffer will be displayed. This sequence is editModeVec. values is used to hold replacement words while editing, thisPageChanged says whether the page in the buffer has had edits made to it and pageHere says if there is page in the buffer at all. pageWords and pageBytes are pointers to the buffer, bufferLength and firstMarked are used when external editing is taking place. MakeEditorButtons makes a container for the editor buttons, input viewer and label. It also makes the typescript and associated stream. See MakeServerButtons for more detail. EditorButton is called from clicks of the EDITOR button in browser or server modes. Note that if the call to CheckStructure fails we aren't allowed into editor mode. SelectIndex figures out which words in the buffer the current selection is pointing at on the screen. There is a lot of dubious character counting going on here. Main fact is that there are 63 chars on each line and 32 lines of 8 numbers with tabs between them. We return first and last the indices of the two words in question. If anything goes wrong we return with first>last having flashed something suitable. IndexSelect is the inverse of SelectIndex. Given two indices in [0..255] it puts the current selection across these two (and any intervening) words. PageOK checks that there is a page in the buffer ResetPageButton gets the current page from the server, puts it in the buffer and in the local copy of the heap. GetPageButton puts the current page in the buffer from local if possible else from the server. GetNextButton and GetPrevButton do the obvious, they set current page to next or prev and then do a GetPage. If the GetPage fails we undo the next or prev operation. PutPageButton writes the buffer to the local heap if edits have been made NewPage is called whenever a new page is put in the edit buffer. It resets the display mode sequence and some flags and then prints the page. ShowPageButton just calls the proc to do the job. ObjectButton takes the first word of the current selection to be the start of an object. It does a few checks beforehand to see if this is a reasonable thing to do and then extends the current selection over the whole object (based on its size field) and puts some more info in the message label. TimeStampButton is similar in idea to ObjectButton but interprets the word at the selection as the start of a timestamp (3 words) and prints that in the message window in detail. Size button interprets the word at the selection as a size field and extends the selection to cover all the words implied by the size. This is easy to do on the current page but very tricky if we go off the end of the page. (Hence only implemented for on-page sizes). RNameButton uses the word at the selection as the start of an RName (a Mesa STRING) and extends the selection across the string. It also changes the display mode of the string's body so that it prints suitably next time the page is redrawn. ReplaceButton performs the editing function proper. Words in the current selection are editable and the user must have supplied values in the input window to replace them. We only replace the minimum number of words. A note of the words changed is written to the log and the page is redrawn. CharsButton and WordsButton set the display mode for words under the current selection. The page is not redrawn however. ExternalButton invokes an external editing procedure. The file GVPatchExternal.bcd is loaded and started. Note that GVPDefs.bcd must be around to compile and load the procedure. GetEditBuffer is what the external editing program calls to get a copy of the words in the current selection. If the buffer is intact and the selection is good we return a REF to a SEQUENCE of CARDINAL containing the words. If something is wrong we return NIL. PutEditBuffer expects to be given the same SEQUENCE that GetEditBuffer gave out (another of the same size or smaller is OK) and replaces the words which were in the selection when GetEditBuffer was called with the words in the SEQUENCE. The log is updated with the rope argument as a comment. If the REF argument is NIL nothing happens. EditInfo simply puts what we know about the current page and buffer where we can see it ShowPageProc prints the contents of the edit buffer on the screen. We move the selction out while we reset the typescript and then print words in various formats. Each word must print in a field of 7 chars. The first 2 words always print in octal and object headers always print bold with the first word octal, the second a string saying the object type and the third a string saying whether the object number is OK or not. Other words are printed according to their entry in the display mode sequence. ShowHeader prints an object header in bold face. There are 3 words to print and Break is called between each. Look takes a rope and adds those looks corresponding to its chars to the given stream Looks used are to clear looks, "f" for fixed pitch font and "b" for bold. Break prints a newline and word number if the word is a multiple of 8. It remembers the current look and restores it after doing its stuff. Note that we compare look strings by their pointers! PutNum prints a buffer word according to the display mode entry for that word. It is careful to get characters correct if they aren't printable. For the first word of a string (the length) it prints "RN-xxx" where xxx is the length in octal. ParseValues reads octal numbers and strings of characters from a rope and packs them into a SEQUENCE of CARDINALs. Characters are surrounded by double quotes ("), are packed two to word and if an odd number occur together the last word is padded with zero. Items may be separated by spaces or tabs only. A REF to the SEQUENCE is passed in and the proc returns the number of words created or LAST[CARDINAL] if something went wrong. EditorInit gets storage and initialises simple variables, note that we restrict the replace SEQUENCE to 40 entries. EditorTidyUp just frees the store we took. ÊO˜šœ™Jšœ ™ Jšœ'™'Jšœ!™!—J˜JšœW™WJšœ0™0J˜šÏk ˜ Jšœ œ˜Jšœœ˜Jšœ œ/˜?Jšœœ˜!Jšœ œ ˜J˜Jš œœ&œœœ˜?Jšœœ˜Jšœœ%˜1Jšœœ˜!Jšœ œ˜Jšœœ#˜-Jšœ œ˜%Jšœ œ)˜8šœ œd˜uJ˜J˜——Jšœ œ˜J˜Jšœ˜ J˜šœ.œ8˜pJ˜—Jšœ ˜J˜Jšœœœ ˜*J˜J˜Jšœ[™[JšœY™YJšœ^™^Jšœ]™]JšœX™XJšœ1™1J˜Jšœœ˜Jšœœ ˜Jšœœ˜J˜Jšœœ$˜9Jš œ œœœ œœ˜IJ˜Jšœ œœ˜#J˜J˜JšœT™TJšœZ™ZJšœS™SJšœ;™;J˜Jšœœ œ˜J˜Jšœœœ˜+J˜Jšœ œœ˜!Jšœ œœ˜!J˜Jšœœ˜(J˜J˜7J˜J˜JšœS™SJšœZ™ZJ˜JšÏnœœœ˜1˜Jšœœ ˜Jšœœ˜Jšœœ˜ Jšœœ˜ J˜˜8Jšœœ œ˜DJ˜—JšœHœ˜QJ˜šœ)œ'˜SJšœœœ˜2J˜—˜XJšœ(œ œ˜?J˜—˜VJ˜LJ˜—˜UJ˜KJ˜—˜QJ˜NJ˜—˜OJ˜LJ˜—˜OJ˜LJ˜—˜OJ˜LJ˜—˜OJ˜LJ˜—˜LJ˜JJ˜—˜LJ˜JJ˜—˜PJ˜MJ˜—˜QJ˜MJ˜—˜NJ˜LJ˜—˜QJ˜MJ˜—˜MJ˜GJ˜—˜MJ˜FJ˜—˜LJ˜JJ˜—˜LJ˜EJ˜—˜RJšœœ˜—Jšœ˜J˜J˜—JšœS™SJšœQ™QJ˜šœœœ˜-Jšœ œ ˜Jšœœ˜Jšœœœ œ˜!J˜ JšœUœ˜\JšœRœ˜YJšœRœ˜YJšœJœ˜QJ˜J˜+J˜.J˜+J˜#J˜J˜!J˜Jšœ˜J˜J˜—šœœÏc ˜CJ˜$Jšœ˜J˜J˜—JšœY™YJšœY™YJšœU™UJšœY™YJšœ9™9J˜š ž œœœœ˜8J˜J˜J˜ J˜ Jšœ,œœ˜SJšœœ œœ˜J˜2J˜J˜!Jšœ œ ˜Jšœœ ˜Jšœ(œ˜2Jšœ%œ˜/Jšœœ˜3Jšœœ˜1šœ ˜Jšœœ1˜J˜Jš˜—Jšœ˜J˜J˜—Jšœ\™\JšœV™VJ˜šœ ˜+Jšœ œ˜Jšœ œœ˜*J˜J˜Jšœ œœ˜J˜Jšœœ"œœ˜MJšœœ œœ˜0šœœ%˜+Jšœ[˜[—Jšœœ˜4Jšœ˜J˜J˜—Jšœ^™^JšœX™XJšœT™TJ˜šœ˜Jšœ œ ˜Jšœœ˜!J˜J˜Jšœ œœ˜J˜J˜J˜Jšœœœœ˜;šœ ˜ šœœ>˜IJšœ!˜%——J˜Jšœ˜J˜J˜—JšœW™WJšœ^™^Jšœ9™9J˜šœ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœœ˜J˜J˜Jšœ œœ˜J˜Jšœœœ˜6J˜J˜J˜JšœœK˜QJ˜ J˜ J˜J˜šœœœ ˜!Jšœœ˜Jšœœœ˜"Jšœœ˜#Jšœœœ˜'J˜J˜&J˜J˜Jšœœœ˜J˜&J˜Jšœ˜—J˜J˜Jšœ˜J˜J˜—JšœV™VJšœW™WJšœW™WJšœ™J˜šœœ˜'Jšœ œ ˜Jšœœ˜"Jšœœ˜J˜J˜Jšœ œœ˜Jšœ œœ!œ˜BJ˜Jšœœ˜J˜˜IJ˜:J˜—šœœœ˜šœœ˜%˜)J˜J˜J˜——J˜Jšœ˜—J˜J˜šœ˜Jšœœ-˜8šœœ.˜9J˜:——Jšœœ˜J˜J˜Jšœ˜J˜J˜—JšœW™WJšœ ™ J˜šœ œ˜%Jšœ œ˜J˜Jšœ œœ˜Jš œœœœœ˜FJ˜Jšœ˜J˜—šœ œ˜%Jšœ œ˜J˜Jšœ œœ˜Jš œœœœœ˜FJ˜Jšœ˜J˜J˜—JšœU™UJšœ[™[J˜šœœ ˜0J˜Jšœœœ˜šœ0˜0šœ˜J˜:Jšœ˜ Jšœ˜J˜——šœ(˜(šœ˜Jšœ;˜;Jšœ˜ Jšœ˜——Jšœœœ$˜5J˜J˜J˜Jšœ ˜Jšœ˜J˜J˜—Jšœ[™[JšœZ™ZJšœO™OJ˜š ž œœœœ œ œ˜EJšœ œ˜J˜Jšœ œœ˜J˜J˜Jšœ œ˜$Jš œœœœ ˜OJšœ˜J˜J˜—JšœX™XJšœZ™ZJšœY™YJšœB™BJ˜š ž œœœ œ œœ˜FJ˜Jšœ œ˜Jšœœœœ˜7Jšœœ˜'J˜J˜˜VJ˜.J˜J˜—šœœœ ˜šœ$˜*˜)J˜J˜&J˜——J˜$Jšœ˜J˜—šœœ3˜9J˜,—Jšœœ˜J˜J˜Jšœ˜J˜J˜—JšœW™WJ˜šžœœ˜!šœœ.˜DJšœœœœ˜Ašœœ#˜+Jšœ œ˜#——Jšœ˜J˜J˜—JšœW™WJšœY™YJšœ^™^JšœX™XJšœU™UJšœ6™6J˜šž œœ˜%Jšœœœ˜ Jšœœ œœ˜J˜J˜ J˜ J˜J˜J˜ J˜J˜ J˜šœœœ˜&J˜J˜1J˜Jšœ˜J˜—šœ˜Jšœ˜ Jš œœœœœ œœ˜LJšœœ˜J˜J˜J˜J˜Jšœœ˜2J˜Jš œœœœ"œ˜NJš˜Jšœ˜—J˜ Jšœ˜J˜J˜—JšœU™UJšœ™J˜š ž œœœœ œœ ˜Tšœœœ˜=Jšœœ(˜J˜šœ˜J˜3Jšœ5œ˜@šœ ˜Jšœœ˜Jšœœ œ˜Jšœœ œ ˜%Jšœœ œ ˜%Jšœœ˜šœœ˜Jšœ"œœ˜M—šœœœ˜J˜'—šœœœ˜Jšœ"œ˜B—šœœ˜Jšœ2œ˜B—Jšœ˜Jšœ˜Jšœœ˜——J˜Jšœ˜J˜J˜—JšœY™YJšœS™SJšœR™RJšœY™YJšœS™SJ˜š ž œœ œ œ œ˜HJšœœ'˜.Jšœœ˜Jšœœ˜Jšœœ˜J˜ J˜šœ˜Jšœœ˜Jšœœ˜˜šœœ œŸ˜&J˜ šœœ ˜J˜J˜Jšœœœ˜J˜Jšœ˜—J˜J˜Jšœ˜J˜—šœ˜ Jšœ Ÿ˜šœ˜J˜Jšœœ œ˜Jšœ œ˜J˜J˜J˜Jšœœœ˜J˜Jšœœ œ˜Jšœ œœ˜"J˜J˜Jš˜—Jšœ˜J˜—Jš œœœœœ ˜J˜Jšœœ˜$—Jš˜—Jšœ˜J˜J˜—Jšœ[™[JšœB™BJ˜šž œœœ˜Jšœœ˜-Jšœ œ˜Jšœ˜Jšœœ˜-Jšœ œ˜Jšœœ˜J˜J˜Jšœ˜J˜J˜—šž œœœ˜!Jšœ œ˜Jšœ œ˜Jšœœ˜Jšœ ˜ Jšœ˜J˜—Jšœ˜J˜J˜—…—G.xç