-- HCellImpl.mesa
-- Last Edited by: Sturgis, August 15, 1985 11:21:26 am PDT

DIRECTORY
Ascii USING[Digit],
Buttons USING[ButtonProc, Create, ReLabel],
Dependencies USING[Action, DataItem, DeactivateAction, DeactivateDataItem, DefineAction, DefineDataItem, MarkActionDirty, NoteThatActionModifiesDataItem, NoteThatDataItemIsReadByAction],
Expressions USING[AnalyzedHCellExpression, AnalyzedHCellIdentifier, AnalyzeHCellExpression, AnalyzeHCellIdentifier, EvaluateExpression, Expression, HCellExpression, HCellIdentifier, ParseHCellExpression, ParseHCellIdentifier, SetAnalyzedHCellIdentifier, SyntaxError, Token],
GCell USING[NoteItemSelection],
HCells USING[DisplayMode, IdMode, Justification, RowColSumMode, ValueMode],
IO USING[char, CR, int, Put, PutFR, rope, STREAM],
NewCalcGlobal USING[Displayer, Document],
Real USING[RoundLI],
Rope USING[Cat, Concat, Equal, Fetch, Find, FromChar, Index, Length, ROPE, Substr],
StructureNodes USING[Action, DACell, DisplayArray, GetElementAt, MarkElementDirty, PrePaintPass, StructureNode, StructureNodeBody, StructureNodeProcs],
VFonts USING[FontHeight, StringWidth],
ViewerClasses USING[Viewer],
ViewerEvents USING[EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc],
ViewerOps USING[AddProp, DestroyViewer, FetchProp, PaintViewer],
ViewerTools USING[GetContents, GetSelectedViewer, InhibitUserEdits, MakeNewTextViewer, SetContents, SetSelection];


HCellImpl: PROGRAM IMPORTS Ascii, Buttons, Dependencies, Expressions, GCell, IO, Real, Rope, StructureNodes, VFonts, ViewerEvents, ViewerOps, ViewerTools EXPORTS HCells =

BEGIN
HCellViewerMode: TYPE = {nil, nonNil};
HCellItemViewerMode: TYPE = {nil, textBox, button};
HCellComplexDependencyControl: TYPE = REF HCellComplexDependencyControlBody;
HCellComplexDependencyControlBody: TYPE = RECORD[
rowColDataItem: Dependencies.DataItem ← NIL,
rowColSum: REAL,
expressionEvalAction: Dependencies.Action ← NIL,
valueDataItem: Dependencies.DataItem ← NIL,
unAnalyzedId: Expressions.HCellIdentifier ← [NIL],
analyzedId: Expressions.AnalyzedHCellIdentifier
];
HCellItemSeq: TYPE = REF HCellItemSeqBody;
HCellItemSeqBody: TYPE = RECORD[
nItems: INTEGER,
item: SEQUENCE maxItems: CARDINAL OF HCellItem];
HCellItem: TYPE = REF HCellItemBody;
HCellItemBody: TYPE = RECORD[
itemIndex: INTEGER,
viewerMode: HCellItemViewerMode ← nil,
viewer: ViewerClasses.Viewer ← NIL,
registration: ViewerEvents.EventRegistration ← NIL,
showControl: HCellShowControl ← NIL,
prePaintControl: HCellPrePaintControl ← NIL,
viewerDirty: BOOLEAN ← FALSE,
itemViewerX, itemViewerY, itemViewerW: INTEGER ← 0 -- these control the location of the viewer, and are set by prePaint code
];
HCellShowControl: TYPE = REF HCellShowControlBody;
HCellShowControlBody: TYPE = RECORD[
textH, textL, textA, textB, textR, textW: INTEGER];
HCellPrePaintControl: TYPE = REF HCellPrePaintControlBody;
HCellPrePaintControlBody: TYPE = RECORD[
screenTextH, screenTextL, screenTextA, screenTextB, screenTextR, screenTextW: INTEGER, wasSelected: BOOLEAN];
HCell: TYPE = REF HCellBody;
HCellBody: PUBLIC TYPE = RECORD[
-- for debugging
interesting: BOOLEAN ← FALSE,
-- the following describe the cell position in the data structure
document: NewCalcGlobal.Document,
displayer: NewCalcGlobal.Displayer,
da: StructureNodes.DisplayArray,
hcaSn: StructureNodes.StructureNode,
selfI, selfJ: CARDINAL ← 0,
colSummer, rowSummer, itemArrayColSummer: Dependencies.Action,
leftCell, rightCell: HCell,
-- the following control the cell value
editable: BOOLEAN ← FALSE,
pageName: Rope.ROPE,
arrayIdText: Rope.ROPE,
text: Rope.ROPE,
-- dependency control
complex: HCellComplexDependencyControl,
-- input information for the cell value
unAnalyzedExpression: Expressions.HCellExpression,
analyzedExpression: Expressions.AnalyzedHCellExpression,
-- the value
valueMode: HCells.ValueMode ← none,
firstTime: BOOLEAN ← TRUE,
value: REAL ← 0,
-- the value may be fed to a row or column summer
rowSumMode: HCells.RowColSumMode ← none,
colSumMode: HCells.RowColSumMode ← none,
-- the value may be fed to an identifier
idMode: HCells.IdMode ← none,
-- the following may depend on the value
displayMode: HCells.DisplayMode ← text,
zeroValueText: Rope.ROPE,
displayText: Rope.ROPE,
analyzedDisplayText: TextSeq ← NIL,
displayTextDirty: BOOLEAN ← TRUE,
-- visual text control information
justification: HCells.Justification ← right,
viewerMode: HCellViewerMode ← nil,
vParent: ViewerClasses.Viewer,
itemSeq: HCellItemSeq
];
HCellRefDesc: TYPE = REF HCellRefDescBody;
HCellRefDescBody: TYPE = RECORD[
document: NewCalcGlobal.Document, da: StructureNodes.DisplayArray, I, J, x: CARDINAL];
-- misc
NewHCellItemSeq: PROCEDURE[nItems: CARDINAL] RETURNS[HCellItemSeq] =
BEGIN
seq: HCellItemSeq ← NEW[HCellItemSeqBody[nItems]];
seq.nItems ← nItems;
FOR J: CARDINAL IN [0..nItems) DO
seq.item[J] ← NEW[HCellItemBody ← [itemIndex: J]]
ENDLOOP;
RETURN[seq];
END;
-- create time routines
CreateHCell: PUBLIC PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, hcaSn: StructureNodes.StructureNode, vParent: ViewerClasses.Viewer] RETURNS[StructureNodes.StructureNode, HCell] =
BEGIN
cell: HCell ← NEW[HCellBody ← [vParent: vParent, document: document, displayer: displayer, hcaSn: hcaSn]];
RETURN[NEW[StructureNodes.StructureNodeBody ← [cell, HCellSNProcs]], cell];
END;
-- these routines are called from the containing HCellArray
SetHCellParameters: PUBLIC PROCEDURE[cell: HCell, rowSummer, colSummer, itemArrayColSummer: Dependencies.Action, leftCell, rightCell: HCell, editable: BOOLEAN, text: Rope.ROPE, valueMode: HCells.ValueMode, rowSumMode: HCells.RowColSumMode, colSumMode: HCells.RowColSumMode, idMode: HCells.IdMode, displayMode: HCells.DisplayMode, justification: HCells.Justification, zeroValueText: Rope.ROPE, pageName: Rope.ROPE, arrayIdText: Rope.ROPE] =
BEGIN
cell.colSummer ← colSummer;
cell.rowSummer ← rowSummer;
cell.itemArrayColSummer ← itemArrayColSummer;
cell.leftCell ← leftCell;
cell.rightCell ← rightCell;
cell.editable ← editable;
IF text # NIL THEN cell.text ← text;
cell.valueMode ← valueMode;
cell.rowSumMode ← rowSumMode;
cell.colSumMode ← colSumMode;
cell.idMode ← idMode;
cell.displayMode ← displayMode;
IF NOT Rope.Equal[cell.zeroValueText, zeroValueText] THEN
BEGIN
cell.zeroValueText ← zeroValueText;
cell.firstTime ← TRUE;
END;
cell.justification ← justification;
cell.pageName ← pageName;
cell.arrayIdText ← arrayIdText;
ForcedSwitchToSimpleMode[cell];
IF NOT ConditionalSwitchToSimpleMode[cell] THEN SwitchToComplexMode[cell];
NoticeNewTextInHCell[cell];
Dependencies.MarkActionDirty[cell.rowSummer];
Dependencies.MarkActionDirty[cell.colSummer];
END;
SetHCellText: PUBLIC PROCEDURE[cell: HCell, text: Rope.ROPE] =
{IF text # NIL THEN
{cell.text ← text; NoticeNewTextInHCell[cell]}};
-- perhaps this should look for a text box, and use that, if any?
-- the issue is undetected edits?
GetHCellText: PUBLIC PROCEDURE[cell: HCell] RETURNS[Rope.ROPE] =
{RETURN[cell.text]};
-- this routine is called from NewCalcImpl during the selection process at the start of an action routine.
ForceNoteHCellSelection: PUBLIC PROCEDURE[cellRef: REF ANY] =
BEGIN
cellRefDesc: HCellRefDesc ← NARROW[cellRef];
cell: HCell;
cellSn: StructureNodes.StructureNode;
[cell, cellSn] ← DecodeHCellRefDescriptor[cellRefDesc];
GCell.NoteItemSelection[cell.displayer, cellSn, 0];
END;
-- these routines are called through the containing structure node
HCellSNProcs: REF StructureNodes.StructureNodeProcs ← NEW[StructureNodes.StructureNodeProcs ← [
 CheckHCellForDirtyData,
 PreShowHCell,
 ShowHCellLine,
 PrePrePaintHCell,
 PrePaintHCell,
 PaintHCell,
 UnPaintHCell,
 NoteEnclosingCellHCell,
 NoteCoordinatesHCell,

 SaveHCell,
 ActHCell,
 SubstituteInHCell,
 AbandonHCell,
 VerifyHCell
 ]];
