GVPEditor.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.
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;
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.
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 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.
values: REF WordSeq ← NIL;
thisPageChanged, pageHere: BOOLEANFALSE;
pageWords: REF PageWordVec ← NIL;
pageBytes: REF PageByteVec ← NIL;
bufferLength, firstMarked: CARDINAL ← 0;
editorButtons, editorText, valueInput, editMsg: Viewer;
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.
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 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.
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 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.
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 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
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 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.
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 writes the buffer to the local heap if edits have been made
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 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.
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 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.
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: ROPEIO.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 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.
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;
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).
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 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.
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 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.
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 and WordsButton set the display mode for words under the current selection.
The page is not redrawn however.
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 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.
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 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.
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 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.
PutEditBuffer: PUBLIC PROC[buffer: REF WordSeq, r: ROPENIL] = 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 simply puts what we know about the current page and buffer where we can see it
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 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.
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 prints an object header in bold face. There are 3 words to print and Break
is called between each.
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<pageWordSize DO -- just print bold octal if not enough room
Break[out, index, bold];
out.PutF["%06b\t", [cardinal[pageWords[index]]]];
index ← index+1
ENDLOOP
ELSE BEGIN
Break[out, index, bold];
out.PutF["%06b\t", [cardinal[pageWords[index]]]]; -- size
index ← index+1;
Break[out, index, bold];
out.PutF["%g\t", [rope[ObjectRope[LOOPHOLE[ptr.number.type, CARDINAL], FALSE]]]];
index ← index+1;
Break[out, index, bold];
out.PutF["%g\t", [rope[IF bad THEN "Bad " ELSE "Valid "]]];
index ← index+1
END
END;
Look takes a rope and adds those looks corresponding to its chars to the given stream
Looks used are <space> to clear looks, "f" for fixed pitch font and "b" for bold.
Look: PROC[out: IO.STREAM, r: ROPE] = BEGIN
out.PutF["%l", [rope[r]]]
END;
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!
Break: PROC[out: IO.STREAM, i: CARDINAL, oldLook: ROPE] = BEGIN
IF i MOD 8 = 0 THEN BEGIN
IF oldLook#fixed THEN Look[out, fixed];
out.PutF["\n%4d:\t", [cardinal[i]]];
IF oldLook#fixed THEN Look[out, oldLook]
END
END;
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.
PutNum: PROC[out: IO.STREAM, number: WORD, look: ROPE] = BEGIN
Break[out, index, look];
SELECT editModeVec[index] FROM
wordMode => 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 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.
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<len DO
c: CHAR ← Rope.Fetch[r, i];
SELECT TRUE FROM
c IN ['0..'7] => 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<len DO
c ← Rope.Fetch[r, i];
IF c='" THEN {i ← i+1; EXIT};
word ← 256*LOOPHOLE[c];
values[number] ← word;
number ← number+1;
i ← i+1;
IF i=len THEN EXIT;
c ← Rope.Fetch[r, i];
IF c='" THEN {i ← i+1; EXIT};
word ← word+LOOPHOLE[c, CARDINAL];
i ← i+1;
values[number-1] ← word
ENDLOOP
END;
c=IO.SP, c=IO.TAB => i ← i+1;
ENDCASE => {number ← lastCard; EXIT}
ENDLOOP
END;
EditorInit gets storage and initialises simple variables, note that we restrict the replace
SEQUENCE to 40 entries. EditorTidyUp just frees the store we took.
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.