-- 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 _DefineDataItem[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. ÊÔ˜šœX˜XJšœ˜Jšœ+˜+Jšœº˜ºJšœ’˜’J˜J˜KJšœ2˜2Jšœ)˜)J˜JšœS˜SJšœ—˜—Jšœ&˜&J˜JšœY˜YJšœ@˜@Jšœr˜r—šœ¶˜¶J˜J˜&J˜3J˜J˜L˜1J˜,J˜Jšœ0˜0J˜+J˜2J˜/J˜—J˜J˜*˜ J˜J˜0—J˜J˜$˜J˜J˜&J˜#J˜3J˜$J˜,J˜J˜|J˜—J˜J˜2˜$J˜3J˜—J˜:˜(J˜m—J˜J˜˜ J˜J˜J˜AJšœ!˜!Jšœ#˜#Jšœ ˜ J˜$Jšœ˜J˜>J˜J˜J˜'J˜J˜J˜J˜J˜J˜J˜'J˜J˜'Jšœ2˜2Jšœ8˜8J˜J˜ J˜#J˜J˜J˜J˜1J˜(J˜(J˜J˜(J˜J˜J˜J˜(J˜'J˜Jšœ˜J˜#J˜!J˜J˜"J˜,J˜"J˜J˜J˜—J˜J˜*˜ JšœW˜W—J˜J˜J˜˜DJ˜J˜2J˜˜!J˜1J˜—J˜ J˜—J˜J˜J˜šœÖ˜ÖJ˜Jšœj˜jJ˜KJ˜J˜J˜—J˜;J˜˜·J˜J˜J˜J˜-J˜J˜J˜J˜$J˜J˜J˜J˜J˜˜9J˜J˜#J˜J˜—J˜#J˜J˜J˜J˜JJ˜J˜-J˜-J˜—J˜˜>˜J˜0——J˜J˜AJ˜!˜@J˜—J˜J˜J˜jJ˜˜=Jšœ˜Jšœ,˜,Jšœ ˜ Jšœ%˜%Jšœ7˜7Jšœ3˜3J˜—J˜J˜BJ˜J˜Ô—˜J˜EJ˜J˜kJ˜˜vJ˜J˜J˜ J˜J˜J˜J˜—J˜RJ˜˜´J˜J˜J˜˜J˜/J˜J˜ J˜—J˜J˜+J˜—J˜J˜J˜sJ˜˜=J˜J˜J˜J˜˜+J˜0J˜—J˜J˜%J˜—J˜J˜DJ˜J˜J˜2J˜˜.J˜ŸJ˜â—J˜J˜J˜aJ˜˜GJ˜J˜J˜+Jšœ˜J˜J˜-J˜>J˜=J˜˜-˜1J˜J˜J˜zJ˜J˜—J˜—J˜J˜Jšœ7˜7šœG˜GJšœ/˜/—˜-JšœJ˜JšœK˜KJšœ/˜/—J˜—J˜J˜J˜J˜J˜—J˜J˜&˜.˜J˜—˜;J˜Jšœ&˜&šœF˜Fšœ˜Jšœ˜JšœA˜AJšœ&˜&Jšœ4˜4JšœG˜GJšœ"˜"Jšœ˜Jšœ˜——˜J˜—J˜—J˜˜=J˜J˜(Jšœ˜J˜—J˜˜2J˜Jšœ^˜^šœF˜Fšœ˜Jšœ˜Jšœ&˜&J˜%JšœA˜AJšœ4˜4JšœG˜GJšœ"˜"Jšœ ˜ Jšœ˜——˜J˜—J˜—J˜J˜J˜J˜J˜LJ˜LJ˜—J˜˜\J˜J˜9J˜J˜—J˜J˜—J˜˜OJ˜Jšœ`˜`J˜IJ˜J˜+J˜@J˜J˜ J˜—J˜˜2J˜˜J˜J˜6˜)J˜Jšœ8˜8Jšœ˜J˜—šœ*˜*J˜Jšœ<˜šœ5˜5Jšœ<˜<—J˜—J˜—J˜J˜J˜J˜*J˜J˜˜^J˜J˜JšœC˜C˜J˜$J˜$J˜J˜—J˜J˜—˜NJ˜J˜JšœA˜AJ˜J˜—˜]J˜J˜JšœB˜B˜J˜#J˜#J˜J˜—J˜—J˜˜?J˜JšœJ˜JJ˜—J˜J˜J˜‡J˜šœL˜LJšœ$˜$—J˜šœ-˜-J˜˜˜˜-JšœO˜OJ˜——Jšœ]˜]J˜Jšœ1˜1J˜˜ ˜-Jšœ˜JšœI˜IJšœ2˜2J˜——J˜ J˜—J˜—J˜˜7J˜˜uJ˜J˜J˜˜ J˜šœV˜VJšœ*˜*˜Jšœ˜Jšœ#˜#J˜;J˜(J˜.J˜hJ˜4J˜šœ˜Jšœ ˜ šœ ˜ Jšœ$˜$—Jšœ˜Jšœ˜—J˜——J˜—J˜—JšœŠ˜Š˜]J˜J˜9J˜J˜—J˜J˜—J˜J˜J˜J˜J˜ ˜J˜J˜J˜2—J˜J˜$˜J˜J˜?J˜—˜:J˜J˜)J˜#J˜J˜ J˜J˜˜3J˜J˜J˜—J˜J˜!J˜J˜J˜ J˜˜-˜"J˜&J˜3J˜J˜——J˜J˜ J˜J˜—J˜J˜J˜&J˜"J˜#J˜J˜šœ{˜{J˜J˜Jšœ2˜2J˜]J˜nJ˜1Jšœ2˜2˜J˜J˜\˜J˜AJ˜—˜6J˜:Jšœ8˜8Jšœ/˜/J˜šœ˜˜Jšœ4˜4—˜Jšœ˜Jšœ˜Jšœ)˜)šœ,˜,šœ$˜$Jšœ˜—Jšœ6˜6Jšœ˜—JšœB˜BJšœ*˜*Jšœ˜J˜—˜Jšœ˜Jšœ˜Jšœ)˜)šœ,˜,Jšœ$˜$Jšœ˜Jšœ6˜6Jšœ˜—Jšœ*˜*JšœB˜BJšœ˜J˜—J˜—Jšœ&˜&Jšœ&˜&Jšœ&˜&Jšœ&˜&Jšœ&˜&J˜J˜—J˜—J˜1J˜RJ˜J˜—J˜$J˜%J˜%J˜˜~J˜J˜JšœI˜IJšœ˜J˜\J˜tJ˜1JšœH˜HJšœ˜˜J˜˜TJ˜˜˜)J˜0J˜——J˜AJ˜J˜—˜6J˜FJšœ8˜8Jšœ7˜7—˜šœ˜˜Jšœ>˜>—˜Jšœ˜Jšœ˜Jšœ)˜)šœ,˜,šœ$˜$Jšœ˜—Jšœ6˜6Jšœ˜—Jšœ^˜^Jšœa˜aJšœ>˜>Jšœ@˜@J˜—˜Jšœ˜Jšœ˜Jšœ)˜)šœ,˜,Jšœ$˜$Jšœ˜Jšœ6˜6Jšœ˜—Jšœ>˜>Jšœ\˜\Jšœc˜cJšœ@˜@J˜—J˜—Jšœ6˜6Jšœ<˜