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