-- 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.