-- ColumnOfRows.mesa
-- last edit July 23, 1984 9:59:01 am PDT Sturgis

DIRECTORY
GCell USING[CreateGCell, CreateGCellArray, GetGCellArray, LoadGCellArray, SetAColumnOfRowsProc, SetAGCellArrayProc, SetARowOfSlotsProc, SetASlotProc, SetGCellArray, SlotCase],
 HCells USING[CreateHCell, CreateHCellArray, GetSummaryArray, HCell, LoadHCellArray, ResetPageNameOfHCellArray, SetHCellParameters, SetHCellText],
IO USING[atom, card, char, GetCard, GetInt, GetRefAny, GetTokenRope, STREAM, int, Put],
NewCalcGlobal USING[Displayer, Document],
Rope USING[Equal, ROPE],
StructureNodes USING[Action, AddColumnAt, AddRowAt, BadFormatLoadFile, CreateDisplayArray, DACell, DeleteColumnAt, DeleteRowAt, DisplayArray, GetContextDA, GetElementAt, PrePaintPass, SetElementAt, StructureNode, StructureNodeBody, StructureNodeProcs],
ViewerClasses USING[Viewer];


ColumnOfRows: PROGRAM IMPORTS GCell, HCells, IO, Rope, StructureNodes EXPORTS GCell =

BEGIN OPEN IO, StructureNodes, ViewerClasses;

-- column of rows


Column: TYPE = REF ColumnBody;
ColumnBody: TYPE = RECORD[
daSn: StructureNodes.StructureNode,
da: StructureNodes.DisplayArray,
nRows: CARDINAL,
document: NewCalcGlobal.Document,
displayer: NewCalcGlobal.Displayer,
vParent: Viewer,
pageName: Rope.ROPE];

ColOfRowsStructureNodeProcs: REF StructureNodes.StructureNodeProcs ← NEW[StructureNodes.StructureNodeProcs ← [ColumnOfRowsCheckForDirtyData, ColumnOfRowsPreShow, ColumnOfRowsShowLine, ColumnOfRowsPrePrePaint, ColumnOfRowsPrePaint, ColumnOfRowsPaint, ColumnOfRowsUnPaint, ColumnOfRowsNoteEnclosingCell, ColumnOfRowsNoteCoordinates, SaveColumnOfRows, ActColumnOfRows, SubstituteInColumnOfRows, AbandonColumnOfRows, VerifyColumnOfRows]];