UnPaintHCell: PROCEDURE[node: StructureNodes.StructureNode] = {NULL};
NoteEnclosingCellHCell: PROCEDURE[node: StructureNodes.StructureNode, dac: StructureNodes.DACell] = {NULL};
NoteCoordinatesHCell: PROCEDURE[node: StructureNodes.StructureNode, da: StructureNodes.DisplayArray, I, J: CARDINAL] =
BEGIN
cell: HCell ← NARROW[node.ref];
cell.da ← da;
cell.selfI ← I;
cell.selfJ ← J;
END;
SaveHCell: PROCEDURE[node: StructureNodes.StructureNode, to: IO.STREAM] = {ERROR};
ActHCell: PROCEDURE[info: StructureNodes.StructureNode, I, J: CARDINAL, action: StructureNodes.Action, p1, p2: REF ANY] RETURNS[StructureNodes.StructureNode, CARDINAL, CARDINAL] =
BEGIN
cell: HCell ← NARROW[info.ref];
SELECT action FROM
MarkCellInteresting => cell.interesting ← TRUE;
MoveSelectionToNext => NULL;
MoveSelectionToPrevious => NULL;
ENDCASE => NULL;
RETURN[cell.hcaSn, cell.selfI, cell.selfJ];
END;
SubstituteInHCell: PROCEDURE[info: StructureNodes.StructureNode, newText: Rope.ROPE, oldText: Rope.ROPE] = {NULL};
AbandonHCell: PROCEDURE[node: StructureNodes.StructureNode] =
BEGIN
cell: HCell ← NARROW[node.ref];
ForcedSwitchToSimpleMode[cell];
IF cell.itemSeq # NIL THEN
FOR J: INT IN [0..cell.itemSeq.nItems) DO
[] ← ClearHCellItemViewer[cell.itemSeq.item[J]];
ENDLOOP;
cell.itemSeq ← NIL;
cell.leftCell ← cell.rightCell ← NIL;
END;
VerifyHCell: PROCEDURE[node: StructureNodes.StructureNode] = {NULL};
-- these routines are run at dirty data check time
-- There are two dependency modes for an HCell
-- 1) simple: In simple mode, there are no actions or data items associated with a cell. The value may change for only one reason: the text is edited. In this case, the value is recomputed at DirtyData time, the display is updated, and any affected row or column sums are marked dirty.
-- 2) normal: In normal mode, there are two dataItems, and one action. One data item is marked dirty by dirty incomming row or columns sums, and the other is marked dirty when the value is updated by the one action. The action is dependent on the rowColumn data item, on the expression, and on the value data item of the cell to its left (if needed).
-- someday, should move the button/viewer distinctions to item code in the text display code area
CheckHCellForDirtyData: PROCEDURE[node: StructureNodes.StructureNode] =
BEGIN
cell: HCell ← NARROW[node.ref];
assumeDirty: BOOLEAN ← FALSE; -- tentative;
text: Rope.ROPE;
IF cell.interesting THEN {A: INTEGER; A ← A};
IF cell.itemSeq = NIL THEN RETURN; -- assume text is not dirty
IF NOT cell.editable THEN RETURN; -- assume text is not dirty
FOR J: INTEGER IN [0..cell.itemSeq.nItems) DO
IF cell.itemSeq.item[J].viewerMode = textBox THEN
BEGIN
assumeDirty ← TRUE;
cell.itemSeq.item[J].viewer.newVersion ← FALSE; -- thus my new version proc should get called at next edit of the text box
EXIT;
END;
ENDLOOP;
IF NOT assumeDirty THEN RETURN;
text ← ViewerTools.GetContents[cell.itemSeq[0].viewer];
IF cell.itemSeq[0].viewerMode = button AND cell.itemSeq.nItems # 1 THEN
text ← Rope.Concat[text, Rope.FromChar[IO.CR]];
FOR I: INTEGER IN (0..cell.itemSeq.nItems) DO
text ← Rope.Concat[text, ViewerTools.GetContents[cell.itemSeq[I].viewer]];
IF cell.itemSeq[I].viewerMode = button AND (I+1) # cell.itemSeq.nItems THEN
text ← Rope.Concat[text, Rope.FromChar[IO.CR]];
ENDLOOP;
cell.text ← text;
NoticeNewTextInHCell[cell];
END;
-- this is also called if modes change
NoticeNewTextInHCell: PROCEDURE[cell: HCell] =
BEGIN
IF cell.valueMode = exp OR cell.valueMode = lvTimesExp THEN
BEGIN
IF cell.displayMode # text THEN ERROR;
cell.unAnalyzedExpression ← Expressions.ParseHCellExpression[cell.text
! Expressions.SyntaxError =>
BEGIN
cell.text ← cell.displayText ← Rope.Cat["&&&", cell.text, "&&&"];
IF cell.itemSeq.nItems # 1 THEN ERROR;
IF cell.itemSeq.item[0].viewer.destroyed THEN ERROR;
ViewerTools.SetContents[cell.itemSeq.item[0].viewer, cell.displayText];
cell.unAnalyzedExpression ← [NIL];
GOTO syntaxError;
END];
EXITS
syntaxError => NULL;
END;
IF cell.displayMode = text OR cell.displayMode = control THEN
BEGIN
IF cell.text = NIL THEN cell.text ← " ";
cell.displayText ← cell.text
END;
IF cell.complex # NIL AND cell.idMode = setId THEN
BEGIN
IF cell.text = NIL OR Rope.Equal[cell.text, ""] OR Rope.Equal[cell.text, " "] THEN GOTO idBad;
cell.complex.unAnalyzedId ← Expressions.ParseHCellIdentifier[cell.text
! Expressions.SyntaxError =>
BEGIN
IF cell.displayMode # text THEN ERROR;
IF cell.itemSeq.nItems# 1 THEN ERROR;
cell.text ← cell.displayText ← Rope.Cat["&&&", cell.text, "&&&"];
IF cell.itemSeq.item[0].viewer.destroyed THEN ERROR;
ViewerTools.SetContents[cell.itemSeq.item[0].viewer, cell.displayText];
cell.complex.unAnalyzedId ← [NIL];
GOTO idBad;
END];
EXITS
idBad => NULL;
END;
IF NOT ConditionalSwitchToSimpleMode[cell] THEN SwitchToComplexMode[cell];
IF cell.complex = NIL THEN
BEGIN
ReComputeHCellValue[cell];
IF cell.colSumMode # none THEN Dependencies.MarkActionDirty[cell.colSummer];
IF cell.rowSumMode # none THEN Dependencies.MarkActionDirty[cell.rowSummer];
END;
IF cell.analyzedDisplayText = NIL OR cell.displayText # cell.analyzedDisplayText.source THEN
BEGIN
cell.analyzedDisplayText ← AnalyzeText[cell.displayText];
cell.displayTextDirty ← TRUE;
END;
END;
ConditionalSwitchToSimpleMode: PROCEDURE[cell: HCell] RETURNS[didIt: BOOLEAN] =
BEGIN
IF cell.valueMode = exp AND ExpressionNonConstant[cell.unAnalyzedExpression] THEN RETURN[FALSE];
IF cell.valueMode = lv OR cell.valueMode = lvTimesExp THEN RETURN[FALSE];
IF cell.valueMode = rowSum OR cell.valueMode = colSum OR cell.valueMode = rowTotal OR cell.valueMode = itemArrayColTotal THEN RETURN[FALSE];
IF cell.rightCell # NIL THEN RETURN[FALSE];
IF cell.complex # NIL AND cell.idMode # none THEN RETURN[FALSE];
ForcedSwitchToSimpleMode[cell];
RETURN[TRUE];
END;
ForcedSwitchToSimpleMode: PROCEDURE[cell: HCell] =
BEGIN
IF cell.complex # NIL THEN
BEGIN
complex: HCellComplexDependencyControl ← cell.complex;
IF cell.complex.rowColDataItem # NIL THEN
BEGIN
Dependencies.DeactivateDataItem[complex.rowColDataItem];
complex.rowColDataItem ← NIL;
END;
IF complex.expressionEvalAction # NIL THEN
BEGIN
Dependencies.DeactivateAction[complex.expressionEvalAction];
complex.expressionEvalAction ← NIL;
END;
IF complex.valueDataItem # NIL THEN
BEGIN
Dependencies.DeactivateDataItem[complex.valueDataItem];
complex.valueDataItem ← NIL;
END;
END;
IF cell.valueMode = exp THEN cell.analyzedExpression ← Expressions.AnalyzeHCellExpression[cell.unAnalyzedExpression, cell.document.hCellIds, cell.pageName, cell.arrayIdText, cell.document.dSet, NIL];
cell.complex ← NIL;
END;

