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
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]] = {
prevLData # NIL AND--increments position but returns previous value atomicly
IF oRef#NIL THEN {
pos ¬ oRef.pos;
oRef.pos.x ¬ oRef.pos.x+rr;
};
};
LAST[CARD] for line means current LINE
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 => {}; -- why does the 'Delete' key send Ascii.NUL?
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 {
delete stuff on last line.
lData: LineData¬ GetLineData[oRef, sY];
FOR x: CARD DECREASING IN [1..eX] DO
[] ¬ RemovePrevChar[oRef, eY, x];
ENDLOOP;
delete lines in between.
FOR line: CARD DECREASING IN (sY..eY) DO
RemoveLine[oRef, line];
ENDLOOP;
delete stuff on first line
FOR x: CARD DECREASING IN (sX..lData.text.Length[]] DO
[] ¬ RemovePrevChar[oRef, sY, x];
ENDLOOP;
[] ¬ RemovePrevChar[oRef,sY+1, 0];
}
ELSE
delete stuff between sX and eX
FOR x: CARD DECREASING IN (sX..eX] DO
[] ¬ RemovePrevChar[oRef, sY, x];
ENDLOOP;
};
};
Ascii.BS => {
IF ~lData.text.IsEmpty[]
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] ~ {
where: how many characters in to place the character
w: XTk.Widget ~ oRef.widget;
pos: Xl.Point;
c: CHAR;
lData: LineData ¬ GetLineData[oRef, line];
IF line = LAST[CARD] THEN line ← oRef.currentLine;
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 {
move text right.
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 {
erase end char
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]
];
take end char out of rope
c ¬ lData.text.Fetch[lData.text.Length[] - 1];
lData.text ¬ lData.text.Replace[lData.text.Length[] - 1, 1, ""];
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
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 {
copy from pos.x..end to front of next line
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]]];
should merge lines together if linked.
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<unconfigured;
createW: BOOL ¬ mapping<unconfigured AND ~existW;
IF createW THEN {
IF Xl.IllegalCursor[widget.attributes.cursor] THEN
widget.attributes.cursor ¬ XlCursor.SharedStandardCursor[widget.connection, arrow];
IF widget.attributes.backgroundPixel=Xl.illegalPixel THEN
widget.attributes.backgroundPixel ¬ widget.screenDepth.screen.whitePixel;
IF widget.attributes.backingStore=illegal THEN
widget.attributes.backingStore ¬ always;
oRef.font ¬ Xl.OpenFont[widget.connection, "8x13"];
BEGIN
fi: REF READONLY Xl.FontInfoRec ¬ Xl.QueryFont[widget.connection, oRef.font];
oRef.ascent ¬ fi.fontAscent;
oRef.descent ¬ fi.fontDescent;
oRef.lineDY ¬ oRef.ascent+oRef.descent+oRef.lineSpace;
oRef.charDX ¬ fi.maxBounds.charWidth;
oRef.topOffset ¬ oRef.ascent+oRef.lineSpace;
oRef.bottomOffset ¬ oRef.descent+oRef.bottomOffset;
oRef.gc ¬ Xl.MakeGContext[widget.connection]; --should we share contexts of same screen?
Xl.SetGCForeground[oRef.gc, widget.screenDepth.screen.blackPixel];
Xl.SetGCBackground[oRef.gc, widget.screenDepth.screen.whitePixel];
Xl.SetGCFont[oRef.gc, oRef.font];
END;
HomePos[oRef];
};
XTkFriends.SimpleConfigureOneLevelLR[widget, geometry, mapping, reConsiderChildren];
IF existW OR createW THEN SetWindowSize[oRef, widget.actual.size]
};
ForgetScreen: XTk.TerminateProc = {
oRef: REF OutputRec ~ GetOutputData[widget];
oRef.font ¬ Xl.nullFont;
oRef.gc ¬ NIL;
};
listOfSpecials: LIST OF Xl.KeySym ~ LIST[KeySymsSun.Paste, KeySymsOSF.Paste, KeySymsHP.Paste, KeySymsKB.Stop, KeySymsKB.Next, KeySymsKB.MoveRight];
events: Xl.EventFilter ~ Xl.FullCreateEventFilter[LIST[keyPress, buttonPress, buttonRelease, focusOut, expose]];
DoFlush: PROCEDURE [widget: XTk.Widget] ~ {IF widget.connection # NIL THEN Xl.Flush[widget.connection, TRUE]};
Show: PROC [widget: XTk.Widget, r: Rope.ROPE] = {
oRef: REF OutputRec ~ GetOutputData[widget];
FOR x: INT IN [0..r.Length[]) DO
UnflushedOutChar[oRef, r.Fetch[x]];
ENDLOOP;
DoFlush[widget];
};
FinalCharPos: PROC [oRef: REF OutputRec] RETURNS [x: INT] ~ {
lineData: LineData ¬ GetLineData[oRef, oRef.lastLine];
RETURN[oRef.leftSpace + oRef.charDX * lineData.text.Length[]];
};
PushChar: PROC [widget: XTk.Widget, ch: CHAR] = {
oRef: REF OutputRec ~ GetOutputData[widget];
deleted: CHAR ¬ Ascii.NUL;
IF widget.fastAccessAllowed#ok THEN RETURN;
UnflushedOutChar[oRef, ch];
DoFlush[widget];
};
StopBlink: PROCEDURE [oRef: REF OutputRec] ~ {
EraseMark[oRef];
oRef.blinking­ ¬ FALSE;
XTkInputFocus.GiveUpFocus[oRef.widget];
};
StartBlink: PROCEDURE [oRef: REF OutputRec] ~ {
XTkInputFocus.SetFocus[oRef.widget];
IF ~(oRef.blinking­) THEN {
bProcess: PROCESS;
oRef.blinking­ ¬ TRUE;
bProcess ¬ FORK BlinkProc[oRef];
DrawMark[oRef];
Process.Detach[bProcess];
};
};
Redraw: PROCEDURE [data: REF] ~ {
oRef: REF OutputRec ¬ NARROW[data];
lData: LineData;
IF oRef.refreshing THEN
FOR x: CARD DECREASING IN [0..oRef.lastLine] DO
lData ¬ GetLineData[oRef, x];
Xl.ImageRope[oRef.widget.connection, oRef.widget.window.drawable, [oRef.leftSpace, oRef.topOffset + oRef.lineDY * x-- - oRef.ascent--], oRef.gc, lData.text];
DoFlush[oRef.widget];
ENDLOOP;
};
BlinkProc: PROCEDURE [oRef: REF OutputRec] ~ {
check for window gone away
WHILE oRef.blinking­ DO
Process.PauseMsec[500];
InvertMark[oRef];
IF oRef.pleaseReconfigure # NIL THEN {
XTk.NoteAndStartReconfigure[oRef.pleaseReconfigure];
oRef.pleaseReconfigure ¬ NIL;
};
ENDLOOP;
};
DoRect: PROCEDURE [oRef: REF OutputRec, x1: INT, y1: CARD, x2: INT, y2: CARD] ~ {
Xl.FillRectangle[
oRef.widget.connection,
oRef.widget.window.drawable,
oRef.gc,
[oRef.leftSpace + oRef.charDX * x1, oRef.topOffset + y1 * oRef.lineDY - oRef.ascent],
[oRef.charDX * (x2 - x1), oRef.lineDY * (y2 - y1)]
];
};
InvertSelection: PROCEDURE[oRef: REF OutputRec, start,end: Xl.Point] RETURNS [text: Rope.ROPE ¬ ""] ~ {
sX, eX: INT;
sY, eY: CARD;
[sX, sY] ¬ PosFromPoint[oRef, start];
[eX, eY] ¬ PosFromPoint[oRef, end];
[sX, sY, eX, eY] ¬ OrderPos[sX, sY, eX, eY];
IF eY > sY
THEN {
prevLineLinked: BOOL ¬ TRUE;
lineData: LineData ¬ GetLineData[oRef, sY];
Xl.SetGCFunction[oRef.gc, xor];
mark from sX to far right on sX
DoRect[oRef, sX, sY, lineData.text.Length[], sY+1];
text ¬ text.Concat[lineData.text.Substr[sX]];
prevLineLinked ¬ lineData.linkedToNext;
mark from eX to oRef.leftSpace on eY
DoRect[oRef, 0, eY, eX, eY+1];
mark all lines between sY and eY
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 {
mark between eX and sX
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;
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;
lData: LineData;
sProcess: PROCESS ¬ NIL;
p: Xl.PointerMapping ¬ Xl.GetPointerMapping[widget.connection];
changePos: BOOL ¬ FALSE;
[xChar, yLines] ¬ PosFromPoint[oRef, bp.pos];
StartBlink[oRef];
XTkInputFocus.SetFocus[oRef.widget];
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];
XTkInputFocus.SetFocus[oRef.widget];
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;
};
IF char#0c THEN {
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];
Moved from StreamInitInstPart
oRef: REF OutputRec ~ NEW[OutputRec];
lData: LineData ~ NEW[LineDataRec];
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.