-- GCellImpl.mesa
-- Last Edited by: Sturgis, February 28, 1983 4:47 pm
DIRECTORY
Expressions USING[AnalyzedExpression, AnalyzedIdentifier, AnalyzeExpression, AnalyzeIdentifier, EvaluateExpression, Expression, Identifier, IdTableSet, ParseExpression, ParseIdentifier, SetAnalyzedIdentifier, SyntaxError, Token],
Buttons USING[ButtonProc, Create, ReLabel],
Convert USING[Value, ValueToRope],
Dependencies USING[Action, DataItem, DeactivateAction, DeactivateDataItem, DefineDataItem, DependencySet, DefineAction, MarkActionDirty, MarkDataItemDirty, NoteThatActionModifiesDataItem, NoteThatDataItemIsReadByAction],
Displayer USING[Displayer],
GCell USING[GCellItemDescriptor, GCellItemJustification, GCellItemMode, GCellItemRowColSumMode, GCellItemValueMode, NoteItemSelection, SelectionDirection, SetAGCellProc, StandardGCells],
NewCalcGlobal USING[NCGlobal],
Rope USING[Cat, Digit, Equal, Fetch, Find, Length, Replace, ROPE, Substr],
IO USING[atom, char, CharProc, GetInt, GetRefAny, GetSequence, int, Put, PutChar, rope, STREAM],
StructureNodes USING[Action, DACell, DisplayArray, GetContextDA, GetElementAt, StructureNode, StructureNodeBody, StructureNodeProcs],
VFonts USING[FontHeight, StringWidth],
ViewerClasses USING[Viewer],
ViewerOps USING[AddProp, DestroyViewer, FetchProp],
ViewerTools USING[GetContents, GetSelectedViewer, InhibitUserEdits, MakeNewTextViewer, SetContents, SetSelection];
GCellImpl: PROGRAM IMPORTS Buttons, Convert, Dependencies, Expressions, GCell, IO, Rope, StructureNodes, VFonts, ViewerOps, ViewerTools EXPORTS GCell =
BEGIN OPEN Dependencies, Displayer, Expressions, GCell, NewCalcGlobal, Rope, IO, ViewerClasses;
-- synthetic, to be removed when expressions interface fixed
--RecognizeIdentifiers: PROCEDURE[exp: Expression, dSet: DependencySet, eval: Action, n, e, s, w, self, global: IdTable] RETURNS[Expression];
--RecognizeTargetIdentifier: PROCEDURE[id: Expression, dSet: DependencySet, store: Action, n, e, s, w, self, global: IdTable] RETURNS[Expression];
-- issues to deal with later
-- September 25, 1982 10:25 am: include leading space to left of a column in these cells as part of their viewers, to allow insertion of more new text during edit.
-- September 25, 1982 11:22 am: in the save file must quote {, }, and '. Also, must undo the quotes on input.
-- September 25, 1982 11:25 am: I am sure I paint the viewers far too often.
-- September 25, 1982 12:04 pm: abandonItem also has to de activate any dataitems and actions the gcellitem has created.
-- for debugging
nGCells: LONG CARDINAL ← 0;
nGCellItems: LONG CARDINAL ← 0;
-- main stuff
GCell: TYPE = REF GCellBody;
GCellBody: PUBLIC TYPE = RECORD[
da: StructureNodes.DisplayArray,
selfI, selfJ: CARDINAL ← 0,
ncGlobal: NCGlobal,
displayer: Displayer,
gcaSn: StructureNodes.StructureNode,
vParent: Viewer,
idTables: IdTableSet,
rowSummer, colSummer: Action ← NIL,
screenLead: INTEGER ← 0,
screenSpace: INTEGER ← 0,
textLead: INTEGER ← 0,
textSpace: INTEGER ← 2, -- eventually this will become changeable by GCellTool
textNLines: INTEGER ← 0, -- set by preshow
lastPrePaintY, lastPrePaintX, lastPrePaintL, lastPrePaintR, lastPrePaintW: INTEGER ← 0,
unPainted: BOOLEAN ← TRUE,
nItems: CARDINAL ← 0,
items: Item ← NIL
];
Item: TYPE = REF ItemBody;
ItemBody: TYPE = RECORD[
da: StructureNodes.DisplayArray,
selfI, selfJ: CARDINAL ← 0,
selfItemX: CARDINAL ← 0,
mode: GCellItemMode,
justification: GCellItemJustification,
rowSumMode: GCellItemRowColSumMode,
colSumMode: GCellItemRowColSumMode,
valueMode: GCellItemValueMode,
idName: ROPE,
expressionText: ROPE,
zeroValText: ROPE,
created: BOOLEAN ← TRUE,
expressionBad: BOOLEAN ← TRUE,
expression: AnalyzedExpression ← [NIL],
evalAction: Action ← NIL, -- created only if expression is not simple constant
firstTime: BOOLEAN ← TRUE, -- set false after value is first displayed
value: REAL ← 0,
valueChanges: DataItem ← NIL, -- created only if information passes through this item
idBad: BOOLEAN ← TRUE,
id: AnalyzedIdentifier ← [NIL],
assignAction: Action ← NIL, -- created only if there is an identifier
buttonText: ROPE ← "",
buttonTextDirty: BOOLEAN ← FALSE, -- set true when buttonText changed
buttonTextAnalyzed: BOOLEAN← FALSE, -- set false when text becomes dirty, is set true when analyzed
buttonShapeDirty: BOOLEAN ← FALSE, -- set true when shape recomputed, textDirty is set false at that time.
buttonL, buttonA, buttonB, buttonR, buttonW: INTEGER ← 0,
buttonHeight: INTEGER ← 0,
textL, textA, textB, textR, textW: INTEGER ← 0,
viewerX, viewerY, viewerW: INTEGER ← 0,
viewer: Viewer ← NIL,
viewerMode: ItemViewerMode ← nil,
editable: BOOLEAN ← FALSE,
next: Item ← NIL];
ItemViewerMode: TYPE = {nil, button, textBox};
ItemRefDesc: TYPE = REF ItemRefDescBody;
ItemRefDescBody: TYPE = RECORD[da: StructureNodes.DisplayArray, I, J: CARDINAL, itemX: CARDINAL];
GCellSNProcs: REF StructureNodes.StructureNodeProcs ← NEW[StructureNodes.StructureNodeProcs ← [
CheckGCellForDirtyData,
PreShowGCell,
ShowGCellLine,
PrePrePaintGCell,
PrePaintGCell,
NoteEnclosingCellGCell,
NoteCoordinatesGCell,
SaveGCell,
ActGCell,
SubstituteInGCell,
AbandonGCell,
VerifyGCell
]];
-- GCell specific interface procedures
LoadGCell: PUBLIC PROCEDURE[global: NCGlobal, displayer: Displayer, gcaSn: StructureNodes.StructureNode, vParent: Viewer, from: STREAM, version: CARDINAL] RETURNS[StructureNodes.StructureNode] =
BEGIN
nItems: CARDINAL;
gc: GCell ← NEW[GCellBody ← [displayer: displayer, gcaSn: gcaSn, vParent: vParent, ncGlobal: global]];
previousItem: Item ← NIL;
nGCells ← nGCells + 1;
nItems ← GetInt[from];
FOR J: CARDINAL IN [1..nItems] DO
desc: GCellItemDescriptor;
item: Item;
desc ← ReadOneItem[from, version];
item ← CreateItem[gc, NIL, 0, 0, J, desc, FALSE];
IF previousItem # NIL THEN previousItem.next ← item ELSE gc.items ← item;
previousItem ← item;
gc.nItems ← gc.nItems + 1;
ENDLOOP;
RETURN[NEW[StructureNodes.StructureNodeBody ← [gc, GCellSNProcs]]];
END;
CreateGCell: PUBLIC PROCEDURE[global: NCGlobal, displayer: Displayer, gcaSn: StructureNodes.StructureNode, vParent: Viewer, type: StandardGCells] RETURNS[StructureNodes.StructureNode] =
BEGIN
gc: GCell ← NEW[GCellBody ← [vParent: vParent, ncGlobal: global, displayer: displayer, gcaSn: gcaSn]];
LoadGCellWithType[gc, type];
nGCells ← nGCells + 1;
RETURN[NEW[StructureNodes.StructureNodeBody ← [gc, GCellSNProcs]]];
END;
SetGCellIdInfo: PUBLIC PROCEDURE[gcn: StructureNodes.StructureNode, idTables: IdTableSet] =
BEGIN
gc: GCell ← NARROW[gcn.ref];
gc.idTables ← idTables;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
AnalyzeItemExpression[gc, item];
AnalyzeItemId[gc, item];
HandleItemValueChanges[gc, item];
ENDLOOP;
END;
NoteSummers: PUBLIC PROCEDURE[gcn: StructureNodes.StructureNode, rowSummer, colSummer: Action] =
BEGIN
gc: GCell ← NARROW[gcn.ref];
IF gc.rowSummer # NIL OR gc.colSummer # NIL THEN ERROR; -- if non nil, then actions have already been tied to valueChanges, would have to re-think this code.
gc.rowSummer ← rowSummer;
gc.colSummer ← colSummer;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
HandleItemValueChanges[gc, item];
ENDLOOP;
-- now, since it is too hard to figure ouut exactly when this should be done, we set the summers dirty
Dependencies.MarkActionDirty[rowSummer];
Dependencies.MarkActionDirty[colSummer];
END;
-- REMARK: WARNING: this algorithm implies that some items may depend on earlier items through a row or column sum. THIS IS NOT RECORDED IN THE DEPENDENCY STRUCTURE!!!
DoRowSum: PUBLIC PROCEDURE[gcn: StructureNodes.StructureNode, sumToLeft: REAL] RETURNS[REAL] =
BEGIN
gc: GCell ← NARROW[gcn.ref];
sum: REAL ← sumToLeft;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
sum ← DoItemRowSum[gc, item, sum];
ENDLOOP;
RETURN[sum];
END;
DoColSum: PUBLIC PROCEDURE[gcn: StructureNodes.StructureNode, sumAbove: REAL] RETURNS[REAL] =
BEGIN
gc: GCell ← NARROW[gcn.ref];
sum: REAL ← sumAbove;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
sum ← DoItemColSum[gc, item, sum];
ENDLOOP;
RETURN[sum];
END;
SetGCellContents: PUBLIC PROCEDURE[gcn: StructureNodes.StructureNode, nItems: CARDINAL, getOneItem: PROCEDURE RETURNS[GCellItemDescriptor], entry: BOOLEAN] =
BEGIN
gc: GCell ← NARROW[gcn.ref];
SetGCellContentsAsGCell[gc, nItems, getOneItem, entry];
END;
SetGCellContentsAsGCell: PROCEDURE[gc: GCell, nItems: CARDINAL, getOneItem: PROCEDURE RETURNS[GCellItemDescriptor], entry: BOOLEAN] =
BEGIN
SGCC: PROCEDURE =
BEGIN
previousItem: Item ← NIL;
ClearGCell[gc];
FOR J: CARDINAL IN [1..nItems] DO
desc: GCellItemDescriptor ← getOneItem[];
item: Item ← CreateItem[gc, gc.da, gc.selfI, gc.selfJ, J, desc, TRUE];
IF previousItem # NIL THEN previousItem.next ← item ELSE gc.items ← item;
previousItem ← item;
gc.nItems ← gc.nItems + 1;
ENDLOOP;
END;
IF entry THEN gc.ncGlobal.executeInMonitor[gc.ncGlobal, SGCC] ELSE SGCC[];
END;
GetGCellContents: PUBLIC PROCEDURE[gcn: StructureNodes.StructureNode, send: SetAGCellProc, entry: BOOLEAN] =
BEGIN
gc: GCell ← NARROW[gcn.ref];
GGCC: PROCEDURE =
BEGIN
previousItem: Item ← NIL;
getOneItem: PROCEDURE RETURNS[GCellItemDescriptor] =
BEGIN
previousItem ← IF previousItem = NIL THEN gc.items ELSE previousItem.next;
RETURN[GetItemDescriptor[previousItem]];
END;
send[gc.nItems, getOneItem];
END;
IF entry THEN gc.ncGlobal.executeInMonitor[gc.ncGlobal, GGCC] ELSE GGCC[];
END;
NarrowGCell: PUBLIC PROCEDURE[ref: REF ANY] RETURNS[StructureNodes.StructureNode] =
BEGIN
sn: StructureNodes.StructureNode ← NARROW[ref];
gCell: GCell ← NARROW[sn.ref]; -- checks to be sure is a structure node for a gcell
RETURN[sn]
END;
StepSelection: PUBLIC PROCEDURE[gcn: StructureNodes.StructureNode, itemX: CARDINAL, direction: SelectionDirection] RETURNS[ok: BOOLEAN] =
BEGIN
gc: GCell ← NARROW[gcn.ref];
SELECT direction FROM
first =>
BEGIN
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
IF ItemAcceptsSelection[item] THEN
{ForceItemSelection[gcn, gc, item]; RETURN[TRUE]};
ENDLOOP;
RETURN[FALSE];
END;
next =>
BEGIN
itemStart: Item ← gc.items;
FOR X: CARDINAL IN [1..itemX] DO itemStart ← itemStart.next ENDLOOP;
FOR item: Item ← itemStart, item.next WHILE item # NIL DO
IF ItemAcceptsSelection[item] THEN
{ForceItemSelection[gcn, gc, item]; RETURN[TRUE]};
ENDLOOP;
RETURN[FALSE];
END;
previous =>
BEGIN
itemTry: Item ← gc.items;
itemLast: Item ← NIL;
FOR X: CARDINAL IN [1..itemX) DO
IF ItemAcceptsSelection[itemTry] THEN itemLast ← itemTry;
itemTry ← itemTry.next
ENDLOOP;
IF itemLast # NIL THEN
{ForceItemSelection[gcn, gc, itemLast]; RETURN[TRUE]}
ELSE RETURN[FALSE];
END;
last =>
BEGIN
itemTry: Item ← gc.items;
itemLast: Item ← NIL;
FOR itemTry: Item ← gc.items, itemTry.next WHILE itemTry # NIL DO
IF ItemAcceptsSelection[itemTry] THEN itemLast ← itemTry;
ENDLOOP;
IF itemLast # NIL THEN
{ForceItemSelection[gcn, gc, itemLast]; RETURN[TRUE]}
ELSE RETURN[FALSE];
END;
ENDCASE => ERROR;
END;
-- Logical node interface procedures
SaveGCell: PROCEDURE[node: StructureNodes.StructureNode, to: STREAM] =
BEGIN
gc: GCell ← NARROW[node.ref];
Put[to, char[' ], int[gc.nItems], char[' ]];
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
desc: GCellItemDescriptor ← GetItemDescriptor[item];
Put[to, char[' ]];
WriteOneItem[desc, to];
ENDLOOP;
END;
ActGCell: PROCEDURE[info: StructureNodes.StructureNode, I, J: CARDINAL, action: StructureNodes.Action, p1, p2: REF ANY] RETURNS[StructureNodes.StructureNode, CARDINAL, CARDINAL] =
BEGIN -- note: J is the index of the selected item
gc: GCell ← NARROW[info.ref];
sn: StructureNodes.StructureNode; cI, cJ: CARDINAL;
SELECT action FROM
MoveSelectionToNext => IF StepSelection[info, J, next] THEN RETURN[NIL, 0, 0];
MoveSelectionToPrevious => IF StepSelection[info, J, previous] THEN RETURN[NIL, 0, 0];
ENDCASE => NULL;
[sn , , ] ← StructureNodes.GetContextDA[gc.da];
RETURN[sn, gc.selfI, gc.selfJ];
END;
SubstituteInGCell: PROCEDURE[info: StructureNodes.StructureNode, newText: Rope.ROPE, oldText: Rope.ROPE] =
BEGIN
gc: GCell ← NARROW[info.ref];
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
SubstituteInItem[gc, item, newText, oldText];
ENDLOOP;
END;
AbandonGCell: PROCEDURE[node: StructureNodes.StructureNode] =
BEGIN
gc: GCell ← NARROW[node.ref];
ClearGCell[gc];
nGCells ← nGCells - 1;
END;
VerifyGCell: PROCEDURE[node: StructureNodes.StructureNode] =
BEGIN
gc: GCell ← NARROW[node.ref];
nItems: CARDINAL ← 0;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO nItems ← nItems + 1 ENDLOOP;
IF nItems # gc.nItems THEN ERROR;
END;
-- display node interface procedures
CheckGCellForDirtyData: PROCEDURE[node: StructureNodes.StructureNode] =
BEGIN
gc: GCell ← NARROW[node.ref];
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
CheckItemForDirtyData[gc, item];
ENDLOOP;
END;
PreShowGCell: PROCEDURE[node: StructureNodes.StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
BEGIN
gc: GCell ← NARROW[node.ref];
height ← 0;
L ← A ← B ← R ← W ← 0;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
IF item.buttonTextDirty THEN ComputeTextShape[item];
IF item.buttonText # NIL THEN
BEGIN
height ← height + 1;
L ← MAX[L, item.textL];
A ← MAX[A, item.textA];
B ← MAX[B, item.textB];
R ← MAX[R, item.textR];
W ← MAX[W, item.textW];
END;
ENDLOOP;
gc.textNLines ← height;
RETURN[gc.textLead, height, gc.textSpace, L, A, B, R, W];
END;
ShowGCellLine: PROCEDURE[node: StructureNodes.StructureNode, L, R, W: INTEGER, lineX: INTEGER, to: STREAM] =
BEGIN
gc: GCell ← NARROW[node.ref];
lx: INTEGER ← 1;
IF lineX > gc.textNLines THEN
BEGIN
FOR I: INTEGER IN [1..W] DO IO.PutChar[to, ' ] ENDLOOP;
RETURN;
END;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
IF item.buttonText # NIL THEN
IF lx = lineX THEN
BEGIN
ShowItem[item, L, R, W, to];
RETURN;
END
ELSE lx ← lx + 1;
ENDLOOP;
END;
PrePrePaintGCell: PROCEDURE[node: StructureNodes.StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
BEGIN
gc: GCell ← NARROW[node.ref];
lead ← 0;
height ← 0;
L ← A ← B ← R ← W ← 0;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
IF item.buttonTextDirty THEN ComputeTextShape[item];
IF item.buttonText # NIL THEN
BEGIN
height ← height + item.buttonHeight;
L ← MAX[L, item.buttonL];
A ← MAX[A, item.buttonA];
B ← MAX[B, item.buttonB];
R ← MAX[R, item.buttonR];
W ← MAX[W, item.buttonW];
END;
ENDLOOP;
RETURN[gc.screenLead, height, gc.screenSpace, L, A, B, R, W];
END;
PrePaintGCell: PROCEDURE[node: StructureNodes.StructureNode, y, x, L, R, W: INTEGER] =
BEGIN
gc: GCell ← NARROW[node.ref];
paramChanges: BOOLEAN ← gc.lastPrePaintY # y OR gc.lastPrePaintX # x OR gc.lastPrePaintL # L OR gc.lastPrePaintR # R OR gc.lastPrePaintW # W OR gc.unPainted;
itemY: INTEGER ← y;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
IF item.buttonText # NIL THEN
BEGIN
IF item.buttonShapeDirty OR paramChanges OR item.viewerMode # button THEN
PrePaintItem[gc, item, itemY, x, L, R, W];
itemY ← itemY + item.buttonHeight;
item.buttonShapeDirty ← FALSE;
END;
ENDLOOP;
gc.lastPrePaintY ← y;
gc.lastPrePaintX ← x;
gc.lastPrePaintL ← L;
gc.lastPrePaintR ← R;
gc.lastPrePaintW ← W;
gc.unPainted ← FALSE;
END;
NoteEnclosingCellGCell: PROCEDURE[node: StructureNodes.StructureNode, dac: StructureNodes.DACell] =
BEGIN
END;
NoteCoordinatesGCell: PROCEDURE[node: StructureNodes.StructureNode, da: StructureNodes.DisplayArray, I, J: CARDINAL] =
BEGIN
gc: GCell ← NARROW[node.ref];
x: CARDINAL ← 0;
gc.da ← da;
gc.selfI ← I;
gc.selfJ ← J;
FOR item: Item ← gc.items, item.next WHILE item # NIL DO
x ← x+1;
NoteCoordinatesItem[item, da, I, J, x];
ENDLOOP;
END;
-- local procedures
GCellItemVersion: INTEGER = 3;
WriteOneItem: PROCEDURE[desc: GCellItemDescriptor, to: STREAM] =
BEGIN
modeAtom: ATOM ← SELECT desc.mode FROM
showId => $a,
showText => $b,
showExp => $c,
showValue => $d,
noShow => $e,
ENDCASE => ERROR;
justAtom: ATOM ← SELECT desc.justification FROM
left => $a,
centered => $b,
right => $c,
leftDecimal => $d,
rightDecimal => $e,
ENDCASE => ERROR;
rowAtom: ATOM ← SELECT desc.rowSumMode FROM
addVal => $a,
subVal => $b,
noEffect => $c,
setZero => $d,
ENDCASE => ERROR;
colAtom: ATOM ← SELECT desc.colSumMode FROM
addVal => $a,
subVal => $b,
noEffect => $c,
setZero => $d,
ENDCASE => ERROR;
valAtom: ATOM ← SELECT desc.valueMode FROM
rowSum => $a,
colSum => $b,
expression => $c,
ENDCASE => ERROR;
Put[to, int[GCellItemVersion]];
Put[to, char[' ], atom[modeAtom]];
Put[to, char[' ], atom[justAtom]];
Put[to, char[' ], atom[rowAtom]];
Put[to, char[' ], atom[colAtom]];
Put[to, char[' ], atom[valAtom]];
Put[to, char[' ], int[Rope.Length[desc.id]], char[' ]];
Put[to, char['{], rope[desc.id], char['}]];
Put[to, char[' ], int[Rope.Length[desc.text]], char[' ]];
Put[to, char['{], rope[desc.text], char['}]];
Put[to, char[' ], int[Rope.Length[desc.zeroValText]], char[' ]];
Put[to, char['{], rope[desc.zeroValText], char['}]];
END;
ReadOneItem: PROCEDURE[from: STREAM, version: CARDINAL] RETURNS[GCellItemDescriptor] =
BEGIN
fileVersion: INTEGER ← GetInt[from];
IF fileVersion = 2 THEN RETURN[OldReadOneItem[from]] ELSE
BEGIN
modeAtom: ATOM ← NARROW[GetRefAny[from]];
mode: GCellItemMode ← SELECT modeAtom FROM
$a => showId,
$b => showText,
$c => showExp,
$d => showValue,
$e => noShow,
ENDCASE => ERROR;
justAtom: ATOM ← NARROW[GetRefAny[from]];
just: GCellItemJustification ← SELECT justAtom FROM
$a => left,
$b => centered,
$c => right,
$d => leftDecimal,
$e => rightDecimal,
ENDCASE => ERROR;
rowAtom: ATOM ← NARROW[GetRefAny[from]];
rowMode: GCellItemRowColSumMode ← SELECT rowAtom FROM
$a => addVal,
$b => subVal,
$c => noEffect,
$d => setZero,
ENDCASE => ERROR;
colAtom: ATOM ← NARROW[GetRefAny[from]];
colMode: GCellItemRowColSumMode ← SELECT colAtom FROM
$a => addVal,
$b => subVal,
$c => noEffect,
$d => setZero,
ENDCASE => ERROR;
valAtom: ATOM ← NARROW[GetRefAny[from]];
valMode: GCellItemValueMode ← SELECT valAtom FROM
$a => rowSum,
$b => colSum,
$c => expression,
ENDCASE => ERROR;
d: CARDINAL; -- comment depth, from old form
n: INTEGER; -- number of characters in rope (new form)
CProc: IO.CharProc = TRUSTED
BEGIN
IF d = 0 AND char # '{ THEN RETURN[include: FALSE, quit: FALSE];
IF d = 0 AND char = '{ THEN {d ← d + 1; RETURN[include: FALSE, quit: FALSE]};
n ← n - 1; RETURN[include: n>=0, quit: n<-1];
END;
id: Rope.ROPE;
text: Rope.ROPE;
zeroValText: Rope.ROPE;
d ← 0; n ← IO.GetInt[from];
id ← IO.GetSequence[from, CProc];
d ← 0; n ← IO.GetInt[from];
text ← IO.GetSequence[from, CProc];
d ← 0; n ← IO.GetInt[from];
zeroValText ← IO.GetSequence[from, CProc];
RETURN[[mode, just, rowMode, colMode, valMode, id, text, zeroValText]];
END;
END;
OldReadOneItem: PROCEDURE[from: STREAM] RETURNS[GCellItemDescriptor] =
BEGIN
modeAtom: ATOM ← NARROW[GetRefAny[from]];
mode: GCellItemMode ← SELECT modeAtom FROM
$showId => showId,
$showText => showText,
$showExp => showExp,
$showValue => showValue,
$noShow => noShow,
ENDCASE => ERROR;
justAtom: ATOM ← NARROW[GetRefAny[from]];
just: GCellItemJustification ← SELECT justAtom FROM
$left => left,
$centered => centered,
$right => right,
$leftDecimal => leftDecimal,
$rightDecimal => rightDecimal,
ENDCASE => ERROR;
-- assumes a leading blank, and surrounded by matched { }
d: INTEGER ← 0; -- commentDepth
CProc: IO.CharProc = TRUSTED
BEGIN
IF char = '{ THEN
BEGIN
d ← d + 1;
RETURN[include: d>1, quit: FALSE]
END;
IF char = '} THEN
BEGIN
d ← d-1;
RETURN[include: d>0, quit: d=0];
END;
RETURN[include: d>0, quit: FALSE];
END;
id: Rope.ROPE ← IO.GetSequence[from, CProc];
text: Rope.ROPE ← IO.GetSequence[from, CProc];
zeroValText: Rope.ROPE ← IO.GetSequence[from, CProc];
RETURN[[mode, just, noEffect, noEffect, expression, id, text, zeroValText]];
END;
ClearGCell: PROCEDURE[gc: GCell] =
BEGIN
nextItem: Item ← gc.items;
WHILE nextItem # NIL DO
item: Item ← nextItem;
nextItem ← item.next;
AbandonItem[item];
ENDLOOP;
gc.items ← NIL;
gc.nItems ← 0;
gc.unPainted ← TRUE;
END;
-- standard cells mechanism
CellDescription: TYPE = RECORD[nItems: CARDINAL, itemDescriptors: LIST OF GCellItemDescriptor];
LoadGCellWithType: PROCEDURE[gc: GCell, type: StandardGCells] =
BEGIN -- uses SetGCellContentsAsGCell
cellDescription: CellDescription ←
SELECT type FROM
null => NullDescription,
colTitle => ColTitleDescription,
colSum => ColSumDescription,
rowTitle => RowTitleDescription,
rowSum => RowSumDescription,
assignment => AssignmentDescription,
slot => SlotDescription,
ENDCASE => ERROR;
nextD: LIST OF GCellItemDescriptor ← cellDescription.itemDescriptors;
NextItem: PROCEDURE RETURNS[GCellItemDescriptor] =
BEGIN
thisOne: GCellItemDescriptor ← nextD.first;
nextD ← nextD.rest;
RETURN[thisOne];
END;
SetGCellContentsAsGCell[gc, cellDescription.nItems, NextItem, FALSE];
IF nextD # NIL THEN ERROR;
END;
NullDescription: CellDescription ← [0, NIL];
ColTitleDescription: CellDescription ← [1, LIST[
[showText, right, noEffect, noEffect, expression, "", "col title"]]];
ColSumDescription: CellDescription ← [1, LIST[
[showValue, rightDecimal, noEffect, noEffect, colSum, "", "", " "]]];
RowTitleDescription: CellDescription ← [1, LIST[
[showText, left, noEffect, noEffect, expression, "", "row title"]]];
RowSumDescription: CellDescription ← [1, LIST[
[showValue, rightDecimal, noEffect, addVal, rowSum, "", "", " "]]];
AssignmentDescription: CellDescription ← [1, LIST[
[showExp, rightDecimal, addVal, addVal, expression, "", "0.00"]]];
SlotDescription: CellDescription ← [1, LIST[
[showText, left, noEffect, noEffect, expression, "", "slot"]]];
-- local procedures, item specific
CreateItem: PROCEDURE[gc: GCell, da: StructureNodes.DisplayArray, selfI, selfJ, selfItemX: CARDINAL, desc: GCellItemDescriptor, show: BOOLEAN] RETURNS[item: Item] =
BEGIN
item ← NEW[ItemBody ← [da: da, selfI: selfI, selfJ: selfJ, selfItemX: selfItemX, mode: desc.mode, justification: desc.justification, rowSumMode: desc.rowSumMode, colSumMode: desc.colSumMode, valueMode: desc.valueMode, idName: desc.id, expressionText: desc.text, zeroValText: desc.zeroValText]];
nGCellItems ← nGCellItems + 1;
--item.valueChanges ← Dependencies.DefineDataItem[item.gc.ncGlobal.dSet];
SELECT desc.mode FROM
showText =>
BEGIN
item.buttonText ← desc.text;
item.buttonTextDirty ← TRUE;
item.buttonTextAnalyzed ← FALSE;
item.editable ← TRUE;
HandleItemValueChanges[gc, item];
END;
showId =>
BEGIN
item.buttonText ← desc.id;
item.buttonTextDirty ← TRUE;
item.buttonTextAnalyzed ← FALSE;
item.editable ← TRUE;
AnalyzeItemExpression[gc, item];
AnalyzeItemId[gc, item];
HandleItemValueChanges[gc, item];
END;
showExp =>
BEGIN
item.buttonText ← desc.text;
item.buttonTextDirty ← TRUE;
item.buttonTextAnalyzed ← FALSE;
item.editable ← TRUE;
AnalyzeItemExpression[gc, item];
AnalyzeItemId[gc, item];
HandleItemValueChanges[gc, item];
END;
showValue, noShow =>
BEGIN
AnalyzeItemExpression[gc, item];
AnalyzeItemId[gc, item];
HandleItemValueChanges[gc, item];
IF show THEN SetItemValue[gc, item, item.value];
END;
ENDCASE => ERROR;
END;
GetItemDescriptor: PROCEDURE[item: Item] RETURNS[GCellItemDescriptor] =
{RETURN[[item.mode, item.justification, item.rowSumMode, item.colSumMode, item.valueMode, item.idName, item.expressionText, item.zeroValText]]};
SubstituteInItem: PROCEDURE[gc: GCell, item: Item, newText: Rope.ROPE, oldText: Rope.ROPE] =
BEGIN
text: Rope.ROPE ← NIL;
loc: INT;
changes: BOOLEAN ← FALSE; -- tentative
CheckItemForDirtyData[gc, item];
IF item.expressionText # NIL THEN
BEGIN
loc ← Rope.Find[item.expressionText, oldText];
IF loc # -1 THEN
BEGIN
item.expressionText ← text ← Rope.Replace[item.expressionText, loc, Rope.Length[oldText], newText];
SELECT item.mode FROM
showText, showExp => {item.buttonText ← text; item.buttonTextDirty ← TRUE};
showId, noShow, showValue => NULL;
ENDCASE => ERROR;
IF item.valueMode = expression THEN AnalyzeItemExpression[gc, item];
changes ← TRUE;
END;
END;
IF item.idName # NIL THEN
BEGIN
loc ← Rope.Find[item.idName, oldText];
IF loc # -1 THEN
BEGIN
item.idName ← text ← Rope.Replace[item.idName, loc, Rope.Length[oldText], newText];
SELECT item.mode FROM
showId => {item.buttonText ← text; item.buttonTextDirty ← TRUE};
showText, showExp, noShow, showValue => NULL;
ENDCASE => ERROR;
AnalyzeItemId[gc, item];
changes ← TRUE;
END;
END;
IF changes THEN
{HandleItemValueChanges[gc, item]; DisplayNewButtonText[gc, item]};
END;
CheckItemForDirtyData: PROCEDURE[gc: GCell, item: Item] =
BEGIN -- use different procedure for the just created case
IF item.viewerMode = textBox THEN
BEGIN
text: Rope.ROPE ← ViewerTools.GetContents[item.viewer];
SELECT item.mode FROM
showText, showExp => item.expressionText ← item.buttonText ← text;
showId => item.idName ← item.buttonText ← text;
noShow, showValue => NULL;
ENDCASE => ERROR;
ItemHasDirtyData[gc, item];
END;
END;
ItemHasDirtyData: PROCEDURE[gc: GCell, item: Item] =
BEGIN
SELECT item.mode FROM
showText, showExp, showId =>
BEGIN
item.buttonTextDirty ← TRUE;
item.buttonTextAnalyzed ← FALSE;
END;
noShow, showValue => NULL;
ENDCASE => ERROR;
IF NOT item.buttonTextAnalyzed THEN SELECT item.mode FROM
showExp =>
BEGIN
AnalyzeItemExpression[gc, item];
HandleItemValueChanges[gc, item];
END;
showId =>
BEGIN
AnalyzeItemId[gc, item];
HandleItemValueChanges[gc, item];
END;
showText, noShow, showValue => NULL;
ENDCASE => ERROR;
item.buttonTextAnalyzed ← TRUE;
END;
AbandonItem: PROCEDURE[item: Item] =
BEGIN
DeactivateAction[item.evalAction];
DeactivateDataItem[item.valueChanges];
DeactivateAction[item.assignAction];
ClearItemViewer[item];
nGCellItems ← nGCellItems - 1;
END;
ItemAcceptsSelection: PROCEDURE[item: Item] RETURNS[BOOLEAN] =
BEGIN
RETURN[ SELECT item.mode FROM
showText, showId, showExp, showValue => TRUE,
ENDCASE => FALSE];
END;
-- item specific computation procedures
AnalyzeItemExpression: PROCEDURE[gc: GCell, item: Item] =
BEGIN
BEGIN
expression: Expression;
IF item.valueMode # expression THEN RETURN;
IF gc.idTables[self] = NIL THEN RETURN;
IF item.mode = showText THEN RETURN;
expression ← Expressions.ParseExpression[item.expressionText
! Expressions.SyntaxError =>
BEGIN
IF item.mode # showExp THEN ERROR;
item.buttonText ← Rope.Cat["&&&", item.expressionText, "&&&"];
item.buttonTextDirty ← TRUE;
ViewerTools.SetContents[item.viewer, item.buttonText];
item.expressionBad ← TRUE;
GOTO syntaxError;
END];
item.expressionBad ← FALSE;
IF item.evalAction # NIL THEN
{Dependencies.DeactivateAction[item.evalAction]; item.evalAction ← NIL};
IF ExpressionNonConstant[expression] THEN
BEGIN
item.evalAction ← Dependencies.DefineAction[gc.ncGlobal.dSet, EvalItemExpression, item];
Dependencies.MarkActionDirty[item.evalAction];
END;
item.expression ← AnalyzeExpression[expression, gc.idTables, gc.ncGlobal.dSet, item.evalAction];
IF item.valueMode = expression AND item.evalAction = NIL THEN
SetItemValue[gc, item, Expressions.EvaluateExpression[item.expression]];
EXITS
syntaxError => NULL;
END;
END;
AnalyzeItemId: PROCEDURE[gc: GCell, item: Item] =
BEGIN
BEGIN
id: Identifier;
IF gc.idTables[self] = NIL THEN RETURN;
IF item.mode = showText THEN RETURN;
IF item.idName= NIL OR Rope.Equal[item.idName, ""] OR Rope.Equal[item.idName, " "] THEN GOTO idBad
ELSE
BEGIN
id ← Expressions.ParseIdentifier[item.idName
! Expressions.SyntaxError =>
BEGIN
IF item.mode # showId THEN ERROR;
item.buttonText ← Rope.Cat["&&&", item.idName, "&&&"];
item.buttonTextDirty ← TRUE;
ViewerTools.SetContents[item.viewer, item.buttonText];
GOTO idBad;
END];
item.idBad ← FALSE;
Dependencies.DeactivateAction[item.assignAction];
item.assignAction ← Dependencies.DefineAction[gc.ncGlobal.dSet, AssignItemValue, item];
item.id ← AnalyzeIdentifier[id, gc.idTables, gc.ncGlobal.dSet, item.assignAction];
Dependencies.MarkActionDirty[item.assignAction];
END;
EXITS
idBad =>
BEGIN
item.idBad ← TRUE;
Dependencies.DeactivateAction[item.assignAction]; item.assignAction ← NIL;
item.id ← [NIL];
END;
END;
END;
HandleItemValueChanges: PROCEDURE[gc: GCell, item: Item] =
BEGIN
valueChangesNeeded: BOOLEAN ← -- info passes through the item
((item.valueMode = rowSum AND gc.rowSummer # NIL) OR
(item.valueMode = colSum AND gc.colSummer # NIL) OR
(item.valueMode = expression AND item.evalAction # NIL))
AND
(((item.rowSumMode = addVal OR item.rowSumMode = subVal) AND gc.rowSummer # NIL) OR
((item.colSumMode = addVal OR item.colSumMode = subVal) AND gc.colSummer # NIL) OR
item.assignAction # NIL);
IF valueChangesNeeded THEN
BEGIN
IF item.valueChanges # NIL THEN Dependencies.DeactivateDataItem[item.valueChanges];
item.valueChanges ← Dependencies.DefineDataItem[ gc.ncGlobal.dSet];
Dependencies.MarkDataItemDirty[item.valueChanges];
IF item.valueMode = expression AND item.evalAction # NIL THEN
Dependencies.NoteThatActionModifiesDataItem[item.evalAction, item.valueChanges];
IF item.valueMode = rowSum AND gc.rowSummer # NIL THEN
Dependencies.NoteThatActionModifiesDataItem[gc.rowSummer, item.valueChanges];
IF item.valueMode = colSum AND gc.colSummer # NIL THEN
Dependencies.NoteThatActionModifiesDataItem[gc.colSummer, item.valueChanges];
IF item.rowSumMode # noEffect AND gc.rowSummer # NIL THEN
Dependencies.NoteThatDataItemIsReadByAction[item.valueChanges, gc.rowSummer];
IF item.colSumMode # noEffect AND gc.colSummer # NIL THEN
Dependencies.NoteThatDataItemIsReadByAction[item.valueChanges, gc.colSummer];
IF item.assignAction # NIL THEN
Dependencies.NoteThatDataItemIsReadByAction[item.valueChanges, item.assignAction];
END
ELSE
BEGIN
IF item.valueChanges # NIL THEN Dependencies.DeactivateDataItem[item.valueChanges];
item.valueChanges ← NIL;
END;
IF item.evalAction # NIL THEN
Dependencies.MarkActionDirty[item.evalAction];
IF gc.rowSummer # NIL THEN
Dependencies.MarkActionDirty[gc.rowSummer];
IF gc.colSummer # NIL THEN
Dependencies.MarkActionDirty[gc.colSummer];
IF item.assignAction # NIL THEN
Dependencies.MarkActionDirty[item.assignAction];
END;
EvalItemExpression: PROCEDURE[action: Action, info: REF ANY] =
BEGIN
item: Item ← NARROW[info];
gcSn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[item.da, item.selfI, item.selfJ].sn;
gc: GCell ← NARROW[gcSn.ref];
IF item.expressionBad THEN RETURN;
SetItemValue[gc, item, Expressions.EvaluateExpression[item.expression]];
END;
DoItemRowSum: PROCEDURE[gc: GCell, item: Item, sum: REAL] RETURNS[REAL] =
BEGIN
IF item.valueMode = rowSum THEN SetItemValue[gc, item, sum];
IF item.rowSumMode = addVal THEN sum ← sum + item.value;
IF item.rowSumMode = subVal THEN sum ← sum - item.value;
IF item.rowSumMode = setZero THEN sum ← 0;
RETURN[sum];
END;
DoItemColSum: PROCEDURE[gc: GCell, item: Item, sum: REAL] RETURNS[REAL] =
BEGIN
IF item.valueMode = colSum THEN SetItemValue[gc, item, sum];
IF item.colSumMode = addVal THEN sum ← sum + item.value;
IF item.colSumMode = subVal THEN sum ← sum - item.value;
IF item.colSumMode = setZero THEN sum ← 0;
RETURN[sum];
END;
SetItemValue: PROCEDURE[gc: GCell, item: Item, newVal: REAL] =
BEGIN
IF item.value # newVal OR item.firstTime OR item.viewerMode = textBox THEN
BEGIN
item.value ← newVal;
item.firstTime ← FALSE;
IF item.mode = showValue THEN
BEGIN
item.buttonText ← IF item.value = 0 AND item.zeroValText # NIL AND Rope.Length[item.zeroValText] # 0
THEN item.zeroValText
ELSE Convert.ValueToRope[Convert.Value[real[item.value]]];
item.buttonTextDirty ← TRUE;
DisplayNewButtonText[gc, item];
END;
IF item.valueChanges = NIL THEN -- must hand set a few things dirty, this call is probably occuring during check for dirty data.
BEGIN
IF item.rowSumMode # noEffect AND gc.rowSummer # NIL THEN
Dependencies.MarkActionDirty[gc.rowSummer];
IF item.colSumMode # noEffect AND gc.colSummer # NIL THEN
Dependencies.MarkActionDirty[gc.colSummer];
IF item.assignAction # NIL THEN
Dependencies.MarkActionDirty[item.assignAction];
END;
END;
END;
AssignItemValue: PROCEDURE[action: Action, info: REF ANY] =
BEGIN
item: Item ← NARROW[info];
IF NOT item.idBad THEN
SetAnalyzedIdentifier[item.id, item.value];
END;
-- item specific display procedures
ShowItem: PROCEDURE[item: Item, L, R, W: INTEGER, to: STREAM] =
BEGIN
length: INTEGER ← Rope.Length[item.buttonText];
fullSkip: INTEGER ← IF length < W THEN (W-length) ELSE 0;
leftSkip, rightSkip: INTEGER;
SELECT item.justification FROM
left =>
BEGIN
rightSkip ← fullSkip;
leftSkip ← 0;
END;
centered =>
BEGIN
leftSkip ← fullSkip/2;
rightSkip ← fullSkip - leftSkip;
END;
right =>
BEGIN
leftSkip ← fullSkip;
rightSkip ← 0;
END;
leftDecimal =>
BEGIN
leftSkip ← IF length < W AND item.textL < L THEN L - item.textL ELSE 0;
rightSkip ← fullSkip - leftSkip;
END;
rightDecimal =>
BEGIN
rightSkip ← IF length < W AND item.textR < R THEN R - item.textR ELSE 0;
leftSkip ← fullSkip - rightSkip;
END;
ENDCASE => ERROR;
FOR I: INTEGER IN [1..leftSkip] DO
Put[to, char[' ]];
ENDLOOP;
Put[to, rope[item.buttonText]];
FOR I: INTEGER IN [1..rightSkip] DO
Put[to, char[' ]];
ENDLOOP;
END;
PrePaintItem: PROCEDURE[gc: GCell, item: Item, y, x, L, R, W: INTEGER] =
BEGIN
leftGap: INTEGER;
item.viewerY ← y;
item.viewerW ← IF item.buttonW < W THEN item.buttonW ELSE W;
SELECT item.justification FROM
left => leftGap ← 0;
centered => leftGap ← (W - item.viewerW)/2;
right => leftGap ← (W-item.viewerW);
leftDecimal => leftGap ← IF item.buttonW < W AND item.buttonL < L
THEN L - item.buttonL
ELSE 0;
rightDecimal =>
BEGIN
fullGap: INTEGER ← IF item.buttonW < W THEN W-item.buttonW ELSE 0;
rightGap: INTEGER ← IF item.buttonW < W AND item.buttonR < R
THEN R - item.buttonR
ELSE 0;
leftGap ← fullGap - rightGap;
END;
ENDCASE => ERROR;
item.viewerX ← x + leftGap;
DisplayButtonTextAsButton[gc, item];
END;
NoteCoordinatesItem: PROCEDURE[item: Item, da: StructureNodes.DisplayArray, I, J, x: CARDINAL] =
BEGIN
item.da ← da;
item.selfI ← I;
item.selfJ ← J;
item.selfItemX ← x;
SELECT item.viewerMode FROM
nil => NULL;
button, textBox => ViewerOps.AddProp[item.viewer, $GCellItem, NEW[ItemRefDescBody ← [da, I, J, x]]];
ENDCASE;
END;
ComputeTextShape: PROCEDURE[item: Item] =
BEGIN
IF item.buttonTextDirty THEN
BEGIN
item.buttonTextDirty ← FALSE;
item.buttonShapeDirty ← TRUE;
IF item.buttonText = NIL THEN
BEGIN
item.buttonL ← item.buttonA ← item.buttonB ← item.buttonR ← item.buttonW ← 0;
item.buttonHeight ← 0;
item.textL ← item.textA ← item.textB ← item.textR ← item.textW ← 0;
RETURN;
END;
SELECT item.justification FROM
left, centered, right =>
BEGIN
item.buttonL ← item.buttonA ← item.buttonB ← item.buttonR ← 0;
item.textL ← item.textA ← item.textB ← item.textR ← 0;
item.buttonW ← VFonts.StringWidth[item.buttonText] + 20;
item.buttonHeight ← VFonts.FontHeight[] + 5;
item.textW ← Rope.Length[item.buttonText];
END;
leftDecimal =>
BEGIN
decimals: CARDINAL ← 0;
length: CARDINAL ← Rope.Length[item.buttonText];
FOR I: CARDINAL DECREASING IN [0..length) DO
IF Rope.Fetch[item.buttonText, I] = '. THEN
{decimals ← length - I; EXIT};
IF NOT Rope.Digit[Rope.Fetch[item.buttonText, I]] THEN EXIT;
ENDLOOP;
item.buttonL ← VFonts.StringWidth[Rope.Substr[item.buttonText, 0, length-decimals]]+5;
item.buttonA ← VFonts.StringWidth[Rope.Substr[item.buttonText, length-decimals, length]];
item.buttonB ← item.buttonR ← 0;
item.buttonW ← VFonts.StringWidth[item.buttonText] + 20;
item.textL ← length-decimals; item.textA ← decimals;
item.buttonHeight ← VFonts.FontHeight[] + 5;
item.textB ← item.textR ← 0;
item.textW ← length;
END;
rightDecimal =>
BEGIN
decimals: CARDINAL ← 0;
length: CARDINAL ← Rope.Length[item.buttonText];
FOR I: CARDINAL DECREASING IN [0..length) DO
IF Rope.Fetch[item.buttonText, I] = '. THEN
{decimals ← length - I; EXIT};
IF NOT Rope.Digit[Rope.Fetch[item.buttonText, I]] THEN EXIT;
ENDLOOP;
item.buttonL ← item.buttonA ← 0;
item.buttonB ← VFonts.StringWidth[Rope.Substr[item.buttonText, 0, length-decimals]];
item.buttonR ← VFonts.StringWidth[Rope.Substr[item.buttonText, length-decimals, length]]+5;
item.buttonW ← VFonts.StringWidth[item.buttonText] + 20;
item.buttonHeight ← VFonts.FontHeight[] + 5;
item.textL ← item.textR ← 0;
item.textB ← length-decimals; item.textR ← decimals;
item.textW ← length;
END;
ENDCASE => ERROR;
END;
END;
DisplayNewButtonText: PROCEDURE[gc: GCell, item: Item] =
BEGIN
SELECT item.viewerMode FROM
textBox => DisplayButtonTextAsButton[gc, item];
button => Buttons.ReLabel[item.viewer, item.buttonText, TRUE];
nil => NULL;
ENDCASE => ERROR;
END;
DisplayButtonTextAsButton: PROCEDURE[gc: GCell, item: Item] =
BEGIN
button: Viewer;
IF ViewerTools.GetSelectedViewer[] = item.viewer THEN
BEGIN
gcSn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[item.da, item.selfI, item.selfJ].sn;
ForceItemSelection[gcSn, gc, item] -- display it as a text viewer anyway, possibly with new text, and certainly it will continue to be selected.
END
ELSE
BEGIN
ClearItemViewer[item];
button ← Buttons.Create[
info: [
name: item.buttonText,
wx: item.viewerX,
ww: item.viewerW,
wy: item.viewerY,
wh: VFonts.FontHeight[]+5,
parent: gc.vParent,
border: FALSE,
scrollable: FALSE],
proc: DisplayButtonTextAsTextViewer,
clientData: NIL,
paint: TRUE,
fork: TRUE];
ViewerOps.AddProp[button, $GCellItem, NEW[ItemRefDescBody ← [item.da, item.selfI, item.selfJ, item.selfItemX]]];
item.viewerMode ← button;
item.viewer ← button;
END;
END;
DisplayButtonTextAsTextViewer: Buttons.ButtonProc = TRUSTED
BEGIN
itemRefDesc: ItemRefDesc ← NARROW[ViewerOps.FetchProp[item.viewer, $GCellItem]];
gcSn: StructureNodes.StructureNode; gc: GCell; item: Item;
DBTATVInternal: PROCEDURE =
BEGIN
IF item.viewer # NARROW[parent, ViewerClasses.Viewer] THEN
RETURN; -- some concurrent activity must have removed this button from the item, presumably it is being destroyed.
ForceItemSelection[gcSn, gc, item]
END;
[gcSn, gc, item] ← DecodeItemRefDesc[itemRefDesc];
gc.ncGlobal.executeInMonitor[gc.ncGlobal, DBTATVInternal];
END;
ForceItemSelection: PROCEDURE[gcSn: StructureNodes.StructureNode, gc: GCell, item: Item] =
BEGIN
textBox: ViewerClasses.Viewer;
NoteItemSelection[gc.displayer, gcSn, item.selfJ];
ClearItemViewer[item];
textBox ← ViewerTools.MakeNewTextViewer[
info: [
parent: gc.vParent,
wx: item.viewerX,
wy: item.viewerY,
ww: item.viewerW,
wh: VFonts.FontHeight[]+5,
border: FALSE,
scrollable: FALSE],
paint: FALSE];
IF NOT item.editable THEN ViewerTools.InhibitUserEdits[textBox];
ViewerTools.SetContents[textBox, item.buttonText];
ViewerOps.AddProp[textBox, $GCellItem, NEW[ItemRefDescBody ← [item.da, item.selfI, item.selfJ, item.selfItemX]]];
ViewerTools.SetSelection[textBox, NIL];
item.viewer ← textBox;
item.viewerMode ← textBox;
END;
ClearItemViewer: PROCEDURE[item: Item] =
BEGIN
IF item.viewerMode = button THEN
Buttons.ReLabel[item.viewer, "", TRUE];
IF item.viewerMode = textBox THEN
ViewerTools.SetContents[item.viewer, "", TRUE];
IF item.viewerMode # nil THEN
BEGIN
ViewerOps.DestroyViewer[item.viewer, FALSE];
item.viewerMode ← nil;
item.viewer ← NIL;
END;
END;
ForceNoteItemSelection: PUBLIC PROCEDURE[itemRef: REF ANY] =
BEGIN
item: Item ← NARROW[itemRef];
itemRefDesc: ItemRefDesc ← NARROW[ViewerOps.FetchProp[item.viewer, $GCellItem]];
gcSn: StructureNodes.StructureNode;
gc: GCell;
[gcSn, gc, ] ← DecodeItemRefDesc[itemRefDesc];
NoteItemSelection[gc.displayer, gcSn, item.selfJ];
END;
-- item ref descriptors
DecodeItemRefDesc: PROCEDURE[itemRefDesc: ItemRefDesc] RETURNS[gcSn: StructureNodes.StructureNode, gc: GCell, item: Item] =
BEGIN
IF itemRefDesc.da = NIL THEN ERROR;
IF itemRefDesc.I = 0 THEN ERROR;
IF itemRefDesc.J = 0 THEN ERROR;
gcSn ← StructureNodes.GetElementAt[itemRefDesc.da, itemRefDesc.I, itemRefDesc.J].sn;
gc ← NARROW[gcSn.ref];
item ← gc.items; -- tentative
FOR J: CARDINAL IN [1..itemRefDesc.J) DO item ← item.next ENDLOOP;
END;
-- BELONGS IN EXPRESSION CODE
ExpressionNonConstant: PROCEDURE[expression: Expression] RETURNS[BOOLEAN] =
BEGIN -- this belongs in expresion code
E1: Expression;
T: Token;
IF expression = NIL THEN RETURN[FALSE];
IF expression.operator # topExp THEN RETURN[TRUE];
IF expression.data2 = NIL THEN RETURN[TRUE];
E1 ← NARROW[expression.data2];
IF E1.operator # token THEN RETURN[TRUE];
IF E1.data1 = NIL THEN RETURN[TRUE];
T ← NARROW[E1.data1];
IF T.operator = number THEN RETURN[FALSE];
RETURN[TRUE];
END;
END..
-- September 23, 1982 5:11 pm: Sturgis, started GCellImpl.mesa
-- October 1, 1982 3:35 pm: add code to create cells with standard types.
-- RET: October 1, 1982 3:50 pm: forgot to define the assorted dependency DataItems in each item. Also, add code to deactivate them at abandon time.
-- RTE: October 1, 1982 4:12 pm: attempted to analyze an expression before the gc idtables had been set up. Also, did not reanalyze after resetting the gcidtables.
-- RTE: October 1, 1982 4:23 pm: attempted to analyze expression text in a showText case.
-- RTE: October 1, 1982 4:36 pm: analyzeItemId passed analyzeId item.id, before item.id had been initialized.
-- RTE: October 1, 1982 4:52 pm: forgot to set lead to 0 in PrePrePaintCell.
-- RTE: October 1, 1982 5:01 pm: never called ComputeTextShape.
-- RTE: October 2, 1982 3:24 pm: appropriate data items were not being set dirty when expressions or identifiers were analyzed. Further, seems useful to add idChanges as a datatem.
-- RTE: October 2, 1982 3:33 pm: expressoin analyze and id analyze did not set epxression (id) bad ← FALSE.
-- RTE: October 2, 1982 3:52 pm: assign item val, in the case of item mode = ShowValue should reset the screen text.
-- October 2, 1982 4:30 pm: add a call to NoteSelection.
-- October 2, 1982 5:12 pm: accept DeSelect as an action, do CheckForDirtyData;
RTE: October 2, 1982 6:17 pm: could not do a NARROW[info] in ActGCell, did not notice that GCellImpl failed to compile, ran with old version of code as binder did not notice. When trying to set a break point, BugBane said "Break NOT set: can't get source name". Not very informative. Finally figured it out using CoPilot, which complained explicitly about dates, exhibiting them.
-- RTE: October 2, 1982 6:26 pm: must fork the button proc for converting to a text box, so as to be able to use bugbane. Eventually this will have to come in through a monitor, but later.
-- change: October 3, 1982 12:55 pm: modify selection mechanism, so that ln.Act returns enclosing context when it fails to perform the act.
change: October 3, 1982 2:04 pm: arrange for button proc to execute inside the NewCalc global monitor. There may have been a race on an earlier execution, and so time to do it right.
RTE: October 3, 1982 2:28 pm: read and write item (from and to save file) did not include noShow case.
RTE: October 3, 1982 2:49 pm: saving an item left a trailing blank. HIgher level code expected no trailing blanks duringload. so remove the trailing blank.
RTE: October 3, 1982 3:31 pm: LoadGCell did not set gc.nItems.
RTE: October 3, 1982 4:11 pm: PreShow was supposed to set gc.textNLines, but didnot.
RTE: October 3, 1982 4:18 pm: ShowGCellLine should start lx at 1, not 0, since first lineX arguemnt is 1.
RTE: October 3, 1982 4:45 pm: ShowLine, after passing all the items, should put out W blanks.
change: October 4, 1982 2:37 pm: add code to handle "ZeroValText". Also, add a version on saved form of an item, so that new fields can be easily added.
RTE: October 4, 1982 2:54 pm: forgot to have GetItemDescriptor also deliver the ZeroValText field. And fact that ropes are default NIL prevented the compiler from complaining.
RTE: October 4, 1982 4:22 pm: load did not read the version number at correct time. Also, modify ShowValue to not use ZeroValueText if lenght of that text is 0. (as saving and the reloading converts nil rope to zero lenght text)
RTE: October 4, 1982 4:50 pm: ReadOneItem did not return ZeroValText, again the compiler did not tell me, but returned default NIL.
RTE: October 5, 1982 4:46 pm: code for reading a rope blanks and left braces. Want it to skip blanks and first left brace, but no characters beyond the first brace. Added a test in InitialSkip to quit the skip on any character beyond the first left brace.
change: October 6, 1982 5:39 pm: NoteSelection is now declared in GCell, and takes an additional param.
RTE: October 7, 1982 2:57 pm: ClearGCell did not set nItems to 0, hence nItems increased each time the GCell was set.
change: October 8, 1982 1:16 pm: add slot type to standard cells.
RTE: October 8, 1982 1:52 pm: wrong count at head of descriptor for slot type standard cell.
change: October 8, 1982 4:09 pm: arrange to prepaint an item only if external params have changed, or item has changed.
RTE: October 8, 1982 4:18 pm: item.buttonHeight was not getting set for leftDecimal or rightDecimal justification.
change: October 10, 1982 11:51 am: convert to 3.4 (the button viewer is now named "parent", and is of type REF ANY).
RTE: October 10, 1982 12:44 pm: read and write were unable to handle mode = showId.
RTE: October 10, 1982 1:07 pm: identifier in textBox form clips off trailing characters. try increasing buttonW to +20 rather than +10.
RTE: October 10, 1982 1:15 pm: hmm, previous fix did not work; anyway, id.name is looking like a decimal point, so modify decimal point code to stop looking for one if a non digit is found during right to left scan.
RTE: October 10, 1982 1:26 pm: ah, did not find all the settings of item.buttonW. eventually I should clean up this code so that there is only one such setting, or I use a named value.
October 10, 1982 2:09 pm: get Action from StrucutreNodes, rather than StructureNode.
October 11, 1982 1:47 pm: make (prefix) space a cell, rather than item, property. Set TextSpace to 2 for the time being, until it becomes a modifiable property.
October 14, 1982 4:04 pm: Note selection now returns more information, which must be passed in at creation time.
October 16, 1982 3:22 pm: conditionally entry to monitor in GetGCellContents and SetGCellContents.
-- October 23, 1982 11:10 am: row sum and col sum code, also only create needed actions and dataitems.
-- RTE: October 23, 1982 12:11 pm: assorted problems handling empty ids, and getting unwanted parse errors.
-- RTE: October 23, 1982 12:27 pm: EvalExpression now has to call SetItemValue, so as to get button created if needed. (AssignItemValue no longer does this.)
-- RTE: October 23, 1982 12:36 pm: had to add a "firstTime" boolean to an item to get value to display the first time, since I had also placed in code not too redisplay value if it hadnt changed.
-- RTE: October 23, 1982 12:56 pm: (symptom: multiple assignment to one dataitem) (cause: analyzing item expression when value mode # expression).
-- RTE: October 23, 1982 1:05 pm: another occurance of the above, found yet another call to AnalyzeItemExpression.
-- RTE: October 23, 1982 1:12 pm: yet another, so, change code so that AnalyzeItemExpression returns if valueMOde # expression.
-- RTE: October 23, 1982 2:56 pm: attempt to read new rope form fails, must set d to 0 before each input rope.
-- change: October 24, 1982 12:48 pm: add code to conditionally add item.valueChanges dataItem.
-- RTEs(2): October 24, 1982 1:05 pm: several attempts to link dependencies to not yet created dependencies, or wrongly named dependencies.
-- change: October 24, 1982 1:13 pm: add carefully taylored test for constant expression. Should eventually move this to expression code.
-- CTE: October 24, 1982 1:18 pm: well, it was taylored using the debugger, but I didn't notice the Ref Anys tying it together. So, have to add some NARROWs.
-- RTE: October 24, 1982 1:30 pm: still got it wrong, one of the types as a Token, not an Expression. Sure would have been helpful if the debugger had stated the types.
-- RTE: October 24, 1982 1:35 pm: must put the analyzed expression in the item, even if a constant. So that it can subsequently evaluated.
-- RTE: October 24, 1982 1:50 pm: now we have to hand set outgoing actions dirty when the expression is edited to a constant.
-- RTE: October 25, 1982 4:09 pm: blank expressions produce NIL, which tripped up the non contant test.
-- change: October 26, 1982 3:21 pm: begin changes to support Next cell feature, also alow for selection of textBox form items. Also, automatic text selection when changing to a textbox.
-- RTE: October 26, 1982 6:02 pm: ItemSelection mechanism counted items from 0 rather than 1.
-- RTE: October 26, 1982 6:08 pm: and that fouled up the move to next.
-- RTE: October 28, 1982 2:52 pm: modify ForceItemSelection to handle non editable items by building a text viewer, and then inhibiting user edit, rather than refusing to build the text viewer. Thus the new selection mechanism that uses the Viewer selection concept will work on non editable cells.
-- RTE: October 28, 1982 2:59 pm: checkitemfordirty data generates an error if in form of text box and not editable. remove this error;
-- RTE: October 28, 1982 3:24 pm: modify DisplayButtonTextAsButton to check to see if the item viewer is the [Viewer] selected viewer. If so, display it as a text viewer anyway, and preserve the selection.
-- RTE: October 28, 1982 3:37 pm: add showValue as a case that will accept selection. This prevents selection from disapearing during certain delete column or row actions.
-- RTE: October 30, 1982 4:34 pm: symptom: cells showing value do not get displayed just after begin set by celltool. Fix: add a "show" param to CreateItem, which is looked at in showValue case, and if true, calls SetItemValue. Also rig PrePaintGCell to PrePaint the item (in addition to previous cases) if the item.mode # button.
-- RTE: October 31, 1982 4:09 pm: remove the error from DisplayButtonTextAsTextViewer, simply return if the item.viewer is not the argument to the button proc. Presumably some concurrent process has removed the button from the item, and is destroying the button.
-- RTEs: November 2, 1982 2:57 pm: 1) modify HandleValueChanges so that in case ValueChanges is not to exist, then hand marks assorted actions dirty. 2) add an unPainted field to a gCell. Force building of an item viewer during prePaint if the gCell is unpainted. 3) remove search for item in Act code. This was causing errors during DeSelect, which will be removed also.
-- change: November 2, 1982 4:10 pm: add SubstituteInGCell.
-- November 2, 1982 4:27 pm: add 2 ref any params to ActGCell.
-- RTE: November 2, 1982 5:27 pm: specified incorrect length of text to replace.
-- RTE: November 2, 1982 5:47 pm: only substituted in the visible text.
-- RTE: November 2, 1982 5:53 pm: substituted in the wrong text.
-- RTE: November 2, 1982 6:02 pm: old ItemIs dirty code expected only the displayed fields to change. So had to put in specific code to call the appropriate anal routines.
-- change: November 3, 1982 3:33 pm: make SubstituteInGCell a logical node proc.
-- change: November 4, 1982 1:31 pm: do not create valueChangesDataItem when creating an item.
-- change: November 14, 1982 1:49 pm: conform to new selection logic
-- change: November 14, 1982 2:49 pm: add setZero mode to col and row summs.
-- RTE: November 14, 1982 4:00 pm: during create item, even in showText case, must call HandleItemValueChanges, because some of the modes may affect rowSummer or colSummer, therefore, want associated actions marked dirty.
-- CTE: February 11, 1983 10:54 am: convert to 4.0. Major change in IO parsing mechanism. Somewhat cleaner, Hvae to use GetSequence, and it has the property that if the char being lookd at when quit is returned is not included, then it will be seen by the next reader.
-- RTE: February 11, 1983 2:24 pm: replace calls on GetAtom by calls on NARROW[GetRefAny[]].
-- RTE: February 11, 1983 2:38 pm: CProc for current text version did not terminate so as to through away the trailing '}.
-- change: February 25, 1983 1:31 pm: add counters for nGCells and nGCellItems.
-- February 27, 1983 1:52 pm: convert from logical and display nodes to structurenodes. Objective is to avoid the back poitners from GCell to the nodes, and the two forward poitners from the cells to GCell. Took aout 45 minutes.
-- RTE: February 28, 1983 10:28 am: selection mechanism failed, had to modify GetContextDACell to also return the sn of the gcell contained in the DACell.
-- remark: February 28, 1983 2:04 pm: previous changes only reduced ref counts by 1, not the expected 2. I suspect that someone is holding two pointers to gcell.sn. Not sure where.
--remark: February 28, 1983 2:05 pm: now change the selectoin mechanism to store a pointer to the display array holding the gcell, along with coordinates, in the viewer. This requires a co-ordinaote change notification routine.
-- remark: February 28, 1983 2:17 pm: first add dummy note coordinates procedure.
-- February 28, 1983 2:56 pm: change selection mechanism to store da and coordinates in viewer, rather than item.
-- UGH: February 28, 1983 4:47 pm: Storage overflow in pass 3. SO have to split of items.
-- February 28, 1983 4:47 pm: remark: Also, need to put indirect addressing info in the actions associated with an item, and this will require the ability to update that info when coordinates change.