-- GCellImplB.mesa -- Last Edited by: Sturgis, August 15, 1985 11:33:42 am PDT DIRECTORY Ascii USING[Digit], Expressions USING[AnalyzedExpression, AnalyzedIdentifier, AnalyzeExpression, AnalyzeIdentifier, EvaluateExpression, Expression, Identifier, IdTableSet, ParseExpression, ParseIdentifier, SetAnalyzedIdentifier, SyntaxError, Token], Buttons USING[ButtonProc, Create, ReLabel], Dependencies USING[Action, DataItem, DeactivateAction, DeactivateDataItem, DefineDataItem, DependencySet, DefineAction, MarkActionDirty, MarkDataItemDirty, NoteThatActionModifiesDataItem, NoteThatDataItemIsReadByAction], GCell USING[GCellItemDescriptor, GCellItemJustification, GCellItemMode, GCellItemRowColSumMode, GCellItemValueMode, GetIdTableSet, NoteItemSelection], GCellPrivateDefs USING[GCell, Item, ItemBody], NewCalcGlobal USING[Displayer, Document], Real USING[RoundLI], Rope USING[Cat, Equal, Fetch, Find, Length, Replace, ROPE, Substr], IO USING[atom, BreakProc, char, GetInt, GetRefAny, GetTokenRope, int, Put, PutFR, rope, STREAM], StructureNodes USING[DisplayArray, GetElementAt, MarkElementDirty, StructureNode], VFonts USING[FontHeight, StringWidth], ViewerClasses USING[Viewer], ViewerOps USING[AddProp, DestroyViewer, FetchProp, PaintViewer], ViewerTools USING[GetContents, GetSelectedViewer, InhibitUserEdits, MakeNewTextViewer, SetContents, SetSelection]; GCellImplB: PROGRAM IMPORTS Ascii, Buttons, Dependencies, Expressions, GCell, IO, Real, Rope, StructureNodes, VFonts, ViewerOps, ViewerTools EXPORTS GCell, GCellPrivateDefs = BEGIN OPEN Dependencies, Expressions, GCell, GCellPrivateDefs, Rope, IO, ViewerClasses; -- for debugging nGCellItems: LONG CARDINAL _ 0; -- main stuff ItemRefDesc: TYPE = REF ItemRefDescBody; ItemRefDescBody: TYPE = RECORD[document: NewCalcGlobal.Document, da: StructureNodes.DisplayArray, I, J: CARDINAL, itemX: CARDINAL]; -- local procedures GCellItemVersion: INTEGER = 3; WriteOneItem: PUBLIC 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: PUBLIC 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; id: Rope.ROPE; text: Rope.ROPE; zeroValText: Rope.ROPE; ReadText: PROCEDURE RETURNS[Rope.ROPE] = BEGIN -- assumes that the form of text is -- a char count -- one or more blanks -- a '{ -- the characters of the text -- a '} n: INTEGER; -- number of characters in rope (new form) rSoFar: INTEGER; -- number of chars collected b1: Rope.ROPE; -- the first break char r: Rope.ROPE; -- either the text, or the second break char b3: Rope.ROPE; -- second break char, if not in r B1Proc: IO.BreakProc = TRUSTED {IF char # '{ THEN RETURN[sepr] ELSE RETURN[break]}; B2Proc: IO.BreakProc = TRUSTED {rSoFar _ rSoFar+1; IF rSoFar <= n THEN RETURN[other] ELSE RETURN[break]}; B3Proc: IO.BreakProc = TRUSTED {IF char # '} THEN ERROR; RETURN[break]}; n _ IO.GetInt[from]; rSoFar _ 0; IF NOT Rope.Equal[b1 _ IO.GetTokenRope[from, B1Proc].token, "{"] THEN ERROR; r _ IO.GetTokenRope[from, B2Proc].token; IF Rope.Length[r] > n THEN {IF NOT Rope.Equal[r, "}"] THEN ERROR; RETURN[""]}; IF NOT Rope.Equal[b3 _ IO.GetTokenRope[from, B3Proc].token, "}"] THEN ERROR; RETURN[r]; END; id _ ReadText[]; text _ ReadText[]; zeroValText _ ReadText[]; 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 -- this code modified June 22, 1984 4:10:07 pm PDT so as to compile, by replacing with error, no attempt to make it work. CProc: IO.BreakProc = TRUSTED {ERROR}; -- 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.GetTokenRope[from, CProc].token; text: Rope.ROPE _ IO.GetTokenRope[from, CProc].token; zeroValText: Rope.ROPE _ IO.GetTokenRope[from, CProc].token; RETURN[[mode, just, noEffect, noEffect, expression, id, text, zeroValText]]; END; ClearGCell: PUBLIC 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; -- local procedures, item specific CreateItem: PUBLIC PROCEDURE[idTables: IdTableSet, 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[idTables, gc, item]; AnalyzeItemId[idTables, gc, item]; HandleItemValueChanges[gc, item]; END; showExp => BEGIN item.buttonText _ desc.text; item.buttonTextDirty _ TRUE; item.buttonTextAnalyzed _ FALSE; item.editable _ TRUE; AnalyzeItemExpression[idTables, gc, item]; AnalyzeItemId[idTables, gc, item]; HandleItemValueChanges[gc, item]; END; showValue, noShow => BEGIN AnalyzeItemExpression[idTables, gc, item]; AnalyzeItemId[idTables, gc, item]; HandleItemValueChanges[gc, item]; IF show THEN SetItemValue[gc, item, item.value]; END; ENDCASE => ERROR; IF gc.da # NIL THEN StructureNodes.MarkElementDirty[gc.da, 0, 0]; END; GetItemDescriptor: PUBLIC PROCEDURE[item: Item] RETURNS[GCellItemDescriptor] = {RETURN[[item.mode, item.justification, item.rowSumMode, item.colSumMode, item.valueMode, item.idName, item.expressionText, item.zeroValText]]}; SubstituteInItem: PUBLIC PROCEDURE[idTables: IdTableSet, 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[idTables, 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[idTables, gc, item]; changes _ TRUE; END; END; IF changes THEN {HandleItemValueChanges[gc, item]; DisplayNewButtonText[gc, item]}; StructureNodes.MarkElementDirty[gc.da, 0, 0]; END; CheckItemForDirtyData: PUBLIC 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 idTablesPresent: BOOLEAN _ FALSE; idTables: IdTableSet; ReceiveIdTables: PROCEDURE[ids: IdTableSet] = {idTables _ ids}; 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 IF NOT idTablesPresent THEN GetIdTableSet[gc.gcaSn, item.selfI, item.selfJ, ReceiveIdTables]; AnalyzeItemExpression[idTables, gc, item]; HandleItemValueChanges[gc, item]; END; showId => BEGIN IF NOT idTablesPresent THEN GetIdTableSet[gc.gcaSn, item.selfI, item.selfJ, ReceiveIdTables]; AnalyzeItemId[idTables, 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: PUBLIC PROCEDURE[item: Item] RETURNS[BOOLEAN] = BEGIN RETURN[ SELECT item.mode FROM showText, showId, showExp, showValue => TRUE, ENDCASE => FALSE]; END; -- item specific computation procedures AnalyzeItemExpression: PUBLIC PROCEDURE[idTables: IdTableSet, gc: GCell, item: Item] = BEGIN BEGIN expression: Expression; IF item.valueMode # expression THEN RETURN; IF 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; IF item.viewer.destroyed THEN ERROR; 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.document.dSet, EvalItemExpression, item]; Dependencies.MarkActionDirty[item.evalAction]; END; item.expression _ AnalyzeExpression[expression, idTables, gc.document.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: PUBLIC PROCEDURE[idTables: IdTableSet, gc: GCell, item: Item] = BEGIN BEGIN id: Identifier; IF 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; IF item.viewer.destroyed THEN ERROR; ViewerTools.SetContents[item.viewer, item.buttonText]; GOTO idBad; END]; item.idBad _ FALSE; Dependencies.DeactivateAction[item.assignAction]; item.assignAction _ Dependencies.DefineAction[gc.document.dSet, AssignItemValue, item]; item.id _ AnalyzeIdentifier[id, idTables, gc.document.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: PUBLIC 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.document.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: PUBLIC 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: PUBLIC 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 ComputeItemButtonText[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; ComputeItemButtonText: PROCEDURE[value: REAL] RETURNS[Rope.ROPE] = BEGIN modifiedVal: REAL _ 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 ""; RETURN[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; 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: PUBLIC 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: PUBLIC 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; PaintItem: PUBLIC PROCEDURE[gc: GCell, item: Item, dirtyOnly: BOOLEAN] = BEGIN IF item.dirty OR NOT dirtyOnly THEN {ViewerOps.PaintViewer[item.viewer, all, FALSE]; item.dirty _ FALSE} END; NoteCoordinatesItem: PUBLIC PROCEDURE[item: Item, document: NewCalcGlobal.Document, da: StructureNodes.DisplayArray, I, J, x: CARDINAL] = BEGIN IF da = NIL THEN ERROR; item.da _ da; item.selfI _ I; item.selfJ _ J; item.selfItemX _ x; SELECT item.viewerMode FROM nil => NULL; button, textBox => BEGIN descRef: REF ANY _ ViewerOps.FetchProp[item.viewer, $GCellItem]; itemRefDesc: ItemRefDesc _ NARROW[descRef]; itemRefDesc.da _ da; itemRefDesc.I _ I; itemRefDesc.J _ J; itemRefDesc.itemX _ x; END; ENDCASE; END; ComputeTextShape: PUBLIC 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 Ascii.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 Ascii.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: FALSE, fork: TRUE]; ViewerOps.AddProp[button, $GCellItem, NEW[ItemRefDescBody _ [gc.document, item.da, item.selfI, item.selfJ, item.selfItemX]]]; item.viewerMode _ button; item.viewer _ button; item.dirty _ TRUE; END; END; DisplayButtonTextAsTextViewer: Buttons.ButtonProc = TRUSTED BEGIN viewer: Viewer _ NARROW[parent]; -- the button itself itemRefDesc: ItemRefDesc _ NARROW[ViewerOps.FetchProp[viewer, $GCellItem]]; DBTATVInternal: PROCEDURE = BEGIN gcSn: StructureNodes.StructureNode; gc: GCell; item: Item; [gcSn, gc, item] _ DecodeItemRefDesc[itemRefDesc]; 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]; StructureNodes.MarkElementDirty[gc.da, 0, 0] END; itemRefDesc.document.procs.executeInMonitor[itemRefDesc.document, DBTATVInternal]; END; ForceItemSelection: PUBLIC 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]; IF textBox.destroyed THEN ERROR; ViewerTools.SetContents[textBox, item.buttonText]; ViewerOps.AddProp[textBox, $GCellItem, NEW[ItemRefDescBody _ [gc.document, item.da, item.selfI, item.selfJ, item.selfItemX]]]; ViewerTools.SetSelection[textBox, NIL]; item.viewer _ textBox; item.viewerMode _ textBox; StructureNodes.MarkElementDirty[gc.da, 0, 0]; END; ClearItemViewer: PROCEDURE[item: Item] = BEGIN -- note: August 15, 1985 11:31:30 am PDT: When Cedar changed from 5.2 to 6.0, buttons and text boxes are painted asynchronously. The fix is to set the contents with paint=FALSE, then directly call ViewerOps.PaintViewer. See similar commend in HCellImpl. IF item.viewerMode = button THEN Buttons.ReLabel[item.viewer, "", FALSE]; IF item.viewerMode = textBox THEN BEGIN IF item.viewer.destroyed THEN ERROR; ViewerTools.SetContents[item.viewer, "", FALSE]; END; ViewerOps.PaintViewer[item.viewer, all]; 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 itemRefDesc: ItemRefDesc _ NARROW[itemRef]; gcSn: StructureNodes.StructureNode; gc: GCell; item: Item; [gcSn, gc, item] _ 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 x: CARDINAL IN [1..itemRefDesc.itemX) 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.. -- February 28, 1983 5:01 pm: Sturgis, started GCellImplB.mesa, will hold the item procedures of old GCellImpl. -- RTE: March 1, 1983 9:05 am: de-referencing item.viewer fails because item is not yet known. Have to wrok from parent = the button. -- RTE: March 1, 1983 9:14 am: ForceNoteItemRefDesc forgot to decode the item ref. -- RTE: March 1, 1983 1:38 pm: monitor problems. Must enter monitor before decoding ItemRefDesc, so have to add ngcGlobal to the descriptor. Also, must not change the descriptor associated with a property, only its contents (since the descriptor is fetched outside the monitor, so that ngcglobal can be sued to enter the monitor.) -- RTE: March 1, 1983 1:56 pm: last fix dropped recrding of da in item at ItemNoteCoordinates. -- RTE: March 1, 1983 2:00 pm: item scan loop in DecodeItemRefDescriptor used wrong limit. (J instead of x). -- RTE: March 1, 1983 2:55 pm: destroyed viewer tries to scroll. Add a test for viewer.destroyed before each set contents to try to catch it. -- March 8, 1983 12:15 pm: add code to call display array to mark item dirty. -- Change: August 14, 1983: modify computation of button text (when computed from value) to always output exactly two digits after decimal point (and round to that representation). -- Change: June 22, 1984 4:17:57 pm PDT: convert to Cedar 5.2 (old style formats are not converted) -- CTE: June 22, 1984 5:38:47 pm PDT: bugs in scanning, added a break char return; -- CTE: June 23, 1984 12:08:33 pm PDT: more bugs in scanning, had to redisign the scheme for reading in the text, is now more of a kludge then ever, but it works. -- RTE: August 15, 1985 11:32:59 am PDT: The change from Cedar 5.2 to Cedar 6.0 caused some painting bugs. The fix is in ClearItemViewer. ÊF˜Jšœ´•˜´•Jšœ˜J˜