<> <> <> <> DIRECTORY IO, Ascii, RefText, Rope, Xl, XlCursor, XlTextWindow; XlTextWindowImpl: CEDAR MONITOR IMPORTS IO, RefText, Rope, Xl, XlCursor EXPORTS XlTextWindow = BEGIN OPEN Xl, XlTextWindow; Failed: PUBLIC ERROR = CODE; myPropKey: REF ATOM ¬ NEW[ATOM ¬ $XlTextWindowImpl]; events: EventFilter ~ CreateEventFilter[configureNotify, destroyNotify]; TextConnection: TYPE = REF TextConnectionRec; TextConnectionRec: TYPE = MONITORED RECORD [ tq: TQ ¬ NIL, --share the expose tq for texts of same connection textCursor: Xl.Cursor ¬ nullCursor, defaultFont: Font ¬ nullFont ]; InitConnection: InitializeProcType = { tcr: TextConnection ¬ NEW[TextConnectionRec]; tcr.tq ¬ CreateTQ[$XlTextWindowImpl]; tcr.textCursor ¬ XlCursor.SharedStandardCursor[c, circle]; RETURN [tcr] }; TextRecord: TYPE = MONITORED RECORD [ connection: Xl.Connection ¬ NIL, window: Window ¬ nullWindow, wSize: Size ¬ [0, 0], --size of window lineDY: INT ¬ 15, --distance between lines leftSpace: NAT ¬ 2, --distance between left border and first character x origin rightSpace: NAT ¬ 2, --distance between window right border and last characters right border topOffset, bottomOffset: NAT ¬ 2, --distance between baseline and border ascent, descent: NAT ¬ 2, bottomSpace: NAT ¬ 2, charDX: NAT ¬ 10, lineSpace: NAT ¬ 2, pos: Point ¬ [0, 0], --position for next character; not yet clipped font: Font ¬ nullFont, gc: GContext ¬ NIL, --could we somehow share gc's? alive: BOOL ¬ TRUE ]; SetWindowSize: ENTRY PROC [tRef: REF TextRecord, s: Size] = { IF tRef#NIL THEN tRef.wSize ¬ s }; TextEventProc: EventProcType = { ENABLE Xl.XError => GOTO oops; tRef: REF TextRecord ~ NARROW[clientData]; WITH event SELECT FROM destroyNotify: DestroyNotifyEvent => { IF destroyNotify.window=tRef.window THEN tRef.alive ¬ FALSE }; configureNotify: ConfigureNotifyEvent => { IF configureNotify.window=tRef.window THEN { SetWindowSize[tRef, configureNotify.geometry.size]; }; }; mapNotify: MapNotifyEvent => { IF mapNotify.window=tRef.window THEN { g: GeometryRec ¬ GetGeometry[event.connection, tRef.window]; SetWindowSize[tRef, g.geometry.size]; }; }; ENDCASE => {}; EXITS oops => {}; }; CreateTextHandle: PUBLIC PROC [] RETURNS [handle: Handle] = { tRef: REF TextRecord ¬ NEW[TextRecord]; RETURN [tRef]; }; CreateTextWindow: PUBLIC PROC [handle: Handle, c: Connection, parent: Window, geometry: Geometry, font: Font ¬ nullFont, moreMatches: MatchList ¬ NIL, depth: INTEGER ¬ -1, attributes: Attributes] = { SetupFont: PROC [tcr: TextConnection] = { ENABLE UNWIND => NULL; IF tcr.defaultFont=nullFont THEN { tcr.defaultFont ¬ Xl.OpenFont[c, "8x13"]; } }; screen: Screen ¬ QueryScreen[c, parent]; tcr: TextConnection ¬ NARROW[GetConnectionPropAndInit[c, myPropKey, InitConnection]]; tRef: REF TextRecord ¬ NARROW[handle]; IF tRef.window#nullWindow THEN ERROR; attributes.eventMask ¬ OREvents[attributes.eventMask, exposureMask, structureNotifyMask]; IF attributes.backgroundPixel=illegalPixel THEN attributes.backgroundPixel ¬ screen.whitePixel; IF font=nullFont THEN { IF tcr.defaultFont=nullFont THEN SetupFont[tcr]; font ¬ tcr.defaultFont }; tRef.connection ¬ c; tRef.font ¬ font; Xl.IncRefCount[c, tRef]; SetWindowSize[tRef, geometry.size]; BEGIN fi: REF READONLY FontInfoRec ¬ QueryFont[c, tRef.font]; tRef.ascent ¬ fi.fontAscent; tRef.descent ¬ fi.fontDescent; tRef.lineDY ¬ tRef.ascent+tRef.descent+tRef.lineSpace; tRef.charDX ¬ fi.maxBounds.charWidth; tRef.topOffset ¬ tRef.ascent+tRef.lineSpace; tRef.bottomOffset ¬ tRef.descent+tRef.bottomSpace; tRef.gc ¬ MakeGContext[c]; --should we share contexts of same parent??? SetGCForeground[tRef.gc, screen.blackPixel]; SetGCBackground[tRef.gc, screen.whitePixel]; SetGCFont[tRef.gc, font]; END; HomePos[tRef]; moreMatches ¬ CONS[ NEW[MatchRep ¬ [proc: TextEventProc, handles: events, tq: tcr.tq, data: tRef]], moreMatches ]; IF IllegalCursor[attributes.cursor] THEN attributes.cursor ¬ tcr.textCursor; tRef.window ¬ CreateWindow[c: c, matchList: moreMatches, geometry: geometry, depth: depth, attributes: attributes, parent: parent]; tRef.alive ¬ TRUE; }; ForgetWindow: PUBLIC PROC [handle: Handle] = { tRef: REF TextRecord ¬ NARROW[handle]; Xl.DecRefCount[tRef.connection, tRef]; tRef.alive ¬ FALSE; tRef.connection ¬ NIL; tRef.window ¬ Xl.nullWindow; }; GetWindow: PUBLIC PROC [handle: Handle] RETURNS [w: Xl.Window] = { tRef: REF TextRecord ¬ NARROW[handle]; RETURN [tRef.window]; }; HomePos: ENTRY PROC [tRef: REF TextRecord] = { ENABLE UNWIND => NULL; IF tRef#NIL THEN { tRef.pos.x ¬ tRef.leftSpace; tRef.pos.y ¬ tRef.topOffset; }; }; PosAndIncrement: ENTRY PROC [tr: REF TextRecord, rr: INT] RETURNS [pos: Point ¬ [0, 0]] = { <<--increments position but returns previous value atomicly>> ENABLE UNWIND => NULL; IF tr#NIL THEN { pos ¬ tr.pos; tr.pos.x ¬ tr.pos.x+rr; }; }; OutRope: PUBLIC PROC [handle: Handle, r: Rope.ROPE, start: INT ¬ 0, len: INT ¬ Rope.MaxLen] = { tRef: REF TextRecord ¬ NARROW[handle]; Action: Rope.ActionType = {UnflushedOutChar[tRef, c]}; IF tRef.window=nullWindow THEN RETURN; [] ¬ Rope.Map[base: r, action: Action, len: len, start: start]; Flush[tRef.connection, 200]; }; OutChar: PUBLIC PROC [handle: Handle, ch: CHAR] = { tRef: REF TextRecord ¬ NARROW[handle]; IF tRef.window=nullWindow THEN RETURN; UnflushedOutChar[tRef, ch]; Flush[tRef.connection, 200]; }; UnflushedOutChar: PROC [tRef: REF TextRecord, ch: CHAR] = { IF tRef.connection=NIL OR ~tRef.alive THEN RETURN; <> SELECT ch FROM Ascii.CR, Ascii.LF => { NewLine[tRef]; }; Ascii.FF => { HomePos[tRef]; ClearArea[tRef.connection, tRef.window, [0, 0], [2000, 2000], FALSE]; }; Ascii.NUL => {}; Ascii.BS => { tRef.pos.x ¬ MAX[tRef.pos.x-tRef.charDX, 0]; ClearArea[tRef.connection, tRef.window, [tRef.pos.x, MAX[tRef.pos.y, tRef.ascent]-tRef.ascent], [tRef.charDX, tRef.lineDY], FALSE]; }; ENDCASE => { pos: Point; IF INT[tRef.pos.x+tRef.charDX+tRef.rightSpace]>INT[tRef.wSize.width] AND tRef.pos.x>tRef.leftSpace THEN NewLine[tRef]; pos ¬ PosAndIncrement[tRef, tRef.charDX]; Xl.ImageChar[tRef.connection, tRef.window.drawable, pos, tRef.gc, ch]; }; }; NewLine: PROC [tr: REF TextRecord] = { mustScroll: INT ¬ AdvanceLine[tr]; IF mustScroll>0 AND tr.alive THEN { y: INT; <<--scrollup>> Xl.CopyArea[c: tr.connection, src: tr.window.drawable, dst: tr.window.drawable, srcP: [0, mustScroll], dstP: [0, 0], size: [tr.wSize.width, tr.wSize.height-mustScroll], gc: tr.gc]; <<--clear bottom>> y ¬ MAX[tr.pos.y, tr.ascent]-tr.ascent; ClearArea[tr.connection, tr.window, [0, y], [2000, 2000]]; }; }; AdvanceLine: ENTRY PROC [tr: REF TextRecord] RETURNS [mustScroll: INT¬0] = { ENABLE UNWIND => NULL; tr.pos.y ¬ tr.pos.y+tr.lineDY; tr.pos.x ¬ tr.leftSpace; IF INT[tr.pos.y+tr.bottomOffset] > INT[tr.wSize.height] THEN { mustScroll ¬ MIN[MAX[tr.wSize.height/4, tr.lineDY], tr.wSize.height]; tr.pos.y ¬ tr.pos.y - mustScroll; } }; outputStreamProcs: REF IO.StreamProcs ¬ IO.CreateStreamProcs[ variety: $output, class: $XlTexts, putChar: OutputTextWindowStreamPutChar, putBlock: OutputTextWindowStreamPutBlock, eraseChar: OutputTextWindowStreamEraseChar ]; OutputTextWindowStreamPutChar: PROC [self: IO.STREAM, char: CHAR] = { ENABLE Xl.XError, Failed => {ERROR IO.Error[StreamClosed, self]}; tRef: REF TextRecord = NARROW[self.streamData]; IF tRef.window=nullWindow THEN RETURN; UnflushedOutChar[tRef, char]; Flush[tRef.connection, 200]; }; OutputTextWindowStreamPutBlock: PROC [self: IO.STREAM, block: REF READONLY TEXT, startIndex: NAT, count: NAT] = { ENABLE Xl.XError, Failed => {ERROR IO.Error[StreamClosed, self]}; tRef: REF TextRecord = NARROW[self.streamData]; Action: PROC[c: CHAR] RETURNS [BOOL¬FALSE] = {UnflushedOutChar[tRef, c]}; IF tRef.window=nullWindow THEN RETURN; [] ¬ RefText.Map[s: block, action: Action, len: count, start: startIndex]; Flush[tRef.connection, 200]; }; OutputTextWindowStreamEraseChar: PROC [self: IO.STREAM, char: CHAR] = { OutputTextWindowStreamPutChar[self, Ascii.BS] }; OutputStream: PUBLIC PROC [handle: Handle] RETURNS [s: IO.STREAM] = { RETURN[IO.CreateStream[streamProcs: outputStreamProcs, streamData: handle]]; }; END.