LoadColumnOfRows: PUBLIC PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, vParent: Viewer, name: Rope.ROPE, from: IO.STREAM, version: CARDINAL] RETURNS[StructureNode] =
BEGIN -- note: column of rows atom has been read
col: Column;
colSn: StructureNode;
[col, colSn] ← BasicCreateColumnOfRows[document, displayer, vParent, name];
IF name # NIL THEN InstallRowAtColumnOfRows[col, 1, name, displayPageName];
col.nRows ← GetInt[from]+ (IF name # NIL THEN 1 ELSE 0);
FOR J: CARDINAL IN [(IF name # NIL THEN 2 ELSE 1)..col.nRows] DO
rssn: StructureNode; -- holds the row of slots
IF NOT Rope.Equal[GetTokenRope[from].token, "("] THEN BadFormatLoadFile;
IF NARROW[GetRefAny[from], ATOM] # $RowOfSlots THEN BadFormatLoadFile;
rssn ← LoadRowOfSlots[document, displayer, vParent, name, from, version];
IF NOT Rope.Equal[GetTokenRope[from].token, ")"] THEN BadFormatLoadFile;
StructureNodes.AddRowAt[col.da, J];
StructureNodes.SetElementAt[col.da, 1, J, rssn, NIL];
ENDLOOP;
RETURN[colSn]
END;

CreateColumnOfRows: PUBLIC PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, vParent: Viewer, pageName: Rope.ROPE] RETURNS[StructureNode] =
BEGIN
col: Column;
colSn: StructureNode;
[col, colSn] ← BasicCreateColumnOfRows[document, displayer, vParent, pageName];
InstallRowAtColumnOfRows[col, 1, pageName, displayPageName];
InstallRowAtColumnOfRows[col, 2, pageName, clientRow];
RETURN[colSn]
END;

ResetPageNameOfColumnOfRows: PUBLIC PROCEDURE[info: StructureNode, pageName: Rope.ROPE] =
BEGIN
col: Column ← NARROW[info.ref];
col.pageName ← pageName;
FOR J: CARDINAL IN [1..col.nRows] DO
 ResetPageNameOfRow[StructureNodes.GetElementAt[col.da, 1, J].sn, pageName];
 ENDLOOP;
END;

GetColumnOfRows: PUBLIC PROCEDURE[info: StructureNode, send: GCell.SetAColumnOfRowsProc, entry: BOOLEAN] =
 BEGIN
column: Column ← NARROW[info.ref];
 GCS: PROCEDURE =
 BEGIN
 J: CARDINAL ← 1;

GetOneRow: PROCEDURE[sendIt: GCell.SetARowOfSlotsProc] =
 BEGIN
 ssn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[column.da, 1, J].sn;
 GetRowOfSlots[ssn, sendIt, FALSE];
 J ← J + 1;
 END;

send[column.nRows, GetOneRow];
END;
IF entry THEN column.document.procs.executeInMonitor[column.document, GCS] ELSE GCS[];
 END;

SetColumnOfRows: PUBLIC PROCEDURE[sn: StructureNode, nRows: CARDINAL, getOneRow: PROCEDURE[GCell.SetARowOfSlotsProc], entry: BOOLEAN] =
 BEGIN
column: Column ← NARROW[sn.ref];
 SCS: PROCEDURE =
 BEGIN
 WHILE column.nRows # 0 DO RemoveRowAtColumnOfRows[column, column.nRows] ENDLOOP;
 column.nRows ← nRows;
 FOR J: CARDINAL IN [1..nRows] DO
  rssn: StructureNode; -- holds the row
  
  SetTheRow: GCell.SetARowOfSlotsProc =
   {SetRowOfSlots[rssn, nSlots, GetOneSlot, FALSE]};
   
  rssn ← CreateRowOfSlots[column.document, column.displayer, column.vParent, NIL, clientRow];
  StructureNodes.AddRowAt[column.da, J];
  StructureNodes.SetElementAt[column.da, 1, J, rssn, NIL];
  getOneRow[SetTheRow];
  ENDLOOP;
END;
IF entry THEN column.document.procs.executeInMonitor[column.document, SCS] ELSE SCS[];
 END;

CountOpenSlotsInColumnOfRows: PUBLIC PROCEDURE[node: StructureNode] RETURNS[CARDINAL] =
 BEGIN
column: Column ← NARROW[node.ref];
count: CARDINAL ← 0;
FOR J: CARDINAL IN [1..column.nRows] DO
 count ← count + CountOpenSlotsInRowOfSlots[StructureNodes.GetElementAt[column.da, 1, J].sn];
 ENDLOOP;
RETURN[count];
 END;

InstallHCellArrayAtOpenSlotInColumnOfRows: PUBLIC PROCEDURE[sn: StructureNode, rowNumber, colNumber: CARDINAL, case: GCell.SlotCase, createIt: PROCEDURE[displayer: NewCalcGlobal.Displayer, vParent: ViewerClasses.Viewer, pageName: Rope.ROPE] RETURNS[StructureNode]] =
 BEGIN
column: Column ← NARROW[sn.ref];
row: StructureNode ← StructureNodes.GetElementAt[column.da, 1, rowNumber].sn;
InstallHCellArrayAtOpenSlotInRowOfSlots[row, colNumber, case, createIt];
 END;

InstallHCellArrayAtAnOpenSlotInColumnOfRows: PUBLIC PROCEDURE[sn: StructureNode, case: GCell.SlotCase, createIt: PROCEDURE[displayer: NewCalcGlobal.Displayer, vParent: ViewerClasses.Viewer, pageName: Rope.ROPE, rowNumber, colNumber: CARDINAL] RETURNS[StructureNode]] =
 BEGIN
column: Column ← NARROW[sn.ref];
FOR J: CARDINAL IN [1..column.nRows] DO
 IF InstallHCellArrayAtAnOpenSlotInRowOfSlots[StructureNodes.GetElementAt[column.da, 1, J].sn, J, case, createIt] THEN RETURN;
 ENDLOOP;
 END;

GetHCellArrayAtSlotInColumnOfRows: PUBLIC PROCEDURE[sn: StructureNode, rowNumber, colNumber: CARDINAL] RETURNS[StructureNode] =
 BEGIN
column: Column ← NARROW[sn.ref];
row: StructureNode ← StructureNodes.GetElementAt[column.da, 1, rowNumber].sn;
RETURN[GetHCellArrayAtSlotInRowOfSlots[row, colNumber]];
 END;

ConnectInstalledItemArraysInColumnOfRows: PUBLIC PROCEDURE[sn: StructureNode, connectOneItemArray: PROCEDURE[itemHcaSn: StructureNodes.StructureNode, row, col: CARDINAL]] =
 BEGIN
column: Column ← NARROW[sn.ref];
FOR J: CARDINAL IN [1..column.nRows] DO
ConnectInstalledItemArraysInRowOfSlots[StructureNodes.GetElementAt[column.da, 1, J].sn, J, connectOneItemArray];
 ENDLOOP;
END;

GenerateSummaryArraysInColumnOfRows: PUBLIC PROCEDURE[sn: StructureNode, seeOneArray: PROCEDURE[summaryHcaSn: StructureNodes.StructureNode, summaryKey: LONG CARDINAL] RETURNS[continue: BOOLEAN]] =
 BEGIN
column: Column ← NARROW[sn.ref];
FOR J: CARDINAL IN [1..column.nRows] DO
IF NOT GenerateSummaryArraysInRowOfSlots[StructureNodes.GetElementAt[column.da, 1, J].sn, seeOneArray] THEN EXIT;
 ENDLOOP;
END;

ConnectLoadedItemArraysInColumnOfRows: PUBLIC PROCEDURE[sn: StructureNode, connectOneItemArray: PROCEDURE[itemHcaSn: StructureNodes.StructureNode, itemKey: LONG CARDINAL, atRow: CARDINAL]] =
 BEGIN
column: Column ← NARROW[sn.ref];
FOR J: CARDINAL IN [1..column.nRows] DO
ConnectLoadedItemArraysInRowOfSlots[StructureNodes.GetElementAt[column.da, 1, J].sn, connectOneItemArray];
 ENDLOOP;
END;

ColumnOfRowsCheckForDirtyData: PROCEDURE[node: StructureNode] =
 BEGIN
 column: Column ← NARROW[node.ref];
 column.daSn.procs.CheckForDirtyData[column.daSn];
 END;

ColumnOfRowsPreShow: PROCEDURE[node: StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
 BEGIN
 column: Column ← NARROW[node.ref];
 [lead, height, space, L, A, B, R, W] ← column.daSn.procs.PreShow[column.daSn];
 END;

ColumnOfRowsShowLine: PROCEDURE[node: StructureNode, L, R, W: INTEGER, lineX: INTEGER, to: STREAM] =
 BEGIN
 column: Column ← NARROW[node.ref];
 column.daSn.procs.ShowLine[column.daSn, L, R, W, lineX, to];
 END;


ColumnOfRowsPrePrePaint: PROCEDURE[node: StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
 BEGIN
 column: Column ← NARROW[node.ref];
 [lead, height, space, L, A, B, R, W] ← column.daSn.procs.PrePrePaint[column.daSn];
 END;


ColumnOfRowsPrePaint: PROCEDURE[node: StructureNode, y, x, L, R, W: INTEGER, pass: PrePaintPass] =
 BEGIN
 column: Column ← NARROW[node.ref];
 column.daSn.procs.PrePaint[column.daSn, y, x, L, R, W, pass];
 END;

ColumnOfRowsPaint: PROCEDURE[node: StructureNode, dirtyOnly: BOOLEAN] =
 BEGIN
 column: Column ← NARROW[node.ref];
 column.daSn.procs.Paint[column.daSn, dirtyOnly];
 END;

ColumnOfRowsUnPaint: PROCEDURE[node: StructureNode] =
 BEGIN
 column: Column ← NARROW[node.ref];
 column.daSn.procs.UnPaint[column.daSn];
 END;

ColumnOfRowsNoteEnclosingCell: PROCEDURE[node: StructureNode, dac: DACell] =
 BEGIN
 column: Column ← NARROW[node.ref];
 column.daSn.procs.NoteEnclosingCell[column.daSn, dac];
 END;


ColumnOfRowsNoteCoordinates: PROCEDURE[node: StructureNode, da: DisplayArray, I, J: CARDINAL] =
 BEGIN
 column: Column ← NARROW[node.ref];
 column.daSn.procs.NoteCoordinates[column.daSn, da, I, J];
 END;

SaveColumnOfRows: PROCEDURE[info: StructureNode, to: IO.STREAM] =
BEGIN
col: Column ← NARROW[info.ref];
Put[to, char[' ], int[col.nRows-1]];
FOR J: CARDINAL IN (1..col.nRows] DO
rssn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[col.da, 1, J].sn;
Put[to, char['(], char[' ], atom[$RowOfSlots]];
rssn.procs.Save[rssn, to];
Put[to, char[')]];
ENDLOOP;
END;

SubstituteInColumnOfRows: PROCEDURE[info: StructureNode, newText, oldText: Rope.ROPE] =
BEGIN
col: Column ← NARROW[info.ref];
FOR J: CARDINAL IN [1..col.nRows] DO
rssn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[col.da, 1, J].sn;
rssn.procs.Substitute[rssn, newText, oldText];
ENDLOOP;
END;

AbandonColumnOfRows: PROCEDURE[info: StructureNode] =
BEGIN
col: Column ← NARROW[info.ref];
WHILE col.nRows # 0 DO RemoveRowAtColumnOfRows[col, col.nRows] ENDLOOP;
StructureNodes.DeleteColumnAt[col.da, 1];
END;

ActColumnOfRows: PROCEDURE[info: StructureNode, I, J: CARDINAL, action: StructureNodes.Action, p1, p2: REF ANY] RETURNS[StructureNode, CARDINAL, CARDINAL] =
BEGIN
col: Column ← NARROW[info.ref];
SELECT action FROM
InsertRow => IF J # 1 THEN InstallRowAtColumnOfRows[col, J, col.pageName, clientRow];
AppendRow => InstallRowAtColumnOfRows[col, J+1, col.pageName, clientRow];
DeleteRow => IF J # 1 THEN RemoveRowAtColumnOfRows[col, J];
ENDCASE =>
 BEGIN
 cSn: StructureNode; cI, cJ: CARDINAL;
 [cSn, cI, cJ] ← GetContextDA[col.da];
 RETURN[cSn, cI, cJ];
 END;
RETURN[NIL, 0, 0];
END;

VerifyColumnOfRows: PROCEDURE[info: StructureNode] =
BEGIN
col: Column ← NARROW[info.ref];
FOR J: CARDINAL IN (1..col.nRows] DO
rssn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[col.da, 1, J].sn;
rssn.procs.Verify[rssn];
ENDLOOP;
END;

-- support routines for Column of rows

BasicCreateColumnOfRows: PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, vParent: Viewer, pageName: Rope.ROPE] RETURNS[Column, StructureNode] =
BEGIN
col: Column ← NEW[ColumnBody ← [NIL, NIL, 0, document, displayer, vParent, pageName]];
colSn: StructureNode ← NEW[StructureNodeBody ← [col, ColOfRowsStructureNodeProcs]];
[col.daSn, col.da] ← CreateDisplayArray[colSn];
StructureNodes.AddColumnAt[col.da, 1];
RETURN[col, colSn]
END;

InstallRowAtColumnOfRows: PROCEDURE[col: Column, J: CARDINAL, pageName: Rope.ROPE, rowCase: RowCase] =
BEGIN
rssn: StructureNode; -- holds the row of slots

IF J = 0 OR J > col.nRows+1 THEN ERROR;
col.nRows ← col.nRows+1;
StructureNodes.AddRowAt[col.da, J];
rssn ← CreateRowOfSlots[col.document, col.displayer, col.vParent, pageName, rowCase];
StructureNodes.SetElementAt[col.da, 1, J, rssn, NIL];
END;


RemoveRowAtColumnOfRows: PROCEDURE[col: Column, J: CARDINAL] =
BEGIN
IF J = 0 OR J > col.nRows THEN ERROR;
StructureNodes.DeleteRowAt[col.da, J];
col.nRows ← col.nRows-1;
END;



-- row of slots

CellWidth: INTEGER = 50;
CellHeight: INTEGER = 20;

RowCase: TYPE = {clientRow, displayPageName};

RowOfSlots: TYPE = REF RowOfSlotsBody;
RowOfSlotsBody: TYPE = RECORD[
  daSn: StructureNodes.StructureNode,
da: StructureNodes.DisplayArray,
nSlots: CARDINAL,
document: NewCalcGlobal.Document,
displayer: NewCalcGlobal.Displayer,
vParent: Viewer,
pageName: Rope.ROPE,
rowCase: RowCase];

RowOfSlotsStructureNodeProcs: REF StructureNodes.StructureNodeProcs ← NEW[StructureNodes.StructureNodeProcs ← [RowOfSlotsCheckForDirtyData, RowOfSlotsPreShow, RowOfSlotsShowLine, RowOfSlotsPrePrePaint, RowOfSlotsPrePaint, RowOfSlotsPaint, RowOfSlotsUnPaint, RowOfSlotsNoteEnclosingCell, RowOfSlotsNoteCoordinates, SaveRowOfSlots, ActRowOfSlots, SubstituteInRowOfSlots, AbandonRowOfSlots, VerifyRowOfSlots]];

LoadRowOfSlots: PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, vParent: Viewer, pageName: Rope.ROPE, from: IO.STREAM, version: CARDINAL] RETURNS[StructureNode] =
BEGIN -- the row of slots atom has been read
row: RowOfSlots;
rowSn: StructureNode;
[row, rowSn] ← BasicCreateRowOfSlots[document, displayer, vParent, pageName, clientRow];
row.nSlots ← GetInt[from];
FOR I: CARDINAL IN [1..row.nSlots] DO
rssn: StructureNode; -- holds the slot
IF NOT Rope.Equal[GetTokenRope[from].token, "("] THEN BadFormatLoadFile;
IF NARROW[GetRefAny[from], ATOM] # $Slot THEN BadFormatLoadFile;
rssn ← LoadSlot[document, displayer, vParent, pageName, from, version];
IF NOT Rope.Equal[GetTokenRope[from].token, ")"] THEN BadFormatLoadFile;
StructureNodes.AddColumnAt[row.da, I];
StructureNodes.SetElementAt[row.da, I, 1, rssn, NIL];
ENDLOOP;
RETURN[rowSn]
END;

CreateRowOfSlots: PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, vParent: Viewer, pageName: Rope.ROPE, rowCase: RowCase] RETURNS[StructureNode] =
BEGIN
row: RowOfSlots;
rowSn: StructureNode;
[row, rowSn] ← BasicCreateRowOfSlots[document, displayer, vParent, pageName, rowCase];
InstallSlotAtRowOfSlots[row, 1];
RETURN[rowSn]
END;

ResetPageNameOfRow: PROCEDURE[rsn: StructureNode, name: Rope.ROPE] =
BEGIN
row: RowOfSlots ← NARROW[rsn.ref];
FOR I: CARDINAL IN [1..row.nSlots] DO
 slotSn: StructureNode ← StructureNodes.GetElementAt[row.da, I, 1].sn;
 ResetPageNameOfSlot[slotSn, name];
 ENDLOOP;
END;


GetRowOfSlots: PROCEDURE[info: StructureNode, send: GCell.SetARowOfSlotsProc, entry: BOOLEAN] =
 BEGIN
row: RowOfSlots ← NARROW[info.ref];
 GRS: PROCEDURE =
 BEGIN
 I: CARDINAL ← 1;

GetOneSlot: PROCEDURE[sendIt: GCell.SetASlotProc] =
 BEGIN
 ssn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[row.da, I, 1].sn;
 GetSlot[ssn, sendIt, FALSE];
 I ← I + 1;
 END;

send[row.nSlots, GetOneSlot];
END;
IF entry THEN row.document.procs.executeInMonitor[row.document, GRS] ELSE GRS[];
 END;

SetRowOfSlots: PROCEDURE[info: StructureNode, nSlots: CARDINAL, getOneSlot: PROCEDURE[GCell.SetASlotProc], entry: BOOLEAN] =
 BEGIN
row: RowOfSlots ← NARROW[info.ref];
 SRS: PROCEDURE =
 BEGIN
 WHILE row.nSlots # 0 DO RemoveSlotAtRowOfSlots[row, row.nSlots] ENDLOOP;
 row.nSlots ← nSlots;
 FOR I: CARDINAL IN [1..nSlots] DO
  rssn: StructureNode; -- holds the slot
  
  SetTheSlot: GCell.SetASlotProc =
   {SetSlot[rssn, case, GetOneArray, FALSE]};
   
  rssn ← CreateSlot[row.document, row.displayer, row.vParent, row.pageName, row.rowCase];
  StructureNodes.AddColumnAt[row.da, I];
  StructureNodes.SetElementAt[row.da, I, 1, rssn, NIL];
  getOneSlot[SetTheSlot];
  ENDLOOP;
END;
IF entry THEN row.document.procs.executeInMonitor[row.document, SRS] ELSE SRS[];
 END;

CountOpenSlotsInRowOfSlots: PROCEDURE[node: StructureNode] RETURNS[CARDINAL] =
 BEGIN
row: RowOfSlots ← NARROW[node.ref];
count: CARDINAL ← 0;
FOR I: CARDINAL IN [1..row.nSlots] DO
 IF SlotCheckForOpenSlot[StructureNodes.GetElementAt[row.da, I, 1].sn] THEN
  count ← count + 1;
 ENDLOOP;
RETURN[count];
 END;

InstallHCellArrayAtOpenSlotInRowOfSlots: PROCEDURE[sn: StructureNode, colNumber: CARDINAL, case: GCell.SlotCase, createIt: PROCEDURE[displayer: NewCalcGlobal.Displayer, vParent: ViewerClasses.Viewer, pageName: Rope.ROPE] RETURNS[StructureNode]] =
 BEGIN
row: RowOfSlots ← NARROW[sn.ref];
SlotInstallHCellArrayAtOpenSlot[StructureNodes.GetElementAt[row.da, colNumber, 1].sn, case, createIt];
 END;

InstallHCellArrayAtAnOpenSlotInRowOfSlots: PROCEDURE[sn: StructureNode, rowNumber: CARDINAL, case: GCell.SlotCase, createIt: PROCEDURE[displayer: NewCalcGlobal.Displayer, vParent: ViewerClasses.Viewer, pageName: Rope.ROPE, rowNumber, colNumber: CARDINAL] RETURNS[StructureNode]] RETURNS[didIt: BOOLEAN] =
 BEGIN
row: RowOfSlots ← NARROW[sn.ref];
FOR I: CARDINAL IN [1..row.nSlots] DO
 IF SlotInstallHCellArrayAtAnOpenSlot[StructureNodes.GetElementAt[row.da, I, 1].sn, rowNumber, I, case, createIt] THEN
  RETURN[TRUE];
 ENDLOOP;
RETURN[FALSE];
 END;

GetHCellArrayAtSlotInRowOfSlots: PROCEDURE[sn: StructureNode, colNumber: CARDINAL] RETURNS[StructureNode] =
 BEGIN
row: RowOfSlots ← NARROW[sn.ref];
RETURN[SlotGetHCellArrayAtSlot[StructureNodes.GetElementAt[row.da, colNumber, 1].sn]];
 END;

ConnectInstalledItemArraysInRowOfSlots: PROCEDURE[sn: StructureNode, rowNumber: CARDINAL, connectOneItemArray: PROCEDURE[itemHcaSn: StructureNodes.StructureNode, row, col: CARDINAL]] =
 BEGIN
row: RowOfSlots ← NARROW[sn.ref];
FOR I: CARDINAL IN [1..row.nSlots] DO
 SlotConnectInstalledItemArrays[StructureNodes.GetElementAt[row.da, I, 1].sn, rowNumber, I, connectOneItemArray]
 ENDLOOP;
 END;

GenerateSummaryArraysInRowOfSlots: PROCEDURE[sn: StructureNode, seeOneArray: PROCEDURE[summaryHcaSn: StructureNodes.StructureNode, summaryKey: LONG CARDINAL] RETURNS[continue: BOOLEAN]] RETURNS[continue: BOOLEAN] =
 BEGIN
row: RowOfSlots ← NARROW[sn.ref];
FOR I: CARDINAL IN [1..row.nSlots] DO
 IF NOT SlotGenerateSummaryArrays[StructureNodes.GetElementAt[row.da, I, 1].sn, seeOneArray] THEN RETURN[FALSE];
 ENDLOOP;
RETURN[TRUE];
 END;

ConnectLoadedItemArraysInRowOfSlots: PROCEDURE[sn: StructureNode, connectOneItemArray: PROCEDURE[itemHcaSn: StructureNodes.StructureNode, itemKey: LONG CARDINAL, atRow: CARDINAL]] =
 BEGIN
row: RowOfSlots ← NARROW[sn.ref];
FOR I: CARDINAL IN [1..row.nSlots] DO
 SlotConnectLoadedItemArrays[StructureNodes.GetElementAt[row.da, I, 1].sn, connectOneItemArray];
 ENDLOOP;
 END;

RowOfSlotsCheckForDirtyData: PROCEDURE[node: StructureNode] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 row.daSn.procs.CheckForDirtyData[row.daSn];
 END;

RowOfSlotsPreShow: PROCEDURE[node: StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 [lead, height, space, L, A, B, R, W] ← row.daSn.procs.PreShow[row.daSn];
 END;

RowOfSlotsShowLine: PROCEDURE[node: StructureNode, L, R, W: INTEGER, lineX: INTEGER, to: STREAM] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 row.daSn.procs.ShowLine[row.daSn, L, R, W, lineX, to];
 END;


RowOfSlotsPrePrePaint: PROCEDURE[node: StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 [lead, height, space, L, A, B, R, W] ← row.daSn.procs.PrePrePaint[row.daSn];
 END;


RowOfSlotsPrePaint: PROCEDURE[node: StructureNode, y, x, L, R, W: INTEGER, pass: PrePaintPass] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 row.daSn.procs.PrePaint[row.daSn, y, x, L, R, W, pass];
 END;


RowOfSlotsPaint: PROCEDURE[node: StructureNode, dirtyOnly: BOOLEAN] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 row.daSn.procs.Paint[row.daSn, dirtyOnly];
 END;


RowOfSlotsUnPaint: PROCEDURE[node: StructureNode] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 row.daSn.procs.UnPaint[row.daSn];
 END;


RowOfSlotsNoteEnclosingCell: PROCEDURE[node: StructureNode, dac: DACell] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 row.daSn.procs.NoteEnclosingCell[row.daSn, dac];
 END;


RowOfSlotsNoteCoordinates: PROCEDURE[node: StructureNode, da: DisplayArray, I, J: CARDINAL] =
 BEGIN
 row: RowOfSlots ← NARROW[node.ref];
 row.daSn.procs.NoteCoordinates[row.daSn, da, I, J];
 END;

SaveRowOfSlots: PROCEDURE[info: StructureNode, to: IO.STREAM] =
BEGIN
row: RowOfSlots ← NARROW[info.ref];
Put[to, char[' ], int[row.nSlots]];
FOR I: CARDINAL IN [1..row.nSlots] DO
ssn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[row.da, I, 1].sn;
Put[to, char['(], char[' ], atom[$Slot]];
ssn.procs.Save[ssn, to];
Put[to, char[')]];
ENDLOOP;
END;

SubstituteInRowOfSlots: PROCEDURE[info: StructureNode, newText, oldText: Rope.ROPE] =
 BEGIN
 row: RowOfSlots ← NARROW[info.ref];
 FOR I: CARDINAL IN [1..row.nSlots] DO
ssn: StructureNodes.StructureNode ← StructureNodes.GetElementAt[row.da, I, 1].sn;
ssn.procs.Substitute[ssn, newText, oldText];
ENDLOOP;
 END;

AbandonRowOfSlots: PROCEDURE[info: StructureNode] =
BEGIN
row: RowOfSlots ← NARROW[info.ref];
WHILE row.nSlots # 0 DO RemoveSlotAtRowOfSlots[row, row.nSlots] ENDLOOP;
StructureNodes.DeleteRowAt[row.da, 1];
END;

ActRowOfSlots: PROCEDURE[info: StructureNode, I, J: CARDINAL, action: StructureNodes.Action, p1, p2: REF ANY] RETURNS[StructureNode, CARDINAL, CARDINAL] =
BEGIN
row: RowOfSlots ← NARROW[info.ref];
SELECT action FROM
InsertSlot => IF row.rowCase = clientRow THEN InstallSlotAtRowOfSlots[row, I];
AppendSlot => IF row.rowCase = clientRow THEN InstallSlotAtRowOfSlots[row, I+1];
DeleteSlot => IF row.rowCase = clientRow THEN RemoveSlotAtRowOfSlots[row, I];
ENDCASE =>
 BEGIN
 cSn: StructureNode; cI, cJ: CARDINAL;
 [cSn, cI, cJ] ← GetContextDA[row.da];
 RETURN[cSn, cI, cJ];
 END;
RETURN[NIL, 0, 0];
END;

VerifyRowOfSlots: PROCEDURE[info: StructureNode] =
BEGIN
row: RowOfSlots ← NARROW[info.ref];
FOR I: CARDINAL IN [1..row.nSlots] DO
 ssn: StructureNode ← StructureNodes.GetElementAt[row.da, I, 1].sn;
 ssn.procs.Verify[ssn];
 ENDLOOP;
 END;


-- support routines for RowOfSlots

BasicCreateRowOfSlots: PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, vParent: Viewer, pageName: Rope.ROPE, rowCase: RowCase] RETURNS[RowOfSlots, StructureNode] =
BEGIN
row: RowOfSlots ← NEW[RowOfSlotsBody ← [NIL, NIL, 0, document, displayer, vParent, pageName, rowCase]];
rowSn: StructureNode ← NEW[StructureNodeBody ← [row, RowOfSlotsStructureNodeProcs]];
[row.daSn, row.da] ← CreateDisplayArray[rowSn];
StructureNodes.AddRowAt[row.da, 1];
RETURN[row, rowSn]
END;

InstallSlotAtRowOfSlots: PROCEDURE[row: RowOfSlots, I: CARDINAL] =
BEGIN
ssn: StructureNode; -- holds the slot
IF I = 0 OR I > row.nSlots+1 THEN ERROR;
row.nSlots ← row.nSlots+1;
StructureNodes.AddColumnAt[row.da, I];
ssn ← CreateSlot[row.document, row.displayer, row.vParent, row.pageName, row.rowCase];
StructureNodes.SetElementAt[row.da, I, 1, ssn, NIL];
END;

RemoveSlotAtRowOfSlots: PROCEDURE[row: RowOfSlots, I: CARDINAL] =
BEGIN
IF I = 0 OR I > row.nSlots THEN ERROR;
StructureNodes.DeleteColumnAt[row.da, I];
row.nSlots ← row.nSlots-1;
END;


-- slots

Slot: TYPE = REF SlotBody;
SlotBody: TYPE = RECORD[
daSn: StructureNodes.StructureNode,
da: StructureNodes.DisplayArray,
document: NewCalcGlobal.Document,
displayer: NewCalcGlobal.Displayer,
vParent: Viewer,
pageName: Rope.ROPE,

case: GCell.SlotCase,
key: LONG CARDINAL, -- used during loading
rowInSummaryArray: CARDINAL -- used during loading
];

PageIdSlotInfo: TYPE = REF PageIdSlotInfoBody;
PageIdSlotInfoBody: TYPE = RECORD[hCell: HCells.HCell];
   
   

SlotStructureNodeProcs: REF StructureNodes.StructureNodeProcs ← NEW[StructureNodes.StructureNodeProcs ← [SlotCheckForDirtyData, SlotPreShow, SlotShowLine, SlotPrePrePaint, SlotPrePaint, SlotPaint, SlotUnPaint, SlotNoteEnclosingCell, SlotNoteCoordinates, SaveSlot, ActSlot, SubstituteInSlot, AbandonSlot, VerifySlot]];

LoadSlot: PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, vParent: Viewer, pageName: Rope.ROPE, from: IO.STREAM, version: CARDINAL] RETURNS[StructureNode] =
BEGIN -- note: column of rows atom has been read
slot: Slot ← NEW[SlotBody ← [NIL, NIL, document, displayer, vParent, pageName, slot, 0, 0]];
type: ATOM;
badToken: Rope.ROPE ← NIL;
slotSn: StructureNode ← NEW[StructureNodeBody ← [slot, SlotStructureNodeProcs]];
[slot.daSn, slot.da] ← CreateDisplayArray[slotSn];
StructureNodes.AddColumnAt[slot.da, 1];
StructureNodes.AddRowAt[slot.da, 1];
IF NOT Rope.Equal[GetTokenRope[from].token, "("] THEN BadFormatLoadFile;
type ← NARROW[GetRefAny[from]];
SELECT type FROM
$Slot =>
BEGIN
gcSn: StructureNode;
gcSn ← GCell.CreateGCell[document, displayer, slotSn, vParent, slot];
StructureNodes.SetElementAt[slot.da, 1, 1, gcSn, NIL];
  slot.case ← slot;
  END;
$GCellArray =>
BEGIN
  gcaSn: StructureNode ← GCell.LoadGCellArray[document, displayer, vParent, from, version];
StructureNodes.SetElementAt[slot.da, 1, 1, gcaSn, NIL];
  slot.case ← GCellArray;
  END;
$individualHCellArray =>
BEGIN
  gcaSn: StructureNode ← HCells.LoadHCellArray[document, displayer, vParent, pageName, NIL, from, version, individual];
StructureNodes.SetElementAt[slot.da, 1, 1, gcaSn, NIL];
  slot.case ← individualHCellArray;
  END;
  $itemHCellArray =>
BEGIN
  gcaSn: StructureNode;
  key: LONG CARDINAL ← GetCard[from];
  row: CARDINAL ← GetCard[from];
  gcaSn ← HCells.LoadHCellArray[document, displayer, vParent, pageName, NIL, from, version, individual]; -- will be converted to groupItem during connection
StructureNodes.SetElementAt[slot.da, 1, 1, gcaSn, NIL];
  slot.case ← itemHCellArray;
  slot.key ← key;
  slot.rowInSummaryArray ← row;
  END;
  $summaryHCellArray =>
BEGIN
  gcaSn: StructureNode;
  key: LONG CARDINAL ← GetCard[from];
  gcaSn ← HCells.LoadHCellArray[document, displayer, vParent, pageName, NIL, from, version, groupSummary];
StructureNodes.SetElementAt[slot.da, 1, 1, gcaSn, NIL];
  slot.case ← summaryHCellArray;
  slot.key ← key;
  END;
ENDCASE => BadFormatLoadFile;
IF NOT Rope.Equal[badToken ← GetTokenRope[from].token, ")"] THEN BadFormatLoadFile;
RETURN[slotSn]
END;

CreateSlot: PUBLIC PROCEDURE[document: NewCalcGlobal.Document, displayer: NewCalcGlobal.Displayer, vParent: Viewer, pageName: Rope.ROPE, rowCase: RowCase] RETURNS[StructureNode] =
BEGIN
slot: Slot ← NEW[SlotBody ← [NIL, NIL, document, displayer, vParent, pageName, slot, 0, 0]];
slotSn: StructureNode ← NEW[StructureNodeBody ← [slot, SlotStructureNodeProcs]];
[slot.daSn, slot.da] ← CreateDisplayArray[slotSn];
StructureNodes.AddColumnAt[slot.da, 1];
StructureNodes.AddRowAt[slot.da, 1];
SELECT rowCase FROM
 clientRow =>
  BEGIN
  slotGcSn: StructureNode;
  slotGcSn ← GCell.CreateGCell[document, displayer, slotSn, vParent, slot];
  StructureNodes.SetElementAt[slot.da, 1, 1, slotGcSn, NIL];
  slot.case ← slot;
  END;
 displayPageName =>
  BEGIN
  slotHcSn: StructureNode;
  hCell: HCells.HCell;
  [slotHcSn, hCell] ← HCells.CreateHCell[document, displayer, slotSn, vParent];
  StructureNodes.SetElementAt[slot.da, 1, 1, slotHcSn, NEW[PageIdSlotInfoBody ← [hCell]]];
  HCells.SetHCellParameters[hCell, NIL, NIL, NIL, NIL, NIL, FALSE, pageName, none, none, none, none, text, left, "", NIL, NIL];
  slot.case ← pageId;
  END;
 ENDCASE => ERROR;
RETURN[slotSn]
END;

ResetPageNameOfSlot: PROCEDURE[slotSn: StructureNode, name: Rope.ROPE] =
 BEGIN
slot: Slot ← NARROW[slotSn.ref];
slot.pageName ← name;
SELECT slot.case FROM
 pageId =>
  BEGIN
  info: PageIdSlotInfo ← NARROW[StructureNodes.GetElementAt[slot.da, 1, 1].info];
  HCells.SetHCellText[info.hCell, name];
  END;
 $individualHCellArray, $itemHCellArray, $summaryHCellArray =>
 BEGIN
 hcaSn: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn;
 HCells.ResetPageNameOfHCellArray[hcaSn, name];
 END;
 $slot, $GCellArray => NULL;
 ENDCASE => ERROR;
 END;

GetSlot: PROCEDURE[info: StructureNode, send: GCell.SetASlotProc, entry: BOOLEAN] =
 BEGIN
slot: Slot ← NARROW[info.ref];
slotGca: StructureNode ← GetElementAt[slot.da, 1, 1].sn;
 GAS: PROCEDURE =
 BEGIN

GetTheArray: PROCEDURE[sendIt: GCell.SetAGCellArrayProc] =
 {GCell.GetGCellArray[slotGca, sendIt, FALSE]};

send[slot.case, GetTheArray];
END;
IF entry THEN slot.document.procs.executeInMonitor[slot.document, GAS] ELSE GAS[];
 END;

SetSlot: PROCEDURE[info: StructureNode, case: GCell.SlotCase, getOneArray: PROCEDURE[GCell.SetAGCellArrayProc], entry: BOOLEAN] =
 BEGIN
slot: Slot ← NARROW[info.ref];
 SAS: PROCEDURE =
 BEGIN
sub: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn;

 sub.procs.Abandon[sub];
 StructureNodes.SetElementAt[slot.da, 1, 1, NIL, NIL];

 SELECT case FROM
  slot =>
   BEGIN
   slotGcSn: StructureNode ← GCell.CreateGCell[slot.document, slot.displayer, NIL, slot.vParent, slot];
  StructureNodes.SetElementAt[slot.da, 1, 1, slotGcSn, NIL];
  slot.case ← slot
   END;
  GCellArray =>
   BEGIN
   gcaSn: StructureNode;
   
   SetTheArray: GCell.SetAGCellArrayProc =
    {GCell.SetGCellArray[gcaSn, nColumns, nRows, GetOneCell, FALSE]};
    
   gcaSn ← GCell.CreateGCellArray[slot.document, slot.displayer, slot.vParent];
   StructureNodes.SetElementAt[slot.da, 1, 1, gcaSn, NIL];
   slot.case ← GCellArray;
   getOneArray[SetTheArray];
   END;
  individualHCellArray =>
   BEGIN
   hcaSn: StructureNode;
   
   hcaSn ← HCells.CreateHCellArray[slot.document, slot.displayer, slot.vParent, NIL, NIL];
   StructureNodes.SetElementAt[slot.da, 1, 1, hcaSn, NIL];
   slot.case ← individualHCellArray;
   END;
  ENDCASE => ERROR;
 END;
IF entry THEN slot.document.procs.executeInMonitor[slot.document, SAS] ELSE SAS[];
 END;

SlotCheckForOpenSlot: PROCEDURE[node: StructureNode] RETURNS[BOOLEAN] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 RETURN[slot.case = slot];
 END;

SlotInstallHCellArrayAtOpenSlot: PROCEDURE[sn: StructureNode, case: GCell.SlotCase, createIt: PROCEDURE[displayer: NewCalcGlobal.Displayer, vParent: ViewerClasses.Viewer, pageName: Rope.ROPE] RETURNS[StructureNode]] =
 BEGIN
slot: Slot ← NARROW[sn.ref];
hcaSn: StructureNode;
IF slot.case # slot THEN ERROR;
hcaSn ← createIt[slot.displayer, slot.vParent, slot.pageName];
StructureNodes.SetElementAt[slot.da, 1, 1, hcaSn, NIL];
slot.case ← case;
 END;

SlotInstallHCellArrayAtAnOpenSlot: PROCEDURE[sn: StructureNode, rowNumber, colNumber: CARDINAL, case: GCell.SlotCase, createIt: PROCEDURE[displayer: NewCalcGlobal.Displayer, vParent: ViewerClasses.Viewer, pageName: Rope.ROPE, rowNumber, colNumber: CARDINAL] RETURNS[StructureNode]] RETURNS[didIt: BOOLEAN] =
 BEGIN
slot: Slot ← NARROW[sn.ref];
hcaSn: StructureNode;
IF slot.case # slot THEN RETURN[FALSE];
hcaSn ← createIt[slot.displayer, slot.vParent, slot.pageName, rowNumber, colNumber];
StructureNodes.SetElementAt[slot.da, 1, 1, hcaSn, NIL];
slot.case ← case;
RETURN[TRUE];
 END;

SlotGetHCellArrayAtSlot: PROCEDURE[sn: StructureNode] RETURNS[StructureNode] =
 BEGIN
slot: Slot ← NARROW[sn.ref];
hcaSn: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn;
SELECT slot.case FROM
 itemHCellArray, summaryHCellArray => RETURN[hcaSn];
 slot => RETURN[NIL];
 ENDCASE => ERROR;
 END;

SlotConnectInstalledItemArrays: PROCEDURE[sn: StructureNode, rowNumber, colNumber: CARDINAL, connectOneItemArray: PROCEDURE[itemHcaSn: StructureNodes.StructureNode, row, col: CARDINAL]] =
 BEGIN
slot: Slot ← NARROW[sn.ref];
hcaSn: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn;
IF slot.case # itemHCellArray THEN RETURN;
connectOneItemArray[hcaSn, rowNumber, colNumber];
 END;

SlotGenerateSummaryArrays: PROCEDURE[sn: StructureNode, seeOneArray: PROCEDURE[summaryHcaSn: StructureNodes.StructureNode, summaryKey: LONG CARDINAL] RETURNS[continue: BOOLEAN]] RETURNS[continue: BOOLEAN] =
 BEGIN
slot: Slot ← NARROW[sn.ref];
hcaSn: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn;
IF slot.case # summaryHCellArray THEN RETURN[TRUE];
RETURN[seeOneArray[hcaSn, slot.key]];
 END;

SlotConnectLoadedItemArrays: PROCEDURE[sn: StructureNode, connectOneItemArray: PROCEDURE[itemHcaSn: StructureNodes.StructureNode, itemKey: LONG CARDINAL, atRow: CARDINAL]] =
 BEGIN
slot: Slot ← NARROW[sn.ref];
hcaSn: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn;
IF slot.case # itemHCellArray THEN RETURN;
connectOneItemArray[hcaSn, slot.key, slot.rowInSummaryArray];
 END;

SlotCheckForDirtyData: PROCEDURE[node: StructureNode] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 slot.daSn.procs.CheckForDirtyData[slot.daSn];
 END;

SlotPreShow: PROCEDURE[node: StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 [lead, height, space, L, A, B, R, W] ← slot.daSn.procs.PreShow[slot.daSn];
 END;

SlotShowLine: PROCEDURE[node: StructureNode, L, R, W: INTEGER, lineX: INTEGER, to: STREAM] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 slot.daSn.procs.ShowLine[slot.daSn, L, R, W, lineX, to];
 END;


SlotPrePrePaint: PROCEDURE[node: StructureNode] RETURNS[lead, height: INTEGER, space, L, A, B, R, W: INTEGER] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 [lead, height, space, L, A, B, R, W] ← slot.daSn.procs.PrePrePaint[slot.daSn];
 END;


SlotPrePaint: PROCEDURE[node: StructureNode, y, x, L, R, W: INTEGER, pass: PrePaintPass] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 slot.daSn.procs.PrePaint[slot.daSn, y, x, L, R, W, pass];
 END;


SlotPaint: PROCEDURE[node: StructureNode, dirtyOnly: BOOLEAN] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 slot.daSn.procs.Paint[slot.daSn, dirtyOnly];
 END;


SlotUnPaint: PROCEDURE[node: StructureNode] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 slot.daSn.procs.UnPaint[slot.daSn];
 END;


SlotNoteEnclosingCell: PROCEDURE[node: StructureNode, dac: DACell] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 slot.daSn.procs.NoteEnclosingCell[slot.daSn, dac];
 END;


SlotNoteCoordinates: PROCEDURE[node: StructureNode, da: DisplayArray, I, J: CARDINAL] =
 BEGIN
 slot: Slot ← NARROW[node.ref];
 slot.daSn.procs.NoteCoordinates[slot.daSn, da, I, J];
 END;

SaveSlot: PROCEDURE[info: StructureNode, to: IO.STREAM] =
BEGIN
slot: Slot ← NARROW[info.ref];
sub: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn; -- the sub structure
Put[to, char['(]];
SELECT slot.case FROM
slot => Put[to, char[' ], atom[$Slot]];
GCellArray =>
BEGIN
  Put[to, char[' ], atom[$GCellArray]];
  sub.procs.Save[sub, to];
  END;
individualHCellArray =>
BEGIN
  Put[to, char[' ], atom[$individualHCellArray]];
  sub.procs.Save[sub, to];
  END;
itemHCellArray =>
BEGIN
summaryArray: StructureNode;
row: CARDINAL;
[summaryArray, row] ← HCells.GetSummaryArray[sub];
  Put[to, char[' ], atom[$itemHCellArray], char[' ]];
  Put[to, card[LOOPHOLE[summaryArray]], char[' ]];
  Put[to, card[row], char[' ]];
  sub.procs.Save[sub, to];
  END;
summaryHCellArray =>
BEGIN
  Put[to, char[' ], atom[$summaryHCellArray], char[' ]];
  Put[to, card[LOOPHOLE[sub]], char[' ]];
  sub.procs.Save[sub, to];
  END;
ENDCASE => ERROR;
Put[to, char[')]];
END;


SubstituteInSlot: PROCEDURE[info: StructureNode, newText, oldText: Rope.ROPE] =
 BEGIN
 slot: Slot ← NARROW[info.ref];
sub: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn; -- the sub structure
SELECT slot.case FROM
slot => NULL;
GCellArray => sub.procs.Substitute[sub, newText, oldText];
ENDCASE => ERROR;
 END;

AbandonSlot: PROCEDURE[info: StructureNode] =
BEGIN
slot: Slot ← NARROW[info.ref];
StructureNodes.DeleteColumnAt[slot.da, 1];
StructureNodes.DeleteRowAt[slot.da, 1];
END;

ActSlot: PROCEDURE[info: StructureNode, I, J: CARDINAL, action: StructureNodes.Action, p1, p2: REF ANY] RETURNS[StructureNode, CARDINAL, CARDINAL] =
BEGIN
slot: Slot ← NARROW[info.ref];

SELECT action FROM


CreateGCellArray =>
IF slot.case = slot THEN
  BEGIN
  gcaSn: StructureNode ← GCell.CreateGCellArray[slot.document, slot.displayer, slot.vParent];
  StructureNodes.SetElementAt[slot.da, 1, 1, gcaSn, NIL];
  slot.case ← GCellArray;
  END;

  CreateHCellArray =>
IF slot.case = slot THEN
  BEGIN
  name: Rope.ROPE ← NARROW[p1];
  hcaSn: StructureNode ← HCells.CreateHCellArray[slot.document, slot.displayer, slot.vParent, slot.pageName, name];
  StructureNodes.SetElementAt[slot.da, 1, 1, hcaSn, NIL];
  slot.case ← individualHCellArray;
  END;

ENDCASE =>
 BEGIN
 cSn: StructureNode; cI, cJ: CARDINAL;
 [cSn, cI, cJ] ← GetContextDA[slot.da];
 RETURN[cSn, cI, cJ];
 END;

RETURN[NIL, 0, 0];
END;

VerifySlot: PROCEDURE[info: StructureNode] =
BEGIN
slot: Slot ← NARROW[info.ref];
sub: StructureNode ← StructureNodes.GetElementAt[slot.da, 1, 1].sn; -- the sub structure;
 SELECT slot.case FROM
slot => NULL;
GCellArray, individualHCellArray, itemHCellArray, summaryHCellArray => sub.procs.Verify[sub];
ENDCASE => ERROR;
END;



-- debugging routines



END..


-- 6-Mar-82 16:25:00: Sturgis, started ColumnOfRows.mesa
-- 6-Mar-82 17:46:45: initial edit completed.
-- 6-Mar-82 18:05:32: forgot to add the single row to the info matrix of a row of slots.
-- RTE: 6-Mar-82 18:20:27: forgot to replace an ERROR with a call on the appropriate routine that had been written after the ERROR installed.
-- change: 7-Mar-82 13:47:44: begin to add the new verions of save and load
-- remark: T: seemed adviasable to add a hole new layer of matrix to hold the slot???
-- RTE: 7-Mar-82 16:50:23: forgot to set the matirx element for the slot text cell.
-- remark: 7-Mar-82 16:54:31: basic structure still seems to work, i.e. can append slots, rows, and columns. However, menu is still too big, so can't test other actions. did not try any delete actions.
RTE: 7-Mar-82 18:00:31: could not append a slot to a simplematrix. modifed slot act to check for case only after seeing that it is a simplematrix create action. Thus, false will perculate up on appendslot.
-- change: 8-Mar-82 14:35:20: modify load and save to follow new conventions.
-- RTE: 8-Mar-82 16:54:07: remove row at column of rows was calling removal of last row an error.
-- RTE: 9-Mar-82 14:14:42: LoadSLot forgot to install the element in the MatrixNode.
-- change: 11-Mar-82 14:13:34: add knowledge of additivematrices to slot code.
-- July 31, 1982 2:12 pm: convert to cedar 3.2
-- August 1, 1982 5:30 pm: add AssignmentCell as a slot case, and necessary code
-- August 4, 1982 10:28 am: add version to load procedure and subsequent calls. remove assignment cell from top level structures.

-- August 7, 1982 5:10 pm: convert to NCGlobal
-- change: September 17, 1982 4:20 pm: add left justification
-- October 1, 1982 11:20 am: add code to splice in GCellArrays
change: October 3, 1982 3:10 pm: trouble during load, loading a slot does not see final ")". So, add code to save the actual token it sees to try to run down the problem.
-- October 8, 1982 11:01 am: begin to convert whole thing to use DisplayArrays, GCells, and only build GCellArrays.
-- BTE: October 8, 1982 1:44 pm: rename the module to be NewColumnOfRows, to match its new file name.
-- October 10, 1982 1:46 pm: rename as columnofrows.
-- October 10, 1982 2:07 pm: get action and badformatloadfile from structurenodes, rather than structurenode.
-- October 11, 1982 3:14 pm: CreateGGCellArray and LoadGCellArray now come from GCell.
-- October 14, 1982 3:58 pm: displayer now passed as an argument through crearte and load. Create and Load now exported through GCell.
-- T: add 2 ref any arguments to ActColumnOfRows, ActRowOfSlots, and ActSlot
-- November 3, 1982 3:29 pm: add Substitute to logical node procs.
-- November 3, 1982 5:33 pm: add Get Set, Column, Row, Slot.
-- RTE: February 11, 1983 2:21 pm: conversion to 4.0, change calls on GetAtom to NARROW[GetRefAny[]].
-- February 25, 1983 3:27 pm: replace displayNode and Logical node with StructureNode
-- February 27, 1983 1:00 pm: completed the change. Now only one reference to columns, rows, or slots; and that is a structure node. display actions on the structure node result in calls on the display routines of the associated display array structure node. Total time spent on this fix up about 2 hours. Note: still one back pointer from the associated display arrays.
-- February 27, 1983 3:27 pm: owing to discovery of the need for GCellarray to store two items in each displayArray crosspoint, have to change all calls on Get and SetElement from this module.
-- February 28, 1983 2:15 pm: add dummy note coordinates procedure.
-- RTE: March 1, 1983 9:48 am: slots must give their slotSn to the GCell holding the text of "slot", otherwise actions can not occur.
-- March 8, 1983 11:34 am: add UnPaint calls.
-- RTE: March 8, 1983 12:58 pm: nobody propogated NoteCoordinates to parallel display array.
-- Change: June 28, 1984 3:11:27 pm PDT: add HCellArrays
-- Change: July 3, 1984 9:39:23 am PDT: add code to save and load HCell arrays.
-- Change: July 10, 1984 11:06:47 am PDT: add code to handle page ids.
-- July 19, 1984 5:56:53 pm PDT: begin to install code to handle array groups.