DIRECTORY Ascii, CharDisplays, SimpleDisplays, IO, ViewerOps, ViewerClasses, ViewerForkers, MessageWindow, Beeps; SimpleDisplaysImpl: CEDAR MONITOR LOCKS NARROW[cd.otherInstanceData, SimpleDisplayState] USING cd: CharDisplay IMPORTS CharDisplays, SimpleDisplays, IO, ViewerOps, ViewerForkers, MessageWindow, Beeps EXPORTS SimpleDisplays ~ BEGIN OPEN CharDisplays, SimpleDisplays; SimpleCharDisplayClass: CharDisplayClass = NEW [CharDisplayClassRep _ [ name: "Simple", Init: SDInit, ChangeDetails: SDChangeDetails, DeleteChar: SDDeleteChar, TakeChar: SDTakeChar, CursorMove: SDCursorMove, Line: SDLine, ClearTo: SDClearTo, ClearAll: SDClearAll, SetEmph: SDSetEmph, Emphasize: SDEmphasize, SetFont: SDSetFont, Beep: SDBeep, Flush: SDFlush, Destroyed: SDDestroyed ]]; SDInit: PROC [cd: CharDisplay, initData: REF ANY] = { sd: SimpleDisplayState _ NEW [ SimpleDisplayStateRep _ [driver: NARROW[initData]] ]; cd.otherInstanceData _ sd; InitCharArray[ sd, cd.det.lines, cd.det.columns ]; [cd.viewer, cd.fromDisplay] _ SimpleDisplaysViewer[sd, cd]; }; SDDeleteChar: ENTRY PROC [cd: CharDisplay] = { ENABLE UNWIND => NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; line: REF LineRep = sd.theLines[cd.line]; chars: REF TEXT = line.chars; IF sd.debugChars THEN { sd.out.PutRope[""]; }; FOR i: INT IN [cd.col .. cd.det.columns-1) DO chars[i] _ chars[i+1]; ENDLOOP; chars[cd.det.columns-1] _ Ascii.SP; IF line.hasSomeEmphasis THEN { emphs: REF LineEmphsRep _ line.emphs; FOR i: INT IN [cd.col .. cd.det.columns-1) DO emphs[i] _ emphs[i+1]; ENDLOOP; emphs[cd.det.columns-1] _ ALL[FALSE]; }; NoteChange[ cd, sd, line, delete1, cd.col ]; }; SDTakeChar: ENTRY PROC [cd: CharDisplay, char: CHAR, insert: BOOL _ FALSE] = { ENABLE UNWIND => NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; line: REF LineRep = sd.theLines[cd.line]; chars: REF TEXT _ line.chars; IF sd.debugChars THEN { IF insert THEN {sd.out.PutChar['_]; }; SELECT char FROM IN [Ascii.SP .. Ascii.DEL) => {sd.out.PutChar[char]; }; ENDCASE => {sd.out.PutF["<%h>", IO.char[char]]; }; }; IF insert THEN { FOR i: INT DECREASING IN [cd.col+1 .. cd.det.columns) DO chars[i] _ chars[i-1]; ENDLOOP; IF line.hasSomeEmphasis THEN { emphs: REF LineEmphsRep _ line.emphs; FOR i: INT DECREASING IN [cd.col+1 .. cd.det.columns) DO emphs[i] _ emphs[i-1]; ENDLOOP; }; }; chars[cd.col] _ char; line.emphs[cd.col] _ sd.emphs; line.hasSomeEmphasis _ sd.hasSomeEmphasis OR line.hasSomeEmphasis; NoteChange[ cd, sd, line, (IF insert THEN insert1 ELSE over1), cd.col ]; CursorMoveAux[cd, 0, 1, TRUE, TRUE, TRUE, FALSE]; }; SDCursorMove: ENTRY PROC [cd:CharDisplay, line,col:INT, relative:BOOL_FALSE, doLine,doCol: BOOL _ TRUE] = { ENABLE UNWIND => NULL; CursorMoveAux[cd,line,col,relative,doLine,doCol,TRUE] } ; CursorMoveAux: INTERNAL PROC [cd:CharDisplay, line,col:INT, relative,doLine,doCol,report: BOOL] = { sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; L:INTEGER = cd.det.lines; C:INTEGER = cd.det.columns; IF sd.debugCM AND report THEN { IF relative THEN sd.out.PutRope["", IO.int[col]] ELSE sd.out.PutRope[".>"]; }; IF relative THEN {line _ line + cd.line; col _ col + cd.col}; IF NOT doLine THEN line _ cd.line; IF NOT doCol THEN col _ cd.col; IF cd.det.autoMargins THEN { dl: INT = col / C; line _ line + dl; col _ col - dl * C; WHILE col < 0 DO col _ col + C; line _ line - 1 ENDLOOP; } ELSE col _ MAX[MIN[col, C-1], 0]; IF line < 0 THEN line _ 0; IF cd.det.scrolls THEN IF line >= L THEN { Scroll[cd, 0, L, MIN[ (line-L)+1, L ] ]; line _ L - 1 } ELSE line_line ELSE line _ line MOD L; cd.line _ line; cd.col _ col; }; Scroll: INTERNAL PROC [cd: CharDisplay, start, size, scrollReq:INTEGER] ~ { sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; shift: INTEGER = IF scrollReq < 0 THEN (size-start)+scrollReq ELSE scrollReq; upFillBase: INTEGER = size - shift; IF sd.debugScroll THEN sd.out.PutF["Scroll[start:%g,size:%g,req:%g]\n",IO.int[start],IO.int[size],IO.int[scrollReq] ]; IF shift IN (0..size-start) THEN { open: INTEGER _ start + shift; -- this line will eventually be in position start headLine: REF LineRep = sd.theLines[open]; WHILE open # start DO source:INTEGER = IF (open NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; IF sd.debugOps THEN { sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; msg: ROPE _ IF insert THEN "" ELSE ""; sd.out.PutRope[msg]; }; Scroll[ cd, cd.line, cd.det.lines, IF insert THEN -1 ELSE 1 ]; }; SDClearTo: ENTRY PROC [cd: CharDisplay, where: Where] = { ENABLE UNWIND => NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; line: REF LineRep = sd.theLines[cd.line]; chars: REF TEXT _ line.chars; IF sd.debugOps THEN { sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; msg: ROPE _ SELECT where FROM EndOfLine => "", EndOfScreen => "", ENDCASE => ERROR; sd.out.PutF[msg]; }; FOR j: INT IN [cd.col .. cd.det.columns) DO chars[j] _ Ascii.SP; ENDLOOP; IF line.hasSomeEmphasis THEN { emphs: REF LineEmphsRep _ line.emphs; FOR j: INT IN [cd.col .. cd.det.columns) DO emphs[j] _ ALL[ FALSE ]; ENDLOOP; }; IF cd.col = 0 THEN { line.modified _ new; line.hasSomeEmphasis _ FALSE; } ELSE NoteChange[ cd, sd, line, tail, cd.col ]; IF where = EndOfScreen AND cd.line+1 < cd.det.lines THEN Scroll[ cd, cd.line+1, cd.det.lines, cd.det.lines - (cd.line+1) ] }; SDClearAll: ENTRY PROC [cd: CharDisplay] = { ENABLE UNWIND => NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; IF sd.debugOps THEN { sd.out.PutF[""] }; Scroll[ cd, 0, cd.det.lines, cd.det.lines ]; }; EmphNames: ARRAY Emph OF ROPE _ [ underline: "underline", bold: "bold", italic: "italic", inverse: "inverse"]; SDSetEmph: ENTRY PROC [cd: CharDisplay, emph: Emph, on: BOOL] = { ENABLE UNWIND => NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; IF sd.debugEmph THEN sd.out.PutF["<%g %g>", IO.rope[EmphNames[emph]], IO.bool[on]]; sd.emphs[emph] _ on; sd.hasSomeEmphasis _ sd.emphs[underline] OR sd.emphs[bold] OR sd.emphs[italic] OR sd.emphs[inverse] ; }; SDEmphasize: ENTRY PROC [cd: CharDisplay, emph: Emph, on: BOOL] = { ENABLE UNWIND => NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; IF sd.debugEmph THEN sd.out.PutF["<%g %g@>", IO.rope[EmphNames[emph]], IO.bool[on]]; sd.theLines[cd.line].emphs[cd.col][emph] _ on; NoteChange[ cd, sd, sd.theLines[cd.line], over1, cd.col ]; }; SDSetFont: ENTRY PROC [cd: CharDisplay, font: ROPE] = { ENABLE UNWIND => NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; IF sd.debugOps THEN sd.out.PutF["", IO.rope[font]]; }; SDBeep: ENTRY PROC [cd: CharDisplay] = { ENABLE UNWIND => NULL; IF Beeps.can THEN Beeps.Beep[ frequency:1000, duration:65 ] -- Hz, milliseconds ELSE { MessageWindow.Append["Beep", TRUE]; MessageWindow.Blink[]; MessageWindow.Clear[]; }; }; SDFlush: PROC [cd: CharDisplay] ~ { sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; ViewerOps.PaintViewer[sd.aViewer, ViewerOps.PaintHint.client, FALSE, $LINES]; sd.changeCount _ 0; }; SDDestroyed: ENTRY PROC [cd: CharDisplay] RETURNS [b: BOOL] = { ENABLE UNWIND => NULL; sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; b _ sd.aViewer.destroyed; }; InitSimpleDisplayClass: PROC ~ { RegClass[SimpleCharDisplayClass]; }; InitCharArray: PROC [sd: SimpleDisplayState, r,c: INT] ~ { sd.theLines _ NEW[LinesRep[r]]; FOR i: INT IN [0..r) DO lineEmphs: REF LineEmphsRep _ NEW[LineEmphsRep[c]]; lineChars: REF TEXT _ NEW[TEXT[c]]; FOR j: INT IN [0..c) DO lineChars[j] _ Ascii.SP; ENDLOOP; sd.theLines[i] _ NEW [LineRep _ [ emphs: lineEmphs, chars: lineChars, screenLine: i ]]; ENDLOOP; }; ResizeTerminal: PUBLIC PROC [ cd: CharDisplay, sd: SimpleDisplayState, r,c: INT ] ~ { oldLines: REF LinesRep _ sd.theLines; oldr: INT _ cd.det.lines; oldc: INT _ cd.det.columns; oldCopyBase : INT _ IF oldr > r THEN oldr - r ELSE 0; InitCharArray[ sd, r, c ]; FOR i: INT IN [0..MIN[r,oldr]) DO oldes: REF LineEmphsRep _ oldLines[oldCopyBase + i].emphs; newes: REF LineEmphsRep _ sd.theLines[i].emphs; oldcs: REF TEXT _ oldLines[oldCopyBase + i].chars; newcs: REF TEXT _ sd.theLines[i].chars; sd.theLines[i].hasSomeEmphasis _ oldLines[oldCopyBase + i].hasSomeEmphasis; FOR j: INT IN [0..MIN[c,oldc]) DO newes[j] _ oldes[j]; newcs[j] _ oldcs[j]; ENDLOOP; ENDLOOP; cd.det.lines _ r; cd.det.columns _ c; cd.line _ MIN[ cd.line, r-1 ]; cd.col _ MIN[ cd.col, c-1 ]; }; NoteChange: INTERNAL PROC [cd: CharDisplay, sd: SimpleDisplayState, line:REF LineRep, mod:ModificationType, col:INT] ~ { IF line.modified = unchanged THEN { line.modified _ mod; line.tailStartColumn _ col } ELSE IF line.modified = new THEN NULL ELSE { line.modified _ tail; line.tailStartColumn _ MIN[ line.tailStartColumn, col ]; }; IF sd.flushMode = FlushOnChangeCount THEN IF sd.changeCount >= sd.changeCountLimit THEN { sd.changeCount _ 0; ViewerForkers.ForkPaint[ viewer: sd.aViewer, hint: ViewerClasses.PaintHint.client, clearClient: FALSE, whatChanged: $LINES, tryShortCuts: FALSE ]; WAIT sd.paintDone; } ELSE { sd.changeCount _ sd.changeCount + 1; } }; InitSimpleDisplayClass[]; END. ^ SimpleDisplays.mesa Copyright c 1990 by Xerox Corporation. All rights reserved. Norman Adams, March 4, 1990 3:38 pm PST Spreitze, April 3, 1990 2:23 pm PDT Last tweaked by Mike Spreitzer on April 4, 1990 12:20:06 pm PDT Known bugs and shortcomings: Smooth interaction of selection/pasting with tioga viewers. Emphasis is not written to the transcript. Add UserProfile opotions: default terminal type, initial lines in terminal and in transcript, font, transcript visibility Blink the cursor? Use DoWithBuffer in the paint proc? Reduce number of repaints on size changes Add mouse support -- Notify proc translates mouse clicks to terminal coordinates then uses a User Profile specified pattern string to generate terminal type-in. The display is represented as a sequence of REF TEXTs. The length field of the TEXT is always ignored, and the TEXT is always padded with space characters. The paint code always strips trailing blanks before painting the line. The problem with this is that emphasis on trailing spaces does not get painted. It also seems cruftier than maintaining an accurate length for each TEXT. When the transcript in not visible, the space between the menu rule and the terminal is whatever was left over after allocating a whole number of lines. It would be better if this slop appeared at the bottom of the terminal viewer rather than at the top. That would make the full repaint out of the adjust proc unnecessary sometimes. As it is now, the position of the characters on the screen changes by up to +-slop whenever the size of the viewer gets smaller, so we must do a repaint then. If the slop were at the bottom the characters would not move when the viewer got smaller. -- start is the index in the lines array of the topmost line to be moved by the scroll -- size is the index 1 more than the last line in the region being scrolled -- ABS[scrollReq] may be [0 .. size - start] -- if scrollReq<0 then insert blank lines at start, remove lines from EOS (scroll down) -- if scrollReq>0 then delete lines at start, insert blank lines at EOS (scroll up) -- this loop really is just scrolling up, for scrolling down shift is hacked to get -- the equivalent of the scroll down by scrolling up. -- Log and clear filled lines -- Flush changes to display Helpers lineEmphs[j] _ ALL[FALSE]; -- defaults to false DumpCharArray: PROC [cd: CharDisplay] ~ { sd: SimpleDisplayState = NARROW[cd.otherInstanceData]; r: INTEGER = cd.det.lines; c: INTEGER = cd.det.columns; sd.out.PutF["--%g--\n", IO.int[sd.theLines.count]]; FOR i:INTEGER IN [0..r) DO es: REF LineEmphsRep _ sd.theLines[i].emphs; cs: REF TEXT _ sd.theLines[i].chars; sd.out.PutF["%g(%g): ", IO.int[i], IO.int[cs.maxLength]]; FOR j: INTEGER IN [0..c) DO ch:CHAR _ cs[j]; sd.out.PutChar[ch]; ENDLOOP; sd.out.PutF["\n"]; ENDLOOP; }; ViewerOps.PaintViewer[sd.aViewer, ViewerOps.PaintHint.client, FALSE, $LINES] Κ(•NewlineDelimiter ™™Icodešœ Οmœ1™K˜—K˜š  œž œ$˜9Kšžœžœžœ˜Kšœžœ˜6Kšœžœ ˜)Kšœžœžœ˜K˜šžœ žœ˜Kšœžœ˜6šœžœžœž˜Kšœ˜Kšœ˜Kšžœžœ˜—K˜Kšœ˜—Kš žœžœžœžœžœžœ˜IK˜šžœžœ˜Kšœžœ˜%Kšžœžœžœžœ žœžœžœ˜MK˜K˜—šžœ žœ˜K˜Kšœžœ˜—šœž˜Kšœ)˜)—K˜šžœžœžœ˜9KšœA˜A—K˜—K˜š  œž œ˜,Kšžœžœžœ˜Kšœžœ˜6Kšžœ žœ ˜3K˜,K˜—šœ žœžœžœ˜!Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜—K˜š  œž œ#žœ˜AKšžœžœžœ˜Kšœžœ˜6Kšžœžœžœžœ ˜SK˜Kšœ)žœžœžœ˜eK˜—K˜š  œž œ#žœ˜CKšžœžœžœ˜Kšœžœ˜6Kšžœžœžœžœ ˜TKšœ.˜.Kšœ:˜:K˜—K˜š  œž œžœ˜7Kšžœžœžœ˜Kšœžœ˜6Kšžœ žœžœ ˜žœ ˜MKšœ˜K˜—K˜š  œž œžœžœ˜?Kšžœžœžœ˜Kšœžœ˜6K˜K˜—K˜—šΠbl™K˜š œžœ˜ Kšœ!˜!K˜—K˜š  œžœžœ˜;Kšœžœ˜šžœžœžœž˜Kšœ žœžœ˜3Kš œ žœžœžœžœ˜#šžœžœžœž˜Kšœžœžœ™0Kšœžœ˜Kšžœ˜K˜—šœžœC˜WK˜—Kšžœ˜—K˜—K˜š œž œ1žœ˜UKšœ žœ˜%Kšœžœ˜Kšœžœ˜Kš œžœžœ žœ žœ˜5Kšœ˜K˜š žœžœžœžœ ž˜!Kšœžœ0˜:Kšœžœ%˜/Kšœžœžœ#˜2Kšœžœžœ˜'KšœK˜Kš žœžœžœžœ ž˜!Kšœ˜Kšœ˜Kšžœ˜—Kšžœ˜K˜—K˜K˜Kšœ žœ˜Kšœ žœ˜K˜K˜—K˜š  œžœ™)Kšœžœ™6Kšœžœ™Kšœžœ™K™3šžœžœžœž™Kšœžœ%™,Kšœžœžœ™$K™9šžœžœžœž™Kšœžœ ™Kšœ™Kšžœ™—K™Kšžœ™—K™K™K™K˜—š  œž œ0žœ$žœ˜xK˜šžœžœ˜$Kšœ˜Kšœ˜—šœžœžœž˜"Kšž˜—šžœ˜K˜Kšœžœ˜8K˜—K˜šžœ#žœ˜*šžœ'žœ˜/K˜šœ˜Jšœ˜Jšœ)˜)Jšœ žœ˜Jšœ˜Jšœžœ˜Jšœ˜—Jšžœ˜Jšœ>žœ ™L—šœžœ˜K˜$K˜K˜——K˜K˜—K˜—K˜Kšžœ˜—…—( B