-- this code requires that switches of row and col sum modes remove the dependencies involved, i.e first do a ForcedSwitchToSimpleMode.
SwitchToComplexMode: PROCEDURE[cell: HCell] =
BEGIN OPEN Dependencies;
complex: HCellComplexDependencyControl ← cell.complex;
IF complex # NIL THEN
BEGIN
IF complex.expressionEvalAction # NIL THEN
BEGIN
DeactivateAction[complex.expressionEvalAction];
complex.expressionEvalAction ← NIL;
END;
END
ELSE
complex ← cell.complex ← NEW[HCellComplexDependencyControlBody];

IF complex.rowColDataItem = NIL THEN
BEGIN
complex.rowColDataItem �ineDataItem[cell.document.dSet];
IF cell.valueMode = rowSum OR cell.valueMode = rowTotal THEN NoteThatActionModifiesDataItem[cell.rowSummer, complex.rowColDataItem];
IF cell.valueMode = colSum THEN NoteThatActionModifiesDataItem[cell.colSummer, complex.rowColDataItem];
IF cell.valueMode = itemArrayColTotal THEN NoteThatActionModifiesDataItem[cell.itemArrayColSummer, complex.rowColDataItem];
END;
complex.expressionEvalAction ← DefineAction[cell.document.dSet, EvalHCellExpression, cell];
MarkActionDirty[complex.expressionEvalAction];
NoteThatDataItemIsReadByAction[complex.rowColDataItem, complex.expressionEvalAction];
IF cell.leftCell # NIL AND cell.leftCell.complex # NIL AND cell.leftCell.complex.valueDataItem # NIL AND (cell.valueMode = lv OR cell.valueMode = lvTimesExp) THEN NoteThatDataItemIsReadByAction[cell.leftCell.complex.valueDataItem, complex.expressionEvalAction];
cell.analyzedExpression ← Expressions.AnalyzeHCellExpression[cell.unAnalyzedExpression, cell.document.hCellIds, cell.pageName, cell.arrayIdText, cell.document.dSet, complex.expressionEvalAction];
IF cell.idMode # none THEN
BEGIN
complex.analyzedId ← Expressions.AnalyzeHCellIdentifier[complex.unAnalyzedId, cell.document.hCellIds, cell.pageName, cell.arrayIdText, cell.document.dSet];
NoteThatActionModifiesDataItem[complex.expressionEvalAction, complex.analyzedId.dataItem];
END;
IF complex.valueDataItem = NIL THEN
BEGIN
complex.valueDataItem ← DefineDataItem[cell.document.dSet];
IF cell.rowSumMode # none THEN NoteThatDataItemIsReadByAction[complex.valueDataItem, cell.rowSummer];
IF cell.colSumMode # none THEN NoteThatDataItemIsReadByAction[complex.valueDataItem, cell.colSummer];
IF cell.rightCell # NIL AND cell.rightCell.complex # NIL AND cell.rightCell.complex.expressionEvalAction # NIL AND (cell.rightCell.valueMode = lv OR cell.rightCell.valueMode = lvTimesExp) THEN
NoteThatDataItemIsReadByAction[complex.valueDataItem, cell.rightCell.complex.expressionEvalAction];
END;
NoteThatActionModifiesDataItem[complex.expressionEvalAction, complex.valueDataItem];
MarkActionDirty[complex.expressionEvalAction];
SELECT cell.valueMode FROM
exp, none => NULL;
rowSum, rowTotal => MarkActionDirty[cell.rowSummer];
colSum => MarkActionDirty[cell.colSummer];
itemArrayColTotal => MarkActionDirty[cell.itemArrayColSummer];
lv, lvTimesExp => IF cell.leftCell.complex # NIL THEN
MarkActionDirty[cell.leftCell.complex.expressionEvalAction];
ENDCASE => ERROR;
END;
-- these procedures run at dependency time
DoRowSum: PUBLIC PROCEDURE[hcn: StructureNodes.StructureNode, sumToLeft: REAL] RETURNS[REAL] =
BEGIN
cell: HCell ← NARROW[hcn.ref];
IF cell.valueMode = rowSum THEN cell.complex.rowColSum ← sumToLeft;
SELECT cell.rowSumMode FROM
add => RETURN[sumToLeft+cell.value];
sub => RETURN[sumToLeft-cell.value];
none => RETURN[sumToLeft];
ENDCASE => ERROR;
END;
DoRowTotal: PUBLIC PROCEDURE[hcn: StructureNodes.StructureNode, total: REAL] =
BEGIN
cell: HCell ← NARROW[hcn.ref];
IF cell.valueMode = rowTotal THEN cell.complex.rowColSum ← total;
END;
DoColSum: PUBLIC PROCEDURE[hcn: StructureNodes.StructureNode, sumAbove: REAL] RETURNS[REAL] =
BEGIN
cell: HCell ← NARROW[hcn.ref];
IF cell.valueMode = colSum THEN cell.complex.rowColSum ← sumAbove;
SELECT cell.colSumMode FROM
add => RETURN[sumAbove+cell.value];
sub => RETURN[sumAbove-cell.value];
none => RETURN[sumAbove];
ENDCASE => ERROR;
END;
DoItemArrayColSum: PUBLIC PROCEDURE[cell: HCell, total: REAL] =
BEGIN
IF cell.valueMode = itemArrayColTotal THEN cell.complex.rowColSum ← total;
END;
-- these routines are run when cell value is recomputed, this could be either at dirty data check time, or during dependency evaluation
EvalHCellExpression: PROCEDURE[action: Dependencies.Action, info: REF ANY] =
{ReComputeHCellValue[NARROW[info]]};
ReComputeHCellValue: PROCEDURE[cell: HCell] =
BEGIN
SELECT cell.valueMode FROM
exp =>
IF NOT cell.analyzedExpression.exp = NIL THEN
{SetHCellValue[cell, Expressions.EvaluateExpression[cell.analyzedExpression]]};
rowSum, colSum, rowTotal, itemArrayColTotal => {SetHCellValue[cell, cell.complex.rowColSum]};
lv => {SetHCellValue[cell, cell.leftCell.value]};
lvTimesExp =>
IF NOT cell.analyzedExpression.exp = NIL THEN
BEGIN
expValue: REAL ← Expressions.EvaluateExpression[cell.analyzedExpression];
SetHCellValue[cell, cell.leftCell.value*expValue];
END;
none => NULL;
ENDCASE => ERROR;
END;
SetHCellValue: PROCEDURE[cell: HCell, newValue: REAL] =
BEGIN
IF cell.value # newValue OR cell.firstTime OR (cell.itemSeq # NIL AND cell.itemSeq.item[0].viewerMode = textBox) THEN
BEGIN
cell.value ← newValue;
cell.firstTime ← FALSE;
IF cell.displayMode = value THEN
BEGIN
IF cell.value = 0 AND cell.zeroValueText # NIL AND Rope.Length[cell.zeroValueText] # 0
THEN cell.displayText ← cell.zeroValueText
ELSE
BEGIN
modifiedVal: REAL ← cell.value*100;
valAsLongInteger: LONG INTEGER ← Real.RoundLI[modifiedVal];
negSign: BOOLEAN ← valAsLongInteger < 0;
highPart: LONG INTEGER ← valAsLongInteger/100;
lowPart: LONG INTEGER ← IF negSign THEN ((-valAsLongInteger) MOD 100) ELSE ((valAsLongInteger) MOD 100);
zeros: Rope.ROPE ← IF lowPart < 10 THEN "0" ELSE "";
cell.displayText ← IO.PutFR[
"%g.%g%g",
IF highPart = 0 AND negSign THEN
IO.rope["-0"] ELSE IO.int[highPart],
IO.rope[zeros],
IO.int[lowPart]];
END;
END;
END;
IF cell.complex # NIL AND cell.complex.analyzedId # NIL THEN Expressions.SetAnalyzedHCellIdentifier[cell.complex.analyzedId, cell.value];
IF cell.analyzedDisplayText = NIL OR cell.displayText # cell.analyzedDisplayText.source THEN
BEGIN
cell.analyzedDisplayText ← AnalyzeText[cell.displayText];
cell.displayTextDirty ← TRUE;
END;
END;
-- visual text procedures
TextSeq: TYPE = REF TextSeqBody;
TextSeqBody: TYPE = RECORD[
source: Rope.ROPE,
nPieces: INTEGER,
piece: SEQUENCE maxPieces: CARDINAL OF Rope.ROPE];
ViewerSeq: TYPE = REF ViewerSeqBody;
ViewerSeqBody: TYPE = RECORD[
nViewers: INTEGER,
viewer: SEQUENCE maxViewers: CARDINAL OF ViewerClasses.Viewer];
AnalyzeText: PROCEDURE[text: Rope.ROPE] RETURNS[TextSeq] =
BEGIN
crRope: Rope.ROPE ← Rope.FromChar[IO.CR];
nPieces: CARDINAL ← 1; -- tentative
pos1: INT ← 0;
next: INT;
tSeq: TextSeq;
WHILE (next ← Rope.Find[text, crRope, pos1]) > 0 DO
nPieces ← nPieces + 1;
pos1 ← next+1;
ENDLOOP;
tSeq ← NEW[TextSeqBody[nPieces]];
tSeq.source ← text;
tSeq.nPieces ← nPieces;
pos1 ← 0;
IF nPieces = 1 THEN tSeq.piece[0] ← text ELSE
FOR I: CARDINAL IN [0..nPieces) DO
next ← Rope.Index[text, pos1, crRope];
tSeq.piece[I] ← Rope.Substr[text, pos1, next-pos1];
pos1 ← next+1;
ENDLOOP;
RETURN[tSeq];
END;
TextArrayPrefixLeadLines: INTEGER = 3;
TextArrayPrefixSpace: INTEGER = 7;
TextColumnPrefixSpace: INTEGER = 2;
PreShowHCell: PROCEDURE[node: StructureNodes.StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
BEGIN
cell: HCell ← NARROW[node.ref];
textH, textA, textB, textL, textR, textW: INTEGER;
textPrefixLead: INTEGER = IF cell.displayMode = control THEN TextArrayPrefixLeadLines ELSE 0;
textPrefixSpace: INTEGER = IF cell.displayMode = control THEN TextArrayPrefixSpace ELSE TextColumnPrefixSpace;
IF cell.interesting THEN {A: INTEGER ← 0; A ← A};
textH ← textA ← textB ← textL ← textR ← textW ← 0;
IF cell.displayText # NIL THEN
BEGIN
IF cell.itemSeq # NIL AND cell.itemSeq.nItems # cell.analyzedDisplayText.nPieces THEN ERROR;
IF cell.itemSeq = NIL THEN
cell.itemSeq ← NewHCellItemSeq[cell.analyzedDisplayText.nPieces];
FOR J: INT IN [0..cell.analyzedDisplayText.nPieces) DO
showControl: HCellShowControl ← NEW[HCellShowControlBody];
itemText: Rope.ROPE ← cell.analyzedDisplayText.piece[J];
cell.itemSeq.item[J].showControl ← showControl;
SELECT cell.justification FROM
left, centered, right =>
showControl.textW ← INTEGER[Rope.Length[itemText]];
leftDecimal =>
BEGIN
decimals: CARDINAL ← 0;
length: CARDINAL ← Rope.Length[itemText];
FOR I: CARDINAL DECREASING IN [0..length) DO
IF Rope.Fetch[itemText, I] = '. THEN
{decimals ← length - I; EXIT};
IF NOT Ascii.Digit[Rope.Fetch[itemText, I]] THEN EXIT;
ENDLOOP;
showControl.textL ← length-decimals; showControl.textA ← decimals;
showControl.textB ← showControl.textR ← 0;
showControl.textW ← length;
END;
rightDecimal =>
BEGIN
decimals: CARDINAL ← 0;
length: CARDINAL ← Rope.Length[itemText];
FOR I: CARDINAL DECREASING IN [0..length) DO
IF Rope.Fetch[itemText, I] = '. THEN
{decimals ← length - I; EXIT};
IF NOT Ascii.Digit[Rope.Fetch[itemText, I]] THEN EXIT;
ENDLOOP;
showControl.textL ← showControl.textA ← 0;
showControl.textB ← length-decimals; showControl.textR ← decimals;
showControl.textW ← length;
END;
ENDCASE => ERROR;
textL ← MAX[textL, showControl.textL];
textA ← MAX[textL, showControl.textA];
textB ← MAX[textL, showControl.textB];
textR ← MAX[textL, showControl.textR];
textW ← MAX[textL, showControl.textW];
textH ← textH + 1;
ENDLOOP;
END;
IF cell.interesting THEN {A: INTEGER ← 0; A ← A};
RETURN[textPrefixLead, textH, textPrefixSpace, textL, textA, textB, textR, textW];
END;
ScreenArrayPrefixLead: INTEGER = 30;
ScreenArrayPrefixSpace: INTEGER = 20;
ScreenColumnPrefixSpace: INTEGER = 0;
PrePrePaintHCell: PROCEDURE[node: StructureNodes.StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER]=
BEGIN
cell: HCell ← NARROW[node.ref];
screenTextA, screenTextB, screenTextL, screenTextR, screenTextW: INTEGER;
screenTextH: INTEGER;
screenPrefixLead: INTEGER = IF cell.displayMode = control THEN ScreenArrayPrefixLead ELSE 0;
screenPrefixSpace: INTEGER = IF cell.displayMode = control THEN ScreenArrayPrefixSpace ELSE ScreenColumnPrefixSpace;
IF cell.interesting THEN {A: INTEGER ← 0; A ← A};
screenTextA ← screenTextB ← screenTextL ← screenTextR ← screenTextW ← 0;
screenTextH ← 0;
IF cell.displayText # NIL THEN
BEGIN
IF cell.itemSeq = NIL OR cell.itemSeq.nItems # cell.analyzedDisplayText.nPieces THEN
BEGIN
IF cell.itemSeq # NIL THEN
FOR J: INT IN [0..cell.itemSeq.nItems) DO
[] ← ClearHCellItemViewer[cell.itemSeq.item[J]];
ENDLOOP;
cell.itemSeq ← NewHCellItemSeq[cell.analyzedDisplayText.nPieces];
cell.viewerMode ← nonNil;
END;
FOR J: INT IN [0..cell.analyzedDisplayText.nPieces) DO
prePaintControl: HCellPrePaintControl ← NEW[HCellPrePaintControlBody];
itemText: Rope.ROPE ← cell.analyzedDisplayText.piece[J];
cell.itemSeq.item[J].prePaintControl ← prePaintControl;
SELECT cell.justification FROM
left, centered, right =>
prePaintControl.screenTextW ← VFonts.StringWidth[itemText]+20;
leftDecimal =>
BEGIN
decimals: CARDINAL ← 0;
length: CARDINAL ← Rope.Length[itemText];
FOR I: CARDINAL DECREASING IN [0..length) DO
IF Rope.Fetch[itemText, I] = '. THEN
{decimals ← length - I; EXIT};
IF NOT Ascii.Digit[Rope.Fetch[itemText, I]] THEN EXIT;
ENDLOOP;
prePaintControl.screenTextL ← VFonts.StringWidth[Rope.Substr[itemText, 0, length-decimals]]+5;
prePaintControl.screenTextA ← VFonts.StringWidth[Rope.Substr[itemText, length-decimals, length]];
prePaintControl.screenTextB ← prePaintControl.screenTextR ← 0;
prePaintControl.screenTextW ← VFonts.StringWidth[itemText] + 20;
END;
rightDecimal =>
BEGIN
decimals: CARDINAL ← 0;
length: CARDINAL ← Rope.Length[itemText];
FOR I: CARDINAL DECREASING IN [0..length) DO
IF Rope.Fetch[itemText, I] = '. THEN
{decimals ← length - I; EXIT};
IF NOT Ascii.Digit[Rope.Fetch[itemText, I]] THEN EXIT;
ENDLOOP;
prePaintControl.screenTextL ← prePaintControl.screenTextA ← 0;
prePaintControl.screenTextB ← VFonts.StringWidth[Rope.Substr[itemText, 0, length-decimals]];
prePaintControl.screenTextR ← VFonts.StringWidth[Rope.Substr[itemText, length-decimals, length]]+5;
prePaintControl.screenTextW ← VFonts.StringWidth[itemText] + 20;
END;
ENDCASE => ERROR;
prePaintControl.screenTextH ← VFonts.FontHeight[] + 5;
screenTextL ← MAX[screenTextL, prePaintControl.screenTextL];
screenTextA ← MAX[screenTextA, prePaintControl.screenTextA];
screenTextB ← MAX[screenTextB, prePaintControl.screenTextB];
screenTextR ← MAX[screenTextR, prePaintControl.screenTextR];
screenTextW ← MAX[screenTextW, prePaintControl.screenTextW];
screenTextH ← screenTextH + prePaintControl.screenTextH;
ENDLOOP;
END;
IF cell.interesting THEN {A: INTEGER ← 0; A ← A};
RETURN[screenPrefixLead, screenTextH, screenPrefixSpace, screenTextL, screenTextA, screenTextB, screenTextR, screenTextW]
END;
ShowHCellLine: PUBLIC PROCEDURE[node: StructureNodes.StructureNode, L, R, W: INTEGER, lineX: INTEGER, to: IO.STREAM] =
BEGIN
cell: HCell ← NARROW[node.ref];
IF cell.interesting THEN {A: INTEGER ← 0; A ← A};
IF lineX > cell.analyzedDisplayText.nPieces THEN
{FOR I: INTEGER IN [1..W] DO to.Put[IO.char[' ]] ENDLOOP}
ELSE
BEGIN
showControl: HCellShowControl ← cell.itemSeq[lineX-1].showControl;
textW: INTEGER ← showControl.textW;
fullSkip: INTEGER ← IF textW < W THEN (W-textW) ELSE 0;
leftSkip, rightSkip: INTEGER;
SELECT cell.justification FROM
left => {rightSkip ← fullSkip; leftSkip ← 0};
centered => {leftSkip ← fullSkip/2; rightSkip ← fullSkip - leftSkip};
right => {leftSkip ← fullSkip; rightSkip ← 0};
leftDecimal =>
BEGIN
leftSkip ← IF textW < W AND showControl.textL < L THEN L - showControl.textL ELSE 0;
rightSkip ← fullSkip - leftSkip;
END;
rightDecimal =>
BEGIN
rightSkip ← IF textW < W AND showControl.textR < R THEN R - showControl.textR ELSE 0;
leftSkip ← fullSkip - rightSkip;
END;
ENDCASE => ERROR;
FOR I: INTEGER IN [1..leftSkip] DO to.Put[IO.char[' ]] ENDLOOP;
to.Put[IO.rope[cell.analyzedDisplayText.piece[lineX-1]]];
FOR I: INTEGER IN [1..rightSkip] DO to.Put[IO.char[' ]] ENDLOOP;
cell.itemSeq[lineX-1].showControl ← NIL;
END;
IF cell.viewerMode = nil AND cell.itemSeq # NIL AND lineX = cell.itemSeq.nItems THEN cell.itemSeq ← NIL;
END;
PrePaintHCell: PUBLIC PROCEDURE[node: StructureNodes.StructureNode, y, x, L, R, W: INTEGER, pass: StructureNodes.PrePaintPass] =
BEGIN
cell: HCell ← NARROW[node.ref];
IF cell.itemSeq = NIL THEN RETURN;
FOR J: INT IN [0..cell.itemSeq.nItems) DO
item: HCellItem ← cell.itemSeq.item[J];
prePaintControl: HCellPrePaintControl ← item.prePaintControl;
leftGap: INTEGER;
newX, newY, newW: INTEGER;
--oldX: INTEGER ← item.itemViewerX;
--oldY: INTEGER ← item.itemViewerY;
--oldW: INTEGER ← item.itemViewerW;
IF cell.interesting THEN {A: INTEGER ← 0; A ← A};
IF item = NIL THEN RETURN;
newY ← y;
newW ← MIN[prePaintControl.screenTextW, W];
SELECT cell.justification FROM
left => leftGap ← 0;
centered => leftGap ← (W - newW)/2;
right => leftGap ← (W - newW);
leftDecimal => leftGap ← IF prePaintControl.screenTextW < W AND prePaintControl.screenTextL < L
THEN L - prePaintControl.screenTextL ELSE 0;
rightDecimal =>
BEGIN
fullGap: INTEGER ← IF prePaintControl.screenTextW < W THEN W-prePaintControl.screenTextW ELSE 0;
rightGap: INTEGER ← IF prePaintControl.screenTextW < W AND prePaintControl.screenTextR < R
THEN R - prePaintControl.screenTextR ELSE 0;
leftGap ← fullGap - rightGap;
END;
ENDCASE => ERROR;
newX ← x + leftGap;
IF pass = first THEN
BEGIN
item.viewerDirty ← FALSE; -- tentative
IF newX # item.itemViewerX OR newY # item.itemViewerY OR newW # item.itemViewerW OR cell.displayTextDirty THEN
BEGIN -- we only white out the area this time
prePaintControl.wasSelected ← BlankHCellItemViewer[item];
item.viewerDirty ← TRUE;
END;
END
ELSE
BEGIN -- now we delete the old viewer and create a new one, but no painting
IF item.viewerDirty THEN
BEGIN
DestroyHCellItemViewer[item];
item.itemViewerX ← newX; item.itemViewerY ← newY; item.itemViewerW ← newW;
DisplayHCellItem[cell, item, item.prePaintControl.wasSelected];
item.prePaintControl ← NIL;
END;
END;
y ← y + prePaintControl.screenTextH;
ENDLOOP;
IF NOT pass = first THEN cell.displayTextDirty ← FALSE;
END;
PaintHCell: PROCEDURE[node: StructureNodes.StructureNode, dirtyOnly: BOOLEAN] =
BEGIN
cell: HCell ← NARROW[node.ref];
IF cell.itemSeq = NIL THEN RETURN;
FOR J: INT IN [0..cell.itemSeq.nItems) DO
item: HCellItem ← cell.itemSeq.item[J];
IF item.viewerDirty OR NOT dirtyOnly THEN ViewerOps.PaintViewer[item.viewer, all];
item.viewerDirty ← FALSE;
ENDLOOP;
END;
DisplayHCellItem: PROCEDURE[cell: HCell, item: HCellItem, wasSelected: BOOLEAN] =
BEGIN
IF wasSelected THEN DisplayHCellItemAsTextViewer[cell, item, selected]
ELSE
SELECT item.viewerMode FROM
textBox =>
BEGIN
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
IF selectedViewer = item.viewer THEN
DisplayHCellItemAsTextViewer[cell, item, selected]
ELSE DisplayHCellItemAsButton[cell, item];
END;
button => DisplayHCellItemAsButton[cell, item];
nil => DisplayHCellItemAsButton[cell, item];
ENDCASE => ERROR;
END;
ForceHCellSelection: PUBLIC PROCEDURE[hcn: StructureNodes.StructureNode, conditional: BOOLEAN ← FALSE] RETURNS[succeeded: BOOLEAN] =
BEGIN
cell: HCell ← NARROW[hcn.ref];
IF conditional AND NOT cell.editable THEN RETURN[FALSE];
DisplayHCellItemAsTextViewer[cell, cell.itemSeq.item[0], selected];
RETURN[TRUE];
END;
DisplayHCellItemAsButton: PROCEDURE[cell: HCell, item: HCellItem] =
BEGIN
[] ← ClearHCellItemViewer[item];
item.viewer ← DisplayTextPieceAsButton[cell.analyzedDisplayText.piece[item.itemIndex], item.itemViewerX, item.itemViewerW, item.itemViewerY, VFonts.FontHeight[]+5, cell, item];
item.viewerMode ← button;
END;
DisplayTextPieceAsButton: PROCEDURE[text: Rope.ROPE, x, w, y, h: INT, cell: HCell, item: HCellItem] RETURNS[ViewerClasses.Viewer] =
BEGIN
button: ViewerClasses.Viewer;
button ← Buttons.Create[
info: [
name: text,
wx: x,
ww: w,
wy: y,
wh: h,
parent: cell.vParent,
border: FALSE,
scrollable: FALSE],
proc: ReDisplayHCellItemButtonAsTextViewer,
clientData: NIL,
paint: FALSE,
fork: TRUE];
item.viewerDirty ← TRUE;
ViewerOps.AddProp[button, $HCell, NEW[HCellRefDescBody ← [cell.document, cell.da, cell.selfI, cell.selfJ, item.itemIndex]]];
RETURN[button];
END;
-- this procedure gets called when ever a cell button is "pushed", and converts the button to an editable text viewer, if allowed
ReDisplayHCellItemButtonAsTextViewer: Buttons.ButtonProc = TRUSTED
BEGIN
viewer: ViewerClasses.Viewer ← NARROW[parent]; -- the button itself
cellRefDesc: HCellRefDesc ← NARROW[ViewerOps.FetchProp[viewer, $HCell]];
DHCBATVInternal: PROCEDURE =
BEGIN
cell: HCell;
J: CARDINAL;
[cell, , J]← DecodeHCellRefDescriptor[cellRefDesc];
IF cell.itemSeq = NIL OR cell.itemSeq.item[J].viewer # NARROW[parent, ViewerClasses.Viewer] THEN
RETURN; -- some concurrent activity must have removed this button from the item, presumably it is being destroyed.
DisplayHCellItemAsTextViewer[cell, cell.itemSeq.item[J], selected]
END;
cellRefDesc.document.procs.executeInMonitor[cellRefDesc.document, DHCBATVInternal];
END;
DisplayHCellItemAsTextViewer: PROCEDURE[cell: HCell, item: HCellItem, mode: {selected, notSelected}] =
BEGIN
x: INT ← item.itemViewerX;
w: INT ← item.itemViewerW;
y: INT ← item.itemViewerY;
dy: INT ← VFonts.FontHeight[]+5;
[] ← ClearHCellItemViewer[item];
[item.viewer, item.registration] ← DisplayTextPieceAsTextViewer[cell.analyzedDisplayText.piece[item.itemIndex], x, w, y, dy, cell, item.itemIndex, (item.itemIndex+1) = cell.analyzedDisplayText.nPieces];
item.viewerMode ← textBox;
StructureNodes.MarkElementDirty[cell.da, 0, 0];
IF mode = selected THEN GCell.NoteItemSelection[cell.displayer, cell.hcaSn, 0];
IF mode = selected THEN ViewerTools.SetSelection[item.viewer, NIL];
END;
DisplayTextPieceAsTextViewer: PROCEDURE[text: Rope.ROPE, x, w, y, h: INT, cell: HCell, cellx: CARDINAL, last: BOOLEAN] RETURNS[ViewerClasses.Viewer, ViewerEvents.EventRegistration] =
BEGIN
textBox: ViewerClasses.Viewer;
registration: ViewerEvents.EventRegistration;
textBox ← ViewerTools.MakeNewTextViewer[
info: [
parent: cell.vParent,
wx: x,
wy: y,
ww: w + (IF NOT last THEN VFonts.StringWidth[Rope.FromChar[IO.CR]] ELSE 0),
wh: h,
border: FALSE,
scrollable: FALSE],
paint: FALSE];
IF NOT cell.editable THEN ViewerTools.InhibitUserEdits[textBox];
IF textBox.destroyed THEN ERROR;
IF last THEN ViewerTools.SetContents[textBox, text]
ELSE ViewerTools.SetContents[textBox, Rope.Concat[text, Rope.FromChar[IO.CR]]];
ViewerOps.AddProp[textBox, $HCell, NEW[HCellRefDescBody ← [cell.document, cell.da, cell.selfI, cell.selfJ, cellx]]];
registration ← ViewerEvents.RegisterEventProc[TextBoxChanges, edit, textBox];
RETURN[textBox, registration];
END;
TextBoxChanges: ViewerEvents.EventProc = TRUSTED
BEGIN
cellRefDesc: HCellRefDesc ← NARROW[ViewerOps.FetchProp[viewer, $HCell]];
TBCInternal: PROCEDURE =
BEGIN
cell: HCell;
J: CARDINAL;
[cell, , J]← DecodeHCellRefDescriptor[cellRefDesc];
StructureNodes.MarkElementDirty[cell.da, 0, 0];
END;
IF event = edit THEN
cellRefDesc.document.procs.executeInMonitor[cellRefDesc.document, TBCInternal];
RETURN[FALSE]
END;
ClearHCellItemViewer: PROCEDURE[item: HCellItem] RETURNS[wasSelected: BOOLEAN] =
BEGIN
wasSelected ← BlankHCellItemViewer[item];
DestroyHCellItemViewer[item];
END;
BlankHCellItemViewer: PROCEDURE[item: HCellItem] RETURNS[wasSelected: BOOLEAN] =
BEGIN
-- remark: August 15, 1985 11:11:01 am PDT: the "set" procedure on both of these viewer varieties is now asynchronous, that is the change from Cedar 5.2 to Cedar 6.0. The fix suggested by Russ is to the the Buttons.ReLabel and ViewerTools.SetContents with paint = FALSE, and then do a ViewerOps.PaintViewer myself, which will be synchronous.
wasSelected ← FALSE; -- tentative
IF item.viewerMode = button AND item.viewer # NIL THEN
Buttons.ReLabel[item.viewer, "", FALSE];
IF item.viewerMode = textBox AND item.viewer # NIL THEN
BEGIN
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
IF item.viewer.destroyed THEN ERROR;
IF selectedViewer = item.viewer THEN wasSelected ← TRUE;
ViewerTools.SetContents[item.viewer, "", FALSE];
END;
ViewerOps.PaintViewer[item.viewer, all];
END;
DestroyHCellItemViewer: PROCEDURE[item: HCellItem] =
BEGIN
IF item.viewerMode # nil AND item.viewer # NIL THEN
BEGIN
ViewerOps.DestroyViewer[item.viewer, FALSE];
item.viewer ← NIL;
item.viewerMode ← nil;
END;
IF item.registration # NIL THEN
BEGIN
ViewerEvents.UnRegisterEventProc[item.registration, edit];
item.registration ← NIL;
END;
END;
DecodeHCellRefDescriptor: PROCEDURE[cellRefDesc: HCellRefDesc] RETURNS[cell: HCell, hcSn: StructureNodes.StructureNode, x: CARDINAL] =
BEGIN
IF cellRefDesc.da = NIL THEN ERROR;
IF cellRefDesc.I = 0 THEN ERROR;
IF cellRefDesc.J = 0 THEN ERROR;
hcSn ← StructureNodes.GetElementAt[cellRefDesc.da, cellRefDesc.I, cellRefDesc.J].sn;
cell ← NARROW[hcSn.ref];
x ← cellRefDesc.x;
END;
-- following should be in expressions
ExpressionNonConstant: PROCEDURE[expression: Expressions.Expression] RETURNS[BOOLEAN] =
BEGIN -- this belongs in expresion code
E1: Expressions.Expression;
T: Expressions.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..
-- June 25, 1984 4:09:57 pm PDT: Sturgis, started HCellImpl.mesa
-- June 26, 1984 10:56:16 am PDT: basic display and show code in place.
-- June 27, 1984 1:02:33 pm PDT: at this time, most of the code should be in place, and I begin work on HCellArray code.
-- RTE: June 28, 1984 4:08:51 pm PDT: forgot to call InstallNewTextInHCell when changing modes.
-- RTE: June 28, 1984 5:20:17 pm PDT: forgot to initialize the row and column summers.
-- RTE: June 29, 1984 9:07:03 am PDT: cells did not show up, so modified DisplayHCell to paint a button if viewerMode = nil, rather than NULL. This seems to follow what GCellImpl does. I assume that PrePaint does not get called for a page that is not open?
-- RTE: June 29, 1984 9:46:48 am PDT: PrePaint code used viewerW when it should have used prePaintControl.screenTextW.
-- RTE: June 29, 1984 10:04:42 am PDT: no one made a call to Structure nodes to note that on element was dirty. In GCellIMplB, this code is in the button proc, but I am afraid the information may also be needed after an adjust, in case anyone edits the remaing text box, so I put the call in DisplayHCellAsTextViewer.
-- RTE: June 29, 1984 10:13:05 am PDT: summers set cell.value, rather than cell.complex.rowColSum, as they should have.
-- RTE: June 29, 1984 10:37:16 am PDT: buttons were not getting repositioned if their logical coordinates changed. Old code always re-wrote them, but I am trying a little more subtility here.
-- RTE: July 1, 1984 2:25:31 pm PDT: could not select a non editable cell.
-- RTE: July 1, 1984 2:48:39 pm PDT: a viewer that was cleared before being redisplayed (i.e. if moving) did not retain its selection.
-- RTE: July 2, 1984 9:49:29 am PDT: PrePaintHCell was using an uninitialized value of wasSelected. This led to too many viewers being selected.
-- RTE: July 2, 1984 5:18:09 pm PDT: symptom: if the control text for a column is selected, and the column is given a new control mode, then the control text for that column is not updated. Attempted fix: in check for dirty data, we currently assumed that if the viewer mode was text box, then the text box may have been edited, and should be assumed dirty. Now, we will not do this if the cell mode is not editable.
-- change: July 3, 1984 8:48:09 am PDT: add logic to control array prefix space and lead, also column prefix space.
-- RTE: July 3, 1984 11:34:58 am PDT: did not attach dependencies correctly if valuemode = rowTotal.
-- change: July 3, 1984 3:42:04 pm PDT: spent most of the afternoon installing multi line display text, for displayMOde = text. Still a number of rough edges to work on.
-- RTE: July 10, 1984 3:01:00 pm PDT: ConditionalSwitchToSimpleMode should have returned false if a rightCell existed, which means that right cell may have been interested in its value, and hence value changes.
-- July 18, 1984 3:51:10 pm PDT: spent preceding two days working on painting performance. By splitting the PrePaint pass into 3 parts, (PrePaint first, PrePaint second, and Paint) I have avoided flushing the cache in viewer paint impl.
-- July 18, 1984 3:52:19 pm PDT: add code to build a summary array cell, i.e. it can accept values from an itemArrayColSummer.
-- RTE: July 20, 1984 2:41:05 pm PDT: several bugs showing up during very first attempts to build a two array group. Including forgetting to handle the new valueMode for the summary cells, e.g. in entering complex mode, and in computing a new value.
-- July 30, 1984 10:16:43 am PDT: add MarkCellInteresting
-- RTE: July 30, 1984 11:14:29 am PDT: dirty bit in overlying DA not maintained for a viewer that remains as a text box. Add code to make use of the edit event available from Viewers. (I think the way I used to do this was to have exactly one viewer not a button, ....)
-- RTE: October 4, 1984 9:52:21 am PDT: Show HCellLine, at the end, conditionally cleared cell.itemSeq. However, forgot to check if already clear, provoking a NIL deref, for pages that have not been opened, that contain a row of items, with unequeal number of lines.
-- RTE: August 15, 1985 11:19:58 am PDT: Viewers changed from Cedar 5.2 to Cedar 6.0. Painting is now done asynchronously, and this causes my routine for blanking out an HCell to fail. The fix suggested by Russ is to the the Buttons.ReLabel and ViewerTools.SetContents with paint = FALSE, and then do a ViewerOps.PaintViewer myself, which will be synchronous. These changes are installed in BlankHCellItemViewer.