DIRECTORY Ascii, CardTab, FanoutStream, IO, KeySymsKB, KeyTypes, KeySymsOSF, KeySymsSun, KeySymsHP, Process, RefText, Rope, Xl, XlAscii, XlCursor, XlCutBuffers, XTk, XTkEditWidgets, XTkFriends, XTkInputFocus, XTkXBiScroller, XTkWidgets; XTkEditWidgetsImpl: CEDAR MONITOR IMPORTS CardTab, FanoutStream, IO, Process, RefText, Rope, Xl, XlAscii, XlCursor, XlCutBuffers, XTk, XTkFriends, XTkInputFocus, XTkXBiScroller, XTkWidgets EXPORTS XTkEditWidgets = BEGIN SetSelection: PUBLIC PROC [widget: XTk.Widget, start: INT ¬ FIRST[INT], length: INT ¬ FIRST[INT], where: XTkEditWidgets.SelectionLocation ¬ before] ~ { oRef: REF OutputRec ¬ GetOutputData[widget]; IF oRef = NIL THEN RETURN; StartBlink[oRef]; [] ¬ InvertSelection[oRef, oRef.selStart, oRef.selEnd]; IF start < 0 THEN { lData: LineData ¬ GetLineData[oRef, oRef.lastLine]; oRef.pos.y ¬ oRef.topOffset + oRef.lastLine * oRef.lineDY; oRef.pos.x ¬ oRef.leftSpace + lData.text.Length[] * oRef.charDX; oRef.selStart ¬ [oRef.leftSpace, oRef.topOffset]; oRef.selEnd ¬ oRef.pos; [] ¬ InvertSelection[oRef, oRef.selStart, oRef.selEnd]; RETURN; } ELSE { charsSeen: INT ¬ 0; startLine : CARD ¬ LAST[CARD]; endLine : CARD ¬ LAST[CARD]; startChar: INT ¬ FIRST[INT]; endChar: INT ¬ FIRST[INT]; lData: LineData; FOR x: CARD IN [0..oRef.lastLine] DO lData ¬ GetLineData[oRef, x]; charsSeen ¬ charsSeen + lData.text.Length[]; IF charsSeen > start AND startLine = LAST[CARD] THEN { startLine ¬ x; startChar ¬ start - charsSeen; }; IF length >= 0 THEN IF charsSeen > (start+length) AND endLine = LAST[CARD] THEN { endLine ¬ x; endChar ¬ start+length - charsSeen; }; ENDLOOP; IF startLine = LAST[CARD] THEN { SetSelection[widget]; RETURN; }; IF endLine = LAST[CARD] THEN { endLine ¬ oRef.lastLine; lData ¬ GetLineData[oRef, endLine]; endChar ¬ lData.text.Length[]; }; oRef.selStart ¬ [oRef.leftSpace + startChar * oRef.charDX, oRef.topOffset + startLine * oRef.lineDY]; oRef.selEnd ¬ [oRef.leftSpace + endChar * oRef.charDX, oRef.topOffset + endLine * oRef.lineDY]; IF where = after THEN oRef.pos ¬ oRef.selEnd ELSE oRef.pos ¬ oRef.selStart; [] ¬ InvertSelection[oRef, oRef.selStart, oRef.selEnd]; }; }; SetText: PUBLIC PROC [widget: XTk.Widget, text: Rope.ROPE] RETURNS [BOOL ¬ TRUE] ~ { oRef: REF OutputRec ¬ GetOutputData[widget]; IF oRef = NIL THEN RETURN[FALSE]; oRef.refreshing ¬ FALSE; FOR x: CARD DECREASING IN [1..oRef.lastLine] DO RemoveLine[oRef, x]; ENDLOOP; Xl.ClearArea[ c: oRef.widget.connection, window: oRef.widget.window, pos: [0, oRef.topOffset - oRef.ascent], size: [oRef.wSize.width, oRef.lineDY]]; [] ¬ CardTab.Store[oRef.lineTab, 0, NEW[LineDataRec ¬ ["", FALSE]]]; oRef.lastLine ¬ 0; oRef.currentLine ¬ 0; HomePos[oRef]; oRef.followCursor ¬ FALSE; Show[oRef.widget, text]; oRef.refreshing ¬ TRUE; oRef.followCursor ¬ TRUE; AdjustPos[oRef]; Redraw[oRef]; }; GetText: PUBLIC PROC [widget: XTk.Widget] RETURNS [text: Rope.ROPE ¬ "", did: BOOL ¬ TRUE] ~ { oRef: REF OutputRec ¬ GetOutputData[widget]; lData: LineData; prevLineLinked: BOOL ¬ TRUE; IF oRef = NIL THEN RETURN["", FALSE]; FOR loop: CARD IN [0..oRef.lastLine] DO lData ¬ GetLineData[oRef, loop]; IF prevLineLinked THEN text ¬ text.Concat[lData.text] ELSE text ¬ text.Cat["\n", lData.text]; prevLineLinked ¬ lData.linkedToNext; ENDLOOP; }; SetWindowSize: PROC [oRef: REF OutputRec, s: Xl.Size] = { IF oRef#NIL THEN oRef.wSize ¬ [s.width, IF oRef.scrollable THEN oRef.scrollWidget.actual.size.height ELSE s.height] }; HomePos: PROC [oRef: REF OutputRec] = { IF oRef#NIL THEN { oRef.pos.x ¬ oRef.leftSpace; oRef.pos.y ¬ oRef.topOffset; }; }; PosAndIncrement: PROC [oRef: REF OutputRec, rr: INT] RETURNS [pos: Xl.Point ¬ [0, 0]] = { IF oRef#NIL THEN { pos ¬ oRef.pos; oRef.pos.x ¬ oRef.pos.x+rr; }; }; GetLineData: PROC [oRef: REF OutputRec, line: CARD ¬ LAST[CARD]] RETURNS [lData: LineData ¬ NIL] ~ { data: REF; found: BOOLEAN; IF line = LAST[CARD] THEN line ¬ oRef.currentLine; [found, data] ¬ CardTab.Fetch[oRef.lineTab, line]; IF ~found THEN ERROR; lData ¬ NARROW[data]; }; UnflushedOutChar: ENTRY PROC [oRef: REF OutputRec, ch: CHAR] = { w: XTk.Widget ~ oRef.widget; lData: LineData; IF w.fastAccessAllowed#ok THEN RETURN; IF ~oRef.acceptKeyboard THEN { EraseMark[oRef]; oRef.pos.y ¬ oRef.topOffset + oRef.lineDY * oRef.lastLine; oRef.currentLine ¬ oRef.lastLine; oRef.pos.x ¬ FinalCharPos[oRef]; DrawMark[oRef]; }; lData ¬ GetLineData[oRef]; SuspendMark[oRef]; [] ¬ InvertSelection[oRef, oRef.selStart, oRef.selEnd]; SELECT ch FROM Ascii.CR, Ascii.LF => { IF CheckShove[oRef] THEN NewLine[oRef]; }; Ascii.NUL, Ascii.DEL => { IF oRef.selStart # oRef.selEnd THEN { sX, eX: INT; sY, eY: CARD; [sX, sY] ¬ PosFromPoint[oRef, oRef.selStart]; [eX, eY] ¬ PosFromPoint[oRef, oRef.selEnd]; oRef.pos ¬ oRef.selStart; [sX, sY, eX, eY] ¬ OrderPos[sX, sY, eX, eY]; IF eY > sY THEN { lData: LineData¬ GetLineData[oRef, sY]; FOR x: CARD DECREASING IN [1..eX] DO [] ¬ RemovePrevChar[oRef, eY, x]; ENDLOOP; FOR line: CARD DECREASING IN (sY..eY) DO RemoveLine[oRef, line]; ENDLOOP; FOR x: CARD DECREASING IN (sX..lData.text.Length[]] DO [] ¬ RemovePrevChar[oRef, sY, x]; ENDLOOP; [] ¬ RemovePrevChar[oRef,sY+1, 0]; } ELSE FOR x: CARD DECREASING IN (sX..eX] DO [] ¬ RemovePrevChar[oRef, sY, x]; ENDLOOP; }; }; Ascii.BS => { charPos: INT ¬ ((oRef.pos.x-oRef.leftSpace) / oRef.charDX); prevL, delP: BOOL; c: CHAR; prevX: INT ¬ 0; IF oRef.currentLine # 0 THEN { prevData: LineData ¬ GetLineData[oRef, oRef.currentLine - 1]; prevX ¬ oRef.leftSpace + oRef.charDX * prevData.text.Length[]; }; IF oRef.currentLine # 0 OR oRef.pos.x # oRef.leftSpace THEN { oRef.pos.x ¬ oRef.pos.x - oRef.charDX; [c, delP, prevL] ¬ RemovePrevChar[oRef, oRef.currentLine, charPos]; IF prevL THEN { oRef.pos.x ¬ prevX; oRef.pos.y ¬ oRef.pos.y - oRef.lineDY; IF delP THEN oRef.pos.x ¬ oRef.pos.x - oRef.charDX; oRef.currentLine ¬ oRef.currentLine - 1; }; }; }; ENDCASE => { charPos: INT ¬ ((oRef.pos.x-oRef.leftSpace) / oRef.charDX); wrapped, did: BOOLEAN; [wrapped, did] ¬ AddCharToLine[oRef, ch, oRef.currentLine, charPos]; IF ~did THEN RETURN; oRef.pos.x ¬ oRef.pos.x + oRef.charDX; IF wrapped THEN { oRef.pos.x ¬ oRef.leftSpace + oRef.charDX; oRef.pos.y ¬ oRef.pos.y + oRef.lineDY; oRef.currentLine ¬ oRef.currentLine + 1; }; }; AdjustPos[oRef]; EnableMark[oRef]; oRef.selStart ¬ oRef.selEnd ¬ oRef.pos; }; OrderPos: PROC [startX: INT, startY: CARD, endX: INT, endY: CARD] RETURNS [lowX: INT, lowY: CARD, highX: INT, highY: CARD] ~ { IF (endY < startY) OR (endY = startY AND endX < startX) THEN RETURN [endX, endY, startX, startY]; RETURN[startX, startY, endX, endY]; }; AdjustPos: PROCEDURE [oRef: REF OutputRec] ~ { IF oRef.scrollable AND oRef.followCursor THEN { where: INT ¬ - XTkXBiScroller.GetState[oRef.scrollWidget].y; diff: INT ¬ oRef.pos.y - (where + oRef.wSize.height); tooHigh: INT ¬ where - oRef.pos.y ; IF diff > -oRef.descent THEN XTkXBiScroller.PublicSetState[oRef.scrollWidget, [0, -(where + (oRef.descent + diff + 4))]] ELSE IF tooHigh > -oRef.ascent THEN XTkXBiScroller.PublicSetState[oRef.scrollWidget, [0, -(where - (oRef.ascent - tooHigh))]]; }; }; SuspendMark: PROCEDURE [oRef: REF OutputRec] ~ { EraseMark[oRef]; oRef.showMark ¬ FALSE; }; InvertMark: PROCEDURE [oRef: REF OutputRec] ~ { IF oRef.showMark AND oRef.widget.fastAccessAllowed=ok THEN IF oRef.markOn THEN EraseMark[oRef] ELSE DrawMark[oRef]; }; EraseMark: PROCEDURE [oRef: REF OutputRec] ~ { IF oRef.markOn AND oRef.widget.fastAccessAllowed=ok THEN { Xl.SetGCFunction[oRef.gc, xor]; DoDraw[oRef, oRef.pos.x, oRef.pos.y]; oRef.markOn ¬ FALSE; Xl.SetGCFunction[oRef.gc, copy]; }; }; EnableMark: PROCEDURE [oRef: REF OutputRec] ~ { oRef.showMark ¬ TRUE; DrawMark[oRef]; }; DoDraw: PROC [oRef: REF OutputRec, x, y: INT] ~ { Xl.DrawLine[c: oRef.widget.connection, drawable: oRef.widget.window.drawable, gc: oRef.gc, p1: [x,y+1], p2: [x,y+4]]; Xl.DrawLine[c: oRef.widget.connection, drawable: oRef.widget.window.drawable, gc: oRef.gc, p1: [x-1,y+2], p2: [x-1,y+2]]; Xl.DrawLine[c: oRef.widget.connection, drawable: oRef.widget.window.drawable, gc: oRef.gc, p1: [x+1,y+2], p2: [x+1,y+2]]; DoFlush[oRef.widget] }; DrawMark: PROCEDURE [oRef: REF OutputRec] ~ { IF ~oRef.markOn AND oRef.showMark AND oRef.blinking­ AND oRef.widget.fastAccessAllowed=ok THEN { Xl.SetGCFunction[oRef.gc, xor]; DoDraw[oRef, oRef.pos.x, oRef.pos.y]; oRef.markOn ¬ TRUE; Xl.SetGCFunction[oRef.gc, copy]; }; }; RemovePrevChar: PROC [oRef: REF OutputRec, line: CARD ¬ LAST[CARD], where: INT ¬ FIRST[INT]] RETURNS [ch: CHAR, deletedOnPrevLine, charOnPrevLine: BOOL ¬ FALSE] ~ { w: XTk.Widget ~ oRef.widget; pos: Xl.Point; lData: LineData ¬ GetLineData[oRef, line]; text: Rope.ROPE ¬ lData.text; linked: BOOL ¬ lData.linkedToNext; prevLData: LineData ¬ IF line # 0 THEN GetLineData[oRef, line-1] ELSE NIL; IF line = LAST[CARD] THEN line ¬ oRef.currentLine; IF where < 0 OR where > lData.text.Length[] THEN where ¬ lData.text.Length[]; pos.x ¬ oRef.leftSpace + where * oRef.charDX; pos.y ¬ oRef.topOffset + line * oRef.lineDY; IF where = 0 AND line # 0 THEN { --last char of prev line charOnPrevLine ¬ TRUE; IF prevLData.linkedToNext THEN { deletedOnPrevLine ¬ TRUE; [] ¬ RemovePrevChar[oRef, line-1]; } ELSE { prevLData.linkedToNext ¬ TRUE; FixLine[oRef, line-1]; } } ELSE IF ~(line = 0 AND where = 0) THEN { -- some random character in line ch ¬ lData.text.Fetch[where-1]; lData.text ¬ lData.text.Replace[where-1, 1, ""]; Xl.ClearArea[ c: w.connection, window: w.window, pos: [pos.x - oRef.charDX, pos.y - oRef.ascent], size: [oRef.charDX , oRef.lineDY] ]; Xl.CopyArea[ c: w.connection, src: w.window.drawable, dst: w.window.drawable, srcP: [pos.x, pos.y - oRef.ascent], dstP: [pos.x - oRef.charDX, pos.y - oRef.ascent], size: [oRef.wSize.width, oRef.lineDY], gc: oRef.gc]; IF linked THEN { c: CHAR; nextLData: LineData ¬ GetLineData[oRef, line+1]; IF ~nextLData.text.IsEmpty[] THEN { [c,,] ¬ RemovePrevChar[oRef, line+1, 1]; IF AddCharToLine[oRef, c, line].thisCharWrapped THEN ERROR; }; IF nextLData.text.IsEmpty[] THEN { RemoveLine[oRef, line+1]; lData.linkedToNext ¬ FALSE; }; }; }; }; AddCharToLine: PROC [oRef: REF OutputRec, ch: CHAR, line: CARD ¬ LAST[CARD], where: INT ¬ FIRST[INT]] RETURNS [thisCharWrapped: BOOL ¬ FALSE, did: BOOL ¬ TRUE] ~ { w: XTk.Widget ~ oRef.widget; pos: Xl.Point; c: CHAR; lData: LineData ¬ GetLineData[oRef, line]; IF where < 0 THEN where ¬ lData.text.Length[]; IF ~CheckLineSpace[oRef] THEN { did ¬ FALSE; RETURN; }; pos.x ¬ oRef.leftSpace + where * oRef.charDX; pos.y ¬ oRef.topOffset + line * oRef.lineDY; IF where # lData.text.Length[] THEN { Xl.CopyArea[ c: w.connection, src: w.window.drawable, dst: w.window.drawable, srcP: [pos.x, pos.y - oRef.ascent], dstP: [pos.x + oRef.charDX, pos.y - oRef.ascent], size: [oRef.wSize.width, oRef.lineDY], gc: oRef.gc]; }; Xl.ImageChar[w.connection, w.window.drawable, pos, oRef.gc, ch]; lData.text ¬ lData.text.Replace[where, 0, Rope.FromChar[ch]]; IF oRef.leftSpace + lData.text.Length[] * oRef.charDX + oRef.rightSpace > oRef.wSize.width THEN { IF where = lData.text.Length[] - 1 THEN thisCharWrapped ¬ TRUE; Xl.ClearArea[ c: w.connection, window: w.window, pos: [oRef.leftSpace + (lData.text.Length[] - 1) * oRef.charDX, pos.y - oRef.ascent], size: [oRef.charDX , oRef.lineDY] ]; c ¬ lData.text.Fetch[lData.text.Length[] - 1]; lData.text ¬ lData.text.Replace[lData.text.Length[] - 1, 1, ""]; IF ~lData.linkedToNext THEN { ShoveDown[oRef, line+1]; [] ¬ CardTab.Store[oRef.lineTab, line+1, NEW[LineDataRec ¬ ["", FALSE]]]; lData.linkedToNext ¬ TRUE; }; [] ¬ AddCharToLine[oRef, c, line+1, 0] }; }; CheckLineSpace: PROC [oRef: REF OutputRec, line: CARD ¬ LAST[CARD]] RETURNS [BOOL ¬ FALSE] ~ { lData: LineData; IF line = LAST[CARD] THEN line ¬ oRef.currentLine; lData ¬ GetLineData[oRef, line]; IF oRef.leftSpace + (lData.text.Length[] + 1) * oRef.charDX + oRef.rightSpace > oRef.wSize.width THEN IF lData.linkedToNext THEN RETURN[CheckLineSpace[oRef, line+1]] ELSE RETURN CheckShove[oRef]; RETURN[TRUE]; }; CheckShove: PROC [oRef: REF OutputRec, line: CARD ¬ LAST[CARD]] RETURNS [BOOL] ~ { l: INT ¬ IF line>=LAST[INT] THEN oRef.lastLine + 1 ELSE line; IF ~oRef.scrollable AND oRef.topOffset + l * oRef.lineDY + oRef.bottomOffset > oRef.wSize.height THEN RETURN[FALSE]; RETURN[TRUE]; }; RemoveLine: PROC [oRef: REF OutputRec, line: CARD] = { w: XTk.Widget ~ oRef.widget; pos: Xl.Point ¬ [0, oRef.topOffset + line * oRef.lineDY]; IF oRef.refreshing THEN { Xl.CopyArea[ c: w.connection, src: w.window.drawable, dst: w.window.drawable, srcP: [pos.x, pos.y + oRef.lineDY - oRef.ascent], dstP: [pos.x, pos.y - oRef.ascent], size: [oRef.wSize.width, oRef.backingSize.height], gc: oRef.gc]; Xl.ClearArea[ c: w.connection, window: w.window, pos: [0, oRef.topOffset + oRef.lastLine * oRef.lineDY - oRef.ascent], size: [oRef.wSize.width, oRef.lineDY]]; }; FOR loop: CARD IN [line+1..oRef.lastLine] DO lData: LineData ¬ GetLineData[oRef, loop]; [] ¬ CardTab.Store[oRef.lineTab, loop-1, lData]; ENDLOOP; IF oRef.lastLine # 0 THEN oRef.lastLine ¬ oRef.lastLine - 1; GrowBacking[oRef, -1]; }; sbts: INT ¬ 10; --scrollbar total size GrowBacking: PROC [oRef: REF OutputRec, increment: INT ¬ 1] ~ { g: Xl.Geometry ¬ oRef.widget.actual; increaseSize: INT ¬ increment * oRef.lineDY; IF oRef.backingSize.height + increaseSize >= oRef.wSize.height THEN { g.size.height ¬ g.size.height + increaseSize; oRef.backingSize.height ¬ oRef.backingSize.height + increaseSize; oRef.widget.s.geometry.size.height ¬ g.size.height; IF oRef.scrollable THEN { child: XTk.Widget ¬ XTkXBiScroller.Child[oRef.scrollWidget]; geometry: Xl.Geometry ¬ []; geometry.size.width ¬ child.parent.actual.size.width-sbts; geometry.size.height ¬ oRef.backingSize.height; XTk.NoteAndStartReconfigure[child, geometry]; }; oRef.pleaseReconfigure ¬ oRef.widget; }; }; ShoveDown: PROC [oRef: REF OutputRec, startLine: CARD] = { w: XTk.Widget ~ oRef.widget; pos: Xl.Point ¬ [0, oRef.topOffset + startLine * oRef.lineDY]; size: CARD ¬ (oRef.lastLine + 1) * oRef.lineDY; IF startLine<= oRef.lastLine THEN { Xl.CopyArea[ c: w.connection, src: w.window.drawable, dst: w.window.drawable, srcP: [pos.x, pos.y - oRef.ascent], dstP: [pos.x, pos.y + oRef.lineDY - oRef.ascent], size: [oRef.wSize.width, oRef.backingSize.height - pos.y - oRef.ascent], gc: oRef.gc]; Xl.ClearArea[ c: w.connection, window: w.window, pos: [0, pos.y - oRef.ascent], size: [oRef.wSize.width, oRef.lineDY]]; FOR loop: CARD DECREASING IN [startLine..oRef.lastLine] DO lData: LineData ¬ GetLineData[oRef, loop]; [] ¬ CardTab.Store[oRef.lineTab, loop+1, lData]; ENDLOOP; } ELSE [] ¬ CardTab.Store[oRef.lineTab, oRef.lastLine+1, NEW[LineDataRec ¬ ["", FALSE]]]; oRef.lastLine ¬ oRef.lastLine + 1; IF oRef.topOffset + size + oRef.bottomOffset > CARD[oRef.backingSize.height] THEN { GrowBacking[oRef]; }; }; NewLine: PROC [oRef: REF OutputRec] = { w: XTk.Widget ~ oRef.widget; nextLineText: Rope.ROPE ¬ ""; lData: LineData; doGrow: BOOL ¬ FALSE; IF oRef.currentLine # oRef.lastLine THEN ShoveDown[oRef, oRef.currentLine + 1] ELSE { size: CARD ¬ (oRef.lastLine + 1) * oRef.lineDY; oRef.lastLine ¬ oRef.lastLine + 1; IF oRef.topOffset + size + oRef.bottomOffset > CARD[oRef.backingSize.height] THEN { doGrow ¬ TRUE; }; }; lData ¬ GetLineData[oRef]; IF oRef.pos.x # oRef.leftSpace + oRef.charDX * (lData.text.Length[]) THEN { Xl.CopyArea[ c: w.connection, src: w.window.drawable, dst: w.window.drawable, srcP: [oRef.pos.x, oRef.pos.y - oRef.ascent], dstP: [oRef.leftSpace, oRef.pos.y + oRef.lineDY - oRef.ascent], size: [oRef.wSize.width - oRef.pos.x, oRef.lineDY], gc: oRef.gc ]; Xl.ClearArea[ c: w.connection, window: w.window, pos: [oRef.pos.x, oRef.pos.y - oRef.ascent], size: [oRef.wSize.width - oRef.pos.x, oRef.lineDY]]; nextLineText ¬ lData.text.Substr[(oRef.pos.x - oRef.leftSpace) / oRef.charDX]; lData.text ¬ lData.text.Substr[0, (oRef.pos.x - oRef.leftSpace) / oRef.charDX]; }; oRef.currentLine ¬ oRef.currentLine+1; [] ¬ CardTab.Store[oRef.lineTab, oRef.currentLine, NEW[LineDataRec ¬ [nextLineText, lData.linkedToNext]]]; oRef.pos ¬ [oRef.leftSpace, oRef.pos.y + oRef.lineDY]; lData.linkedToNext ¬ FALSE; FixLine[oRef, oRef.currentLine]; IF doGrow THEN GrowBacking[oRef]; }; FixLine: PROC [oRef: REF OutputRec, line: CARD] = { lineChecking: CARD ¬ line; done: BOOL ¬ FALSE; w: XTk.Widget ~ oRef.widget; WHILE ~done DO lineData: LineData ¬ GetLineData[oRef, lineChecking]; IF lineChecking # oRef.lastLine AND lineData.linkedToNext AND oRef.leftSpace + lineData.text.Length[] * oRef.charDX + oRef.rightSpace <= oRef.wSize.width THEN { spaceLeft: INT ¬ (oRef.wSize.width - (lineData.text.Length[] * oRef.charDX + oRef.leftSpace + oRef.rightSpace)) / oRef.charDX; nextLine: LineData ¬ GetLineData[oRef, lineChecking+1]; nText: Rope.ROPE ¬ nextLine.text; charsAvail: INT ¬ nText.Length[]; IF charsAvail <= spaceLeft THEN { -- copy whole next line up, and done. done ¬ TRUE; Xl.CopyArea[ c: w.connection, src: w.window.drawable, dst: w.window.drawable, srcP: [oRef.leftSpace, oRef.topOffset + (lineChecking+1) * oRef.lineDY - oRef.ascent], dstP: [oRef.leftSpace + lineData.text.Length[] * oRef.charDX, oRef.topOffset + lineChecking * oRef.lineDY - oRef.ascent], size: [charsAvail * oRef.charDX, oRef.lineDY], gc: oRef.gc]; lineData.text ¬ lineData.text.Concat[nText]; RemoveLine[oRef, lineChecking+1]; lineData.linkedToNext ¬ FALSE; } ELSE { Xl.CopyArea[ c: w.connection, src: w.window.drawable, dst: w.window.drawable, srcP: [oRef.leftSpace, oRef.topOffset + (lineChecking+1) * oRef.lineDY - oRef.ascent], dstP: [oRef.leftSpace + lineData.text.Length[] * oRef.charDX, oRef.topOffset + lineChecking * oRef.lineDY - oRef.ascent], size: [spaceLeft * oRef.charDX, oRef.lineDY], gc: oRef.gc]; lineData.text¬ lineData.text.Concat[nText.Substr[0, spaceLeft]]; nextLine.text ¬ nextLine.text.Replace[0, spaceLeft, ""]; Xl.CopyArea[ c: w.connection, src: w.window.drawable, dst: w.window.drawable, dstP: [oRef.leftSpace, oRef.topOffset + (lineChecking+1) * oRef.lineDY - oRef.ascent], srcP: [oRef.leftSpace + spaceLeft * oRef.charDX, oRef.topOffset + (lineChecking + 1) * oRef.lineDY - oRef.ascent], size: [(charsAvail - spaceLeft) * oRef.charDX, oRef.lineDY], gc: oRef.gc]; Xl.ClearArea[ c: w.connection, window: w.window, pos: [oRef.leftSpace + (charsAvail - spaceLeft) * oRef.charDX, oRef.topOffset + (lineChecking + 1) * oRef.lineDY - oRef.ascent], size: [spaceLeft * oRef.charDX, oRef.lineDY]]; } } ELSE done ¬ TRUE; lineChecking ¬ lineChecking + 1; ENDLOOP; }; outputStreamProcs: REF IO.StreamProcs ¬ IO.CreateStreamProcs[ variety: $output, class: $XlTexts, putChar: OutputTextWindowStreamPutChar, putBlock: OutputTextWindowStreamPutBlock, eraseChar: OutputTextWindowStreamEraseChar ]; OutputTextWindowStreamPutChar: PROC [self: IO.STREAM, char: CHAR] = { ENABLE UNCAUGHT => GOTO Oops; oRef: REF OutputRec ~ NARROW[self.streamData]; w: XTk.Widget ~ oRef.widget; IF w.fastAccessAllowed#ok THEN RETURN; UnflushedOutChar[oRef, char]; DoFlush[w]; EXITS Oops => {} }; OutputTextWindowStreamPutBlock: PROC [self: IO.STREAM, block: REF READONLY TEXT, startIndex: NAT, count: NAT] = { ENABLE UNCAUGHT => GOTO Oops; oRef: REF OutputRec ~ NARROW[self.streamData]; w: XTk.Widget ~ oRef.widget; Action: PROC[c: CHAR] RETURNS [BOOL¬FALSE] = {UnflushedOutChar[oRef, c]}; IF w.fastAccessAllowed#ok THEN RETURN; [] ¬ RefText.Map[s: block, action: Action, len: count, start: startIndex]; DoFlush[w]; EXITS Oops => {} }; OutputTextWindowStreamEraseChar: PROC [self: IO.STREAM, char: CHAR] = { OutputTextWindowStreamPutChar[self, Ascii.BS] }; outputClass: XTk.ImplementorClass ¬ XTkFriends.CreateClass[[key: $EditStreamWidget, wDataNum: 1, classNameHint: $Typescript, configureLR: Configure, initInstPart: StreamInitInstPart, forgetScreenLR: ForgetScreen]]; OutputRec: TYPE = RECORD [ widget: XTk.Widget, --backpointer for liveness scrollWidget: XTk.Widget, driblee: IO.STREAM ¬ NIL, font: Xl.Font ¬ Xl.nullFont, wSize: Xl.Size ¬ [0, 0], --size of window backingSize: Xl.Size ¬ [0, 0], --size of scrollable part...only height is used. 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: CARD ¬ 2, --distance between baseline and border ascent, descent: NAT ¬ 2, charDX: NAT ¬ 10, lineSpace: CARD ¬ 2, pos: Xl.Point ¬ [2, 2], --position for next character; not yet clipped lastLine: CARD ¬ 0, --position for next character; not yet clipped gc: Xl.GContext ¬ NIL, --could we somehow share gc's? currentLine: CARD ¬ 0, data: Rope.ROPE ¬ NIL, lineTab: CardTab.Ref, markPixmap: Xl.Pixmap, showMark: BOOL ¬ TRUE, startedBlinking: BOOL ¬ FALSE, markOn: BOOL ¬ FALSE, buttonOneDown:REF BOOL ¬ NEW[BOOL ¬ FALSE], buttonTwoDown:REF BOOL ¬ NEW[BOOL ¬ FALSE], buttonThreeDown:REF BOOL ¬ NEW[BOOL ¬ FALSE], selStart, selEnd: Xl.Point ¬ [2, 2], blinking: REF BOOL ¬ NEW[BOOL ¬ FALSE], scrollable: BOOL ¬ FALSE, backHeight: INT ¬ FIRST[INT], acceptKeyboard: BOOL ¬ TRUE, pleaseReconfigure: XTk.Widget ¬ NIL, followCursor: BOOL ¬ TRUE, refreshing: BOOL ¬ TRUE ]; LineData: TYPE ~ REF LineDataRec ¬ NIL; LineDataRec: TYPE ~ RECORD [ text: Rope.ROPE ¬ "", linkedToNext: BOOL ¬ FALSE ]; sampleORec: REF OutputRec ¬ NEW[OutputRec]; GetOutputData: PROC [w: XTk.Widget] RETURNS [REF OutputRec] = INLINE { data: REF ¬ XTkFriends.InstPart[w, outputClass]; WITH data SELECT FROM oRec: REF OutputRec => RETURN[oRec]; ENDCASE => { data ¬ XTk.GetWidgetProp[w, sampleORec]; IF data = NIL THEN RETURN[NIL] ELSE RETURN[NARROW[data]]; }; }; Configure: XTk.ConfigureProc = { oRef: REF OutputRec ~ GetOutputData[widget]; existW: BOOL ¬ widget.actualMapping sY THEN { prevLineLinked: BOOL ¬ TRUE; lineData: LineData ¬ GetLineData[oRef, sY]; Xl.SetGCFunction[oRef.gc, xor]; DoRect[oRef, sX, sY, lineData.text.Length[], sY+1]; text ¬ text.Concat[lineData.text.Substr[sX]]; prevLineLinked ¬ lineData.linkedToNext; DoRect[oRef, 0, eY, eX, eY+1]; FOR line: CARD IN (sY..eY) DO lineData ¬ GetLineData[oRef, line]; DoRect[oRef, 0, line, lineData.text.Length[], line+1]; IF prevLineLinked THEN text ¬ text.Concat[lineData.text] ELSE text ¬ text.Cat["\n", lineData.text]; prevLineLinked ¬ lineData.linkedToNext ENDLOOP; lineData ¬ GetLineData[oRef, eY]; IF prevLineLinked THEN text ¬ text.Concat[lineData.text.Substr[0, eX]] ELSE text ¬ text.Cat["\n", lineData.text.Substr[0, eX]]; Xl.SetGCFunction[oRef.gc, copy]; } ELSE IF eX # sX THEN { lineData: LineData ¬ GetLineData[oRef, sY]; Xl.SetGCFunction[oRef.gc, xor]; DoRect[oRef, sX, sY, eX, sY+1]; text ¬ lineData.text.Substr[sX, eX-sX+1]; Xl.SetGCFunction[oRef.gc, copy]; }; }; ExtendProc: PROCEDURE [oRef: REF OutputRec, trigger: REF BOOL] ~ { WHILE trigger­ DO tmpStart: Xl.Point ¬ oRef.selStart; tmpEnd: Xl.Point ¬ oRef.selEnd; oRef.selEnd ¬ Xl.QueryPointer[oRef.widget.connection, oRef.widget.window].pos; [] ¬ InvertSelection[oRef, tmpStart, tmpEnd]; XlCutBuffers.Put[oRef.widget.connection, InvertSelection[oRef, oRef.selStart, oRef.selEnd]]; ENDLOOP; }; PosFromPoint: PROCEDURE [oRef: REF OutputRec, point: Xl.Point] RETURNS [x: INT, y: CARD] ~ { tmpY: INT ¬ (point.y + oRef.ascent - oRef.topOffset) / oRef.lineDY; tmpX: INT ¬ (point.x + oRef.charDX / 2 - oRef.leftSpace) / oRef.charDX; lData: LineData; y ¬ IF tmpY < 0 THEN 0 ELSE tmpY; IF y > oRef.lastLine THEN y ¬ oRef.lastLine; lData ¬ GetLineData[oRef, y]; x ¬ IF tmpX < 0 THEN 0 ELSE tmpX; IF x > lData.text.Length[] THEN x ¬ lData.text.Length[]; }; SelectProc: PROCEDURE [oRef: REF OutputRec, trigger: REF BOOL] ~ { [] ¬ InvertSelection[oRef, oRef.selStart, oRef.selEnd]; oRef.selStart ¬ oRef.pos; oRef.selEnd ¬ oRef.pos; Process.PauseMsec[150]; ExtendProc[oRef, trigger]; }; HandleButtonPress: PROCEDURE [event: Xl.Event, widget: XTk.Widget] ~ { bp: Xl.ButtonPressEvent ¬ NARROW[event]; oRef: REF OutputRec ~ GetOutputData[widget]; tmpY: INT ¬ (bp.pos.y + oRef.ascent - oRef.topOffset) / oRef.lineDY; yLines: CARD; xChar: INT; lData: LineData; sProcess: PROCESS ¬ NIL; p: Xl.PointerMapping ¬ Xl.GetPointerMapping[widget.connection]; changePos: BOOL ¬ FALSE; [xChar, yLines] ¬ PosFromPoint[oRef, bp.pos]; StartBlink[oRef]; SELECT p[bp.button] FROM 1 => { oRef.buttonOneDown­ ¬ TRUE; changePos¬ TRUE; }; 2 => oRef.buttonTwoDown­ ¬ TRUE; 3 => oRef.buttonThreeDown­ ¬ TRUE; ENDCASE => {}; IF changePos THEN { t: CARD _ tmpY; IF t > oRef.lastLine THEN xChar ¬ LAST[INT]; lData ¬ GetLineData[oRef, yLines]; IF xChar > lData.text.Length[] THEN xChar ¬ lData.text.Length[]; EraseMark[oRef]; oRef.pos ¬ [oRef.leftSpace + xChar * oRef.charDX, oRef.topOffset + yLines * oRef.lineDY]; DrawMark[oRef]; oRef.currentLine ¬ yLines; DoFlush[oRef.widget]; }; SELECT p[bp.button] FROM 1 => sProcess ¬ FORK SelectProc[oRef, oRef.buttonOneDown]; 2 => sProcess ¬ FORK SelectProc[oRef, oRef.buttonTwoDown]; 3 => sProcess ¬ FORK ExtendProc[oRef, oRef.buttonThreeDown]; ENDCASE => {}; IF sProcess # NIL THEN Process.Detach[sProcess]; }; HandleButtonRelease: PROCEDURE [event: Xl.Event, widget: XTk.Widget] ~ { bp: Xl.ButtonReleaseEvent ¬ NARROW[event]; oRef: REF OutputRec ~ GetOutputData[widget]; p: Xl.PointerMapping ¬ Xl.GetPointerMapping[widget.connection]; SELECT p[bp.button] FROM 1 => oRef.buttonOneDown­ ¬ FALSE; 2 => oRef.buttonTwoDown­ ¬ FALSE; 3 => oRef.buttonThreeDown­ ¬ FALSE; ENDCASE => {}; }; EventProc: Xl.EventProcType = { ENABLE { Xl.XError => GOTO oops; }; widget: XTk.Widget ~ NARROW[clientData]; oRef: REF OutputRec ~ GetOutputData[widget]; WITH event SELECT FROM focusOut: Xl.FocusOutEvent => { StopBlink[oRef]; }; buttonPress: Xl.ButtonPressEvent => { HandleButtonPress[buttonPress, widget]; }; expose: Xl.ExposeEvent => { Redraw[oRef]; }; buttonRelease: Xl.ButtonReleaseEvent => { HandleButtonRelease[buttonRelease, widget]; }; keyPress: Xl.KeyPressEvent => { char: CHAR; keysym: Xl.KeySym; matched: Xl.KeySym; isModifier: BOOL; IF ~oRef.acceptKeyboard THEN RETURN; [char: char, keysym: keysym, matched: matched, isModifier: isModifier] ¬ XlAscii.Convert[event.connection, keyPress.keyCode, keyPress.state, listOfSpecials]; IF isModifier THEN RETURN; IF matched = KeySymsSun.Paste OR matched = KeySymsOSF.Paste OR matched = KeySymsHP.Paste THEN { Show[widget, XlCutBuffers.Get[widget.connection]]; RETURN }; IF matched = KeySymsKB.Next OR matched = KeySymsKB.MoveRight THEN { XTkInputFocus.SetFocus[XTkInputFocus.NextFocusTarget[oRef.widget]]; RETURN; }; IF matched = KeySymsKB.Stop THEN { StopBlink[oRef]; RETURN; }; PushChar[widget, char]; }; ENDCASE => {}; EXITS oops => {}; }; StreamInitInstPart: XTk.InitInstancePartProc = { }; DefaultHeight: INT ¬ 17; DefaultWidth: INT ¬ 200; QueryKeyboardAccepting: PUBLIC PROC [w: XTk.Widget] RETURNS [ans: BOOL ¬ FALSE] ~ { oRef: REF OutputRec ¬ GetOutputData[w]; IF oRef # NIL THEN RETURN[oRef.acceptKeyboard]; }; EnableKeyboardInput: PUBLIC PROC [w: XTk.Widget] ~ { oRef: REF OutputRec ¬ GetOutputData[w]; IF oRef # NIL THEN oRef.acceptKeyboard ¬ TRUE; }; DisableKeyboardInput: PUBLIC PROC [w: XTk.Widget] ~ { oRef: REF OutputRec ¬ GetOutputData[w]; IF oRef # NIL THEN oRef.acceptKeyboard ¬ FALSE; }; CreateEditWidget: PUBLIC PROC [widgetSpec: XTk.WidgetSpec ¬ [], scrollable: BOOL ¬ FALSE, backingHeight: INT ¬ FIRST[INT], keyboardAccepting: BOOL ¬ TRUE, widgetStream: IO.STREAM ¬ NIL] RETURNS [widget: XTk.Widget] = { oRef: REF OutputRec ~ NEW[OutputRec]; lData: LineData ~ NEW[LineDataRec]; parent: XTk.Widget ¬ NIL; IF widgetSpec.geometry.size.height <= 0 OR widgetSpec.geometry.size.height = Xl.dontUse THEN widgetSpec.geometry.size.height ¬ DefaultHeight; IF widgetSpec.geometry.size.width <= 0 OR widgetSpec.geometry.size.width = Xl.dontUse THEN widgetSpec.geometry.size.width ¬ DefaultWidth; IF scrollable THEN { child: XTk.Widget; oRef.scrollable ¬ TRUE; SELECT TRUE FROM backingHeight <= 0 => backingHeight ¬ oRef.backHeight ¬ widgetSpec.geometry.size.height; ENDCASE => oRef.backHeight ¬ backingHeight; child ¬ XTkWidgets.CreateXStack[ widgetSpec: [geometry: [[0, 0], [100, backingHeight], 0]] ]; parent ¬ XTkXBiScroller.CreateXBiScroller[ child: child, widgetSpec: widgetSpec, hsbar: FALSE]; widgetSpec.geometry.pos ¬ [0, 0]; widgetSpec.geometry.size.width ¬ widgetSpec.geometry.size.width - 15; widgetSpec.geometry.size.height ¬ backingHeight; }; widget ¬ XTk.CreateWidget[widgetSpec, outputClass]; IF widgetStream#NIL THEN BindStream[widget, widgetStream]; oRef.lineTab ¬ CardTab.Create[]; [] ¬ CardTab.Store[oRef.lineTab, 0, lData]; oRef.acceptKeyboard ¬ keyboardAccepting; oRef.driblee ¬ IO.CreateStream[streamProcs: outputStreamProcs, streamData: oRef]; oRef.widget ¬ widget; XTk.AddPermanentMatch[widget, [proc: EventProc, handles: events, tq: Xl.CreateTQ[], data: widget], [keyPress: TRUE, buttonPress: TRUE, buttonRelease: TRUE, focusChange: TRUE, exposure: TRUE]]; XTkFriends.AssignInstPart[widget, outputClass, oRef]; XTk.RegisterNotifier[oRef.widget, XTk.postConfigureKey, ReconfigureProc, oRef]; IF scrollable THEN { oRef.backingSize.height ¬ backingHeight; XTkWidgets.AppendChild[XTkXBiScroller.Child[parent], widget]; XTk.PutWidgetProp[parent, sampleORec, oRef]; oRef.scrollWidget ¬ parent; widget ¬ parent; }; }; ReconfigureProc: XTk.WidgetNotifyProc ~ { oRef: REF OutputRec ¬ NARROW[registerData]; IF oRef.scrollWidget#NIL THEN { child: XTk.Widget ¬ XTkXBiScroller.Child[oRef.scrollWidget]; g: Xl.Geometry; g.size.width ¬ child.parent.actual.size.width; IF g.size.width#child.actual.size.width THEN { g.size.height ¬ oRef.backingSize.height; XTk.NoteAndStartReconfigure[child, g]; RETURN; }; }; Redraw[oRef]; }; CreateStream: PUBLIC PROC [w: XTk.Widget ¬ NIL] RETURNS [widgetStream: IO.STREAM] = { widgetStream ¬ FanoutStream.Create[reportErrors: FALSE, forwardClose: FALSE]; IF w#NIL THEN BindStream[w, widgetStream]; }; BindStream: PUBLIC PROC [w: XTk.Widget, widgetStream: IO.STREAM] = { oRef: REF OutputRec ~ GetOutputData[w]; FanoutStream.AddSlave[master: widgetStream, slave: oRef.driblee]; }; END. v XTkEditWidgetsImpl.mesa Copyright Σ 1992 by Xerox Corporation. All rights reserved. Philip James, March 20, 1992 3:28 pm PST Christian Jacobi, June 12, 1992 11:21 am PDT prevLData # NIL AND--increments position but returns previous value atomicly LAST[CARD] for line means current LINE Ascii.NUL => {}; -- why does the 'Delete' key send Ascii.NUL? delete stuff on last line. delete lines in between. delete stuff on first line delete stuff between sX and eX IF ~lData.text.IsEmpty[] where: how many characters in to place the character IF line = LAST[CARD] THEN line _ oRef.currentLine; move text right. erase end char take end char out of rope if next line is connected...and end char to front else make new line with just this char, andset this line to be connected to it copy from pos.x..end to front of next line should merge lines together if linked. XTkInputFocus.GiveUpFocus[oRef.widget]; check for window gone away mark from sX to far right on sX mark from eX to oRef.leftSpace on eY mark all lines between sY and eY mark between eX and sX _ IF tmpY < 0 THEN 0 ELSE tmpY; tmpX: INT _ (bp.pos.x - oRef.charDX / 2 - oRef.leftSpace) / oRef.charDX; _ IF tmpX < 0 THEN 0 ELSE tmpX; XTkInputFocus.SetFocus[oRef.widget]; XTkInputFocus.SetFocus[oRef.widget]; IF char#0c THEN { }; Moved from StreamInitInstPart oRef: REF OutputRec ~ NEW[OutputRec]; lData: LineData ~ NEW[LineDataRec]; Κ&R–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ Οeœ1™K˜——šžœžœžœ˜=K˜&K˜Dšžœžœ˜K˜K˜&šžœž˜ K˜&—K˜(K˜—K˜—K˜—šžœ˜ Kšœ žœ/˜;Kšœžœ˜K˜DKšžœžœž˜K˜&šžœ ˜ šžœ˜K˜*K˜&K˜(K˜——K˜——K˜K˜K˜'K˜—K˜šŸœžœ žœ žœžœžœžœžœžœ žœ žœ˜~šžœžœžœž˜žœ˜FK˜-K˜BK˜3šžœžœ˜K˜Kšœžœ%˜/šžœžœ˜#šœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ$˜$Kšœ3˜3KšœI˜IKšœ ˜ —šœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ'˜'—š žœžœž œžœž˜:K˜*K˜0Kšžœ˜—šœžœ˜Kšœ2žœžœ˜RK˜—K˜"šžœ-žœžœ˜SK˜K˜——K˜—K˜šŸœžœžœ˜'Kšœ˜Kšœžœ˜K˜Kšœžœžœ˜šžœ!˜#šžœ˜K˜%—šžœ˜Kšœžœ%˜/K˜"šžœ-žœžœ˜SKšœ žœ˜K˜—K˜——K˜K˜šžœC˜Ešžœ˜K™*šœ ˜ Kšœ˜Kšœ0˜0Kšœ.˜.Kšœ@˜@Kšœ3˜3Kšœ ˜ Kšœ˜—šœ ˜ Kšœ˜Kšœ˜Kšœ-˜-Kšœ4˜4—K˜NK˜OK˜——K˜&Kšœ3žœ5˜kKšœ&™&K˜6Kšœžœ˜K˜ šžœž˜K˜—Kšœ˜—K˜šŸœžœžœžœ˜4Kšœžœ˜Kšœžœžœ˜Kšœ˜šžœž˜K˜5šžœžœžœ^˜›šžœ˜Kšœ žœp˜~K˜7K˜!Kšœ žœ˜!šžœ˜šžœ %˜,Kšœžœ˜ šœ ˜ Kšœ˜Kšœ˜Kšœ˜KšœW˜WKšœz˜zKšœ/˜/Kšœ ˜ —K˜,K˜!Kšœžœ˜K˜—šžœ˜šœ ˜ Kšœ˜Kšœ˜Kšœ˜KšœW˜WKšœz˜zKšœ.˜.Kšœ ˜ —K˜@K˜8šœ ˜ Kšœ˜Kšœ˜Kšœ˜KšœW˜WKšœs˜sKšœ=˜=Kšœ ˜ —šœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ.˜.—K˜——K˜—Kšžœžœ˜—K˜ Kšžœ˜—K˜—K˜– "cedar" stylešœžœžœžœ˜=K– "cedar" style˜K– "cedar" stylešœ˜K– "cedar" stylešœ'˜'K– "cedar" stylešœ)˜)K– "cedar" stylešœ*˜*K– "cedar" stylešœ˜—K˜– "cedar" styleš Ÿœžœžœžœžœ˜EKšžœžœžœ˜Kšœžœ žœ˜.Kšœ˜Kšžœžœžœ˜&Kšœ˜Kšœ ˜ K– "cedar" stylešžœ ˜K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" stylešŸœžœžœžœ žœžœžœžœ žœ˜qKšžœžœžœ˜Kšœžœ žœ˜.Kšœ˜Kš Ÿœžœžœžœžœžœ ˜IKšžœžœžœ˜&K˜JKšœ ˜ K– "cedar" stylešžœ ˜K– "cedar" stylešœ˜K– "cedar" style˜—š Ÿœžœžœžœžœ˜GKšœ*žœ˜-Kšœ˜—K˜K˜ΦK˜šœ žœžœ˜Kšœ ˜.Kšœ˜Kšœ žœžœžœ˜K˜Kšœ ˜)Kšœ 0˜OKšœžœ ˜*Kšœ žœ ;˜OKšœ žœ G˜\Kšœžœ &˜IKšœžœ˜Kšœžœ˜Kšœ žœ˜Kšœ .˜FKšœ žœ .˜BKšœžœ ˜5Kšœ žœ˜Kšœ žœžœ˜K˜K˜Kšœ žœž˜Kšœžœž˜Kšœžœž˜Kšœžœž œžœ˜+Kšœž œž œžœ˜,Kš œžœžœžœžœžœ˜-K˜$Kš œ žœžœžœžœžœ˜'Kšœ žœž˜Kšœ žœžœžœ˜Kšœžœžœ˜Kšœ ž˜$Kšœžœž˜Kšœ žœž˜K˜—K˜Kšœ žœžœžœ˜'šœ žœžœ˜Kšœ žœ˜Kšœžœž˜K˜—K˜Kšœ žœ žœ ˜+K˜š Ÿ œžœžœžœžœ˜FKšœžœ'˜0šžœžœž˜Kšœžœžœ˜$šžœ˜ K˜(š žœžœžœžœžœž˜#Kšžœžœ˜—K˜——Kšœ˜—K˜šŸ œ˜ Kšœžœ#˜,Kšœžœ%˜1Kšœ žœžœ ˜1šžœ žœ˜šžœ,žœ˜3K˜S—šžœ3žœ˜:K˜J—šžœ(žœ˜/K˜)—K˜3šž˜Kšœžœžœ=˜MK˜K˜K˜6K˜%K˜,K˜3Kšœ. *˜XKšœB˜BKšœB˜BKšœ!˜!Kšžœ˜—Kšœ˜Kšœ˜—KšœT˜TKšžœžœ žœ(˜AK˜—K˜šŸ œ˜#Kšœžœ#˜,K˜Kšœ žœ˜Kšœ˜—K˜Kšœžœžœ žœk˜“Kšœ2žœ:˜pK˜Kš Ÿœž œžœžœžœžœ˜nK˜šŸœžœžœ˜1Kšœžœ#˜,šžœžœžœž˜ Kšœ#˜#Kšžœ˜—Kšœ˜K˜K˜—š Ÿ œžœžœ žœžœ˜=K˜6Kšžœ8˜>K˜—K˜šŸœžœžœ˜1Kšœžœ#˜,Kšœ žœ žœ˜Kšžœžœžœ˜+Kšœ˜Kšœ˜Kšœ˜K˜—šŸ œž œžœ˜.K˜Kšœžœ˜Kšœ'™'K˜—K˜šŸ œž œžœ˜/Kšœ$˜$šžœžœ˜Kšœ žœ˜Kšœžœ˜Kšœ žœ˜ Kšœ˜K˜K˜—K˜—K˜šŸœž œžœ˜!Kšœžœ žœ˜#Kšœ˜šžœžœ˜š žœžœž œžœž˜/K˜Kšœs œ˜Kšœž˜Kšžœ˜——K˜—K˜šŸ œž œžœ˜.Kšœ™šžœž˜K˜Kšœ˜šžœžœžœ˜&Kšœ4˜4Kšœžœ˜K˜—Kšžœ˜—K˜—K˜šŸœž œžœžœžœžœžœ˜R˜K˜K˜K˜ K˜VK˜2K˜—K˜—K˜š Ÿœž œžœ"žœ žœ ˜hKšœžœ˜ Kšœžœ˜ K˜%K˜#K˜,šžœ ˜ šžœ˜Kšœžœžœ˜K˜+K˜K˜Kšœ™K˜3K˜-K˜'K˜K™$K˜K˜K™ šžœžœžœ ž˜K˜#K˜6šžœž˜K˜!—šž˜K˜%—K˜&Kšžœ˜—K˜K˜!šžœž˜K˜/—šž˜K˜3—K˜K˜ K˜—šžœžœ žœ˜Kšœ™K˜+K˜K˜K˜)K˜ K˜——K˜—K˜K˜š Ÿ œž œžœžœžœ˜Bšžœ ž˜K˜#K˜K˜NK˜-K˜\Kšžœ˜—K˜—K˜š Ÿ œž œžœžœžœžœ˜\Kšœžœ:˜CKšœžœ>˜GK˜Kšœžœ žœžœ˜"Kšžœžœ˜,K˜Kšœžœ žœžœ˜!Kšžœžœ˜8K˜K˜—š Ÿ œž œžœžœžœ˜BK˜7K˜K˜K˜K˜K˜—K˜šŸœž œ*˜FKšœžœ˜(Kšœžœ#˜,Kšœžœ;˜DKšœž˜ Kšœžœ˜ Kšœžœ žœžœ™!Kšœžœ?™HKšœžœ žœžœ™ K˜Kšœ žœžœ˜K˜?Kšœ žœž˜K˜-K˜Kšœ$™$šžœž˜šœ˜Kšœžœ˜Kšœ žœ˜K˜—Kšœžœ˜ Kšœžœ˜"Kšžœ˜—šžœ žœ˜Kšœžœ˜Kšžœžœ žœžœ˜,K˜"Kšžœžœ˜@K˜K˜YK˜K˜Kšœ˜K˜—šžœž˜Kšœžœ&˜:Kšœžœ&˜:Kšœžœ(˜