-- GCellToolImpl.mesa
-- last edit     July 10, 1984 9:42:03 pm PDT

DIRECTORY
   Commander USING[CommandProc, Register],
	EasyTool USING[DefineItemsProc, DefineTool, ItemHandle],
   Expressions USING[ParseExpression, ParseIdentifier, SyntaxError],
   GCell USING[GCellItemDescriptor, GCellItemJustification, GCellItemMode, GCellItemRowColSumMode, GCellItemValueMode, GetGCellContents, GetItemSelection, SetGCellContents],
   MessageWindow USING[Append, Blink],
   NewCalcGlobal USING[Document],
   Rope USING[Equal, ROPE],
   StructureNodes USING[StructureNode];
   
GCellToolImpl: MONITOR IMPORTS Commander, GCell, EasyTool, Expressions, MessageWindow, Rope EXPORTS GCell =

BEGIN

ToolInstance: TYPE = REF ToolBody;
ToolBody: TYPE = RECORD [
	document: NewCalcGlobal.Document,
	itemDescriptors: ItemSequence,
	nActiveItems: REF CARDINAL
	];
	
ItemSequence: TYPE = REF ItemSequenceBody;
ItemSequenceBody: TYPE = RECORD[SEQUENCE maxNItems: CARDINAL OF ItemDisplay];

ItemDisplay: TYPE = REF ItemDisplayBody;
ItemDisplayBody: TYPE = RECORD[
	tool: ToolInstance,
	j: CARDINAL,
	active: BOOLEAN,
	mode:  GCell.GCellItemMode, modeText: REF Rope.ROPE,
	justification: GCell.GCellItemJustification, justificationText: REF Rope.ROPE,
	rowSumMode: GCell.GCellItemRowColSumMode, rowSumModeText: REF Rope.ROPE,
	colSumMode: GCell.GCellItemRowColSumMode, colSumModeText: REF Rope.ROPE,
	valueMode: GCell.GCellItemValueMode, valueModeText: REF Rope.ROPE,
	idText: REF Rope.ROPE,
	textText: REF Rope.ROPE,
	zeroValTextText: REF Rope.ROPE
	];
	
	
ModeTextSize: Rope.ROPE ← "showValue";	
JustificationTextSize: Rope.ROPE ← "rightDecimal";	
rowSumModeTextSize: Rope.ROPE ← "noEffect";
colSumModeTextSize: Rope.ROPE ← "noEffect";
valueModeTextSize: Rope.ROPE ← "expression";
IdTextSize: Rope.ROPE ← "lots of text, ver long rope etc         more space";	
TextTextSize: Rope.ROPE ← "lots of text, ver long rope etc         more space";	
ZVTextSize: Rope.ROPE ← "lots of text, ver long rope etc         more space";

-- build an instance of the tool

MakeAGCellToolInstance: PUBLIC ENTRY PROCEDURE[document: NewCalcGlobal.Document] =
	BEGIN
	NItems: CARDINAL = 20;
	sequence: ItemSequence ← NEW[ItemSequenceBody[NItems]];
	instance: ToolInstance ← NEW[ToolBody ← [document, sequence, NEW[CARDINAL ←0]]];
	DefineDisplayItems: EasyTool.DefineItemsProc =
		BEGIN
		colDescList: LIST OF EasyTool.ItemHandle ← NIL;
		lastOnColDescList: LIST OF EasyTool.ItemHandle;
		
		AppendToColumnList: PROCEDURE[item: EasyTool.ItemHandle] =
			BEGIN
			new: LIST OF EasyTool.ItemHandle ← LIST[item];
			IF colDescList = NIL THEN colDescList ← lastOnColDescList ← new
			  ELSE {lastOnColDescList.rest ← new; lastOnColDescList ← new};
			END;
			
		AppendToColumnList[defineRow[LIST[
				defineButton["Get a cell", GetACell],
				defineFiller[5, 5],
				defineButton["Set a cell", SetACell],
				defineFiller[5, 5],
				defineItem[ , "nItems", , instance.nActiveItems]]]];
		
		AppendToColumnList[defineFiller[1, 10]];
			
		FOR J: CARDINAL IN [0..NItems) DO
			AppendToColumnList[defineFiller[1, 10]];
			AppendToColumnList[defineRow[LIST[
					defineButton["I", InsertItem, sequence[J]],
					defineButton["D", DeleteItem, sequence[J]],
					defineFiller[15, 1],
					defineButton["M", StepItemMode, sequence[J]],
					defineItem[ , "m", , sequence[J].modeText, ModeTextSize, TRUE],
					defineFiller[15, 1],
					defineButton["J", StepItemJustification, sequence[J]],
					defineItem[ , "j", , sequence[J].justificationText, JustificationTextSize, TRUE],
					defineButton["RSM", StepItemRowSumMode, sequence[J]],
					defineItem[ , "rsm", , sequence[J].rowSumModeText, rowSumModeTextSize, TRUE],
					defineButton["CSM", StepItemColSumMode, sequence[J]],
					defineItem[ , "csm", , sequence[J].colSumModeText, colSumModeTextSize, TRUE],
					defineButton["VM", StepItemValueMode, sequence[J]],
					defineItem[ , "vm", , sequence[J].valueModeText, valueModeTextSize, TRUE]]]];
			AppendToColumnList[defineRow[LIST[
					defineFiller[15, 1],
					defineItem[ , "id", , sequence[J].idText, IdTextSize, TRUE]]]];
			AppendToColumnList[defineRow[LIST[
					defineFiller[15, 1],
					defineItem[ , "exp", , sequence[J].textText, TextTextSize, TRUE]]]];
			AppendToColumnList[defineRow[LIST[
					defineFiller[15, 1],
					defineItem[ , "zvt", , sequence[J].zeroValTextText, ZVTextSize, TRUE]]]];
			ENDLOOP;
			
		
		
		RETURN[defineColumn[colDescList]];			
		END;
		
	FOR J: CARDINAL IN [0..NItems) DO
		sequence[J] ← NEW[ItemDisplayBody ← [instance, J, FALSE, showText, NEW[Rope.ROPE ← " "], left, NEW[Rope.ROPE ← " "],  addVal, NEW[Rope.ROPE ← " "],  addVal, NEW[Rope.ROPE ← " "],  expression, NEW[Rope.ROPE ← " "],  NEW[Rope.ROPE ← " "], NEW[Rope.ROPE ← " "], NEW[Rope.ROPE ← " "]]];
		ENDLOOP;
	[] ← EasyTool.DefineTool["Cell Tool", instance, DefineDisplayItems];
	END;
	
	
	
	
-- global actions


GetACell: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	tool: ToolInstance ← NARROW[data];
	gcSn: StructureNodes.StructureNode;
	ReceiveTheItems: PROCEDURE[nItems: CARDINAL, getOneItem: PROCEDURE RETURNS[GCell.GCellItemDescriptor]] =
		BEGIN
		FOR J: CARDINAL IN [0..nItems) DO
			desc: GCell.GCellItemDescriptor← getOneItem[];
			tool.itemDescriptors[J].active ← TRUE;
			tool.itemDescriptors[J].mode ← desc.mode;
			ShowItemMode[tool.itemDescriptors[J]];
			tool.itemDescriptors[J].justification ← desc.justification;
			ShowItemJustification[tool.itemDescriptors[J]];
			tool.itemDescriptors[J].rowSumMode ← desc.rowSumMode;
			ShowItemRowSumMode[tool.itemDescriptors[J]];
			tool.itemDescriptors[J].colSumMode ← desc.colSumMode;
			ShowItemColSumMode[tool.itemDescriptors[J]];
			tool.itemDescriptors[J].valueMode ← desc.valueMode;
			ShowItemValueMode[tool.itemDescriptors[J]];
			tool.itemDescriptors[J].idText↑ ← desc.id;
			tool.itemDescriptors[J].textText↑ ← desc.text;
			tool.itemDescriptors[J].zeroValTextText↑ ← desc.zeroValText;
			tool.nActiveItems↑ ← nItems;
			ENDLOOP;
		END;
		
	FOR J: CARDINAL IN [0..tool.itemDescriptors.maxNItems) DO
		NilAnItem[tool.itemDescriptors[J]]
		ENDLOOP;
	tool.nActiveItems↑ ← 0;
	
	[ , gcSn, ] ← GCell.GetItemSelection[]; -- really a call on NewCalcImpl
	IF gcSn = NIL THEN RETURN;
	
	GCell.GetGCellContents[gcSn, ReceiveTheItems, TRUE];
	
	END;

SetACell: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	tool: ToolInstance ← NARROW[data];
	gcSn: StructureNodes.StructureNode;
	j: CARDINAL ← 0;
	SendOneItem: PROCEDURE RETURNS[GCell.GCellItemDescriptor] =
		BEGIN
		desc: GCell.GCellItemDescriptor ← [
			mode: tool.itemDescriptors[j].mode,
			justification: tool.itemDescriptors[j].justification,
			rowSumMode: tool.itemDescriptors[j].rowSumMode,
			colSumMode: tool.itemDescriptors[j].colSumMode,
			valueMode: tool.itemDescriptors[j].valueMode,
			id: tool.itemDescriptors[j].idText↑,
			text: tool.itemDescriptors[j].textText↑,
			zeroValText:tool.itemDescriptors[j].zeroValTextText↑
			];
		j ← j + 1;
		RETURN[desc];
		END;
		
	BEGIN -- allow for abort exit
		
	FOR J: CARDINAL IN [0..tool.nActiveItems↑) DO -- make sure there will be no "hidden" parse errors
		SELECT tool.itemDescriptors[J].mode FROM
			showText => NULL;
			showId, showExp, showValue, noShow =>
				BEGIN
				IF tool.itemDescriptors[J].mode # showId  
				 AND NOT tool.itemDescriptors[J].idText↑ = NIL
				 AND NOT Rope.Equal[tool.itemDescriptors[J].idText↑, ""] 
				 AND NOT Rope.Equal[tool.itemDescriptors[J].idText↑, " "] 
				 THEN [] ← Expressions.ParseIdentifier[tool.itemDescriptors[J].idText↑
					! Expressions.SyntaxError =>
						BEGIN
						MessageWindow.Append[
							message: "syntax error in identifier, text is ", clearFirst: TRUE];
						MessageWindow.Append[
							message: tool.itemDescriptors[J].idText↑, clearFirst: FALSE];
						MessageWindow.Blink[];
						GOTO abort;
						END];
				[] ← Expressions.ParseExpression[tool.itemDescriptors[J].textText↑
					! Expressions.SyntaxError =>
						BEGIN
						MessageWindow.Append[
							message: "syntax error in expression, text is ", clearFirst: TRUE];
						MessageWindow.Append[
							message: tool.itemDescriptors[J].textText↑, clearFirst: FALSE];
						MessageWindow.Blink[];
						GOTO abort;
						END];
				END;
			ENDCASE => ERROR;
		ENDLOOP;

	[ , gcSn, ] ← GCell.GetItemSelection[]; -- really a call on NewCalcImpl
	IF gcSn = NIL THEN RETURN;
	
	j ← 0;
	GCell.SetGCellContents[gcSn, tool.nActiveItems↑, SendOneItem, TRUE];
	
	EXITS
	  abort => NULL;
	  END
	
	END;

-- item button procs 



InsertItem: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	item: ItemDisplay ← NARROW[data];
	itemJ: CARDINAL ← item.j;
	IF item.j > 0 AND NOT item.tool.itemDescriptors[item.j-1].active THEN RETURN;
	IF item.tool.nActiveItems↑ = item.tool.itemDescriptors.maxNItems THEN RETURN;
	FOR J: CARDINAL DECREASING IN [itemJ..item.tool.nActiveItems↑] DO
		item.tool.itemDescriptors[J+1].mode ← item.tool.itemDescriptors[J].mode;
		item.tool.itemDescriptors[J+1].modeText↑ ← item.tool.itemDescriptors[J].modeText↑;
		item.tool.itemDescriptors[J+1].justification ← item.tool.itemDescriptors[J].justification;
		item.tool.itemDescriptors[J+1].justificationText↑ ← item.tool.itemDescriptors[J].justificationText↑;
		item.tool.itemDescriptors[J+1].idText↑ ← item.tool.itemDescriptors[J].idText↑;
		item.tool.itemDescriptors[J+1].textText↑ ← item.tool.itemDescriptors[J].textText↑;
		item.tool.itemDescriptors[J+1].zeroValTextText↑ ← item.tool.itemDescriptors[J].zeroValTextText↑;
		ENDLOOP;
	item.tool.nActiveItems↑ ← item.tool.nActiveItems↑ + 1;
	item.active ← TRUE;
	item.mode  ← showText;
	item.justification ← right;
	item.idText↑ ← "                 ";
	item.textText↑ ← "                 ";
	item.zeroValTextText↑ ← "                 ";
	ShowItemMode[item];
	ShowItemJustification[item];
	END;

DeleteItem: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	item: ItemDisplay ← NARROW[data];
	itemJ: CARDINAL ← item.j;
	IF NOT item.active THEN RETURN;
	FOR J: CARDINAL IN [itemJ..item.tool.nActiveItems↑) DO
		item.tool.itemDescriptors[J].mode ← item.tool.itemDescriptors[J+1].mode;
		item.tool.itemDescriptors[J].modeText↑ ← item.tool.itemDescriptors[J+1].modeText↑;
		item.tool.itemDescriptors[J].justification ← item.tool.itemDescriptors[J+1].justification;
		item.tool.itemDescriptors[J].justificationText↑ ← item.tool.itemDescriptors[J+1].justificationText↑;
		item.tool.itemDescriptors[J].idText↑ ← item.tool.itemDescriptors[J+1].idText↑;
		item.tool.itemDescriptors[J].textText↑ ← item.tool.itemDescriptors[J+1].textText↑;
		item.tool.itemDescriptors[J].zeroValTextText↑ ← item.tool.itemDescriptors[J+1].zeroValTextText↑;
		ENDLOOP;
	NilAnItem[item.tool.itemDescriptors[item.tool.nActiveItems↑]];
	item.tool.nActiveItems↑ ← item.tool.nActiveItems↑-1; 
	END;

StepItemMode: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	item: ItemDisplay ← NARROW[data];
	IF NOT item.active THEN RETURN;
	item.mode ← SELECT item.mode FROM
		showText => showId,
		showId => showExp,
		showExp => showValue,
		showValue => noShow,
		noShow => showText,
		ENDCASE => ERROR;
	ShowItemMode[item];
	END;

ShowItemMode: PROCEDURE[item: ItemDisplay] =
	BEGIN
	IF NOT item.active THEN RETURN;
	item.modeText↑ ← SELECT item.mode FROM
		showText => "showText",
		showId => "showId",
		showExp => "showExp",
		showValue => "showValue",
		noShow => "noShow",
		ENDCASE => ERROR;
	END;

StepItemJustification: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	item: ItemDisplay ← NARROW[data];
	IF NOT item.active THEN RETURN;
	item.justification ← SELECT item.justification FROM
		left => leftDecimal,
		leftDecimal => centered,
		centered => rightDecimal,
		rightDecimal => right,
		right => left,
		ENDCASE => ERROR;
	ShowItemJustification[item];
	END;

ShowItemJustification: PROCEDURE[item: ItemDisplay] =
	BEGIN
	IF NOT item.active THEN RETURN;
	item.justificationText↑ ← SELECT item.justification FROM
		left => "left",
		leftDecimal => "leftDecimal",
		centered => "centered",
		rightDecimal => "rightDecimal",
		right => "right",
		ENDCASE => ERROR;
	END;
	
StepItemRowSumMode: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	item: ItemDisplay ← NARROW[data];
	IF NOT item.active THEN RETURN;
	item.rowSumMode ← SELECT item.rowSumMode FROM
			addVal => subVal,
			subVal => setZero,
			setZero => noEffect,
			noEffect => addVal,
			ENDCASE => ERROR;
	ShowItemRowSumMode[item];
	END;

ShowItemRowSumMode: PROCEDURE[item: ItemDisplay] =
	BEGIN
	IF NOT item.active THEN RETURN;
	item.rowSumModeText↑ ← SELECT item.rowSumMode FROM
		addVal => "addVal",
		subVal => "subVal",
		setZero => "setZero",
		noEffect => "noEffect",
		ENDCASE => ERROR;
	END;
	
StepItemColSumMode: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	item: ItemDisplay ← NARROW[data];
	IF NOT item.active THEN RETURN;
	item.colSumMode ← SELECT item.colSumMode FROM
			addVal => subVal,
			subVal => setZero,
			setZero => noEffect,
			noEffect => addVal,
			ENDCASE => ERROR;
	ShowItemColSumMode[item];
	END;

ShowItemColSumMode: PROCEDURE[item: ItemDisplay] =
	BEGIN
	IF NOT item.active THEN RETURN;
	item.colSumModeText↑ ← SELECT item.colSumMode FROM
		addVal => "addVal",
		subVal => "subVal",
		setZero => "setZero",
		noEffect => "noEffect",
		ENDCASE => ERROR;
	END;

StepItemValueMode: ENTRY PROCEDURE[data: REF ANY] =
	BEGIN
	item: ItemDisplay ← NARROW[data];
	IF NOT item.active THEN RETURN;
	item.valueMode ← SELECT item.valueMode FROM
			rowSum => colSum,
			colSum => expression,
			expression => rowSum,
			ENDCASE => ERROR;
	ShowItemValueMode[item];
	END;

ShowItemValueMode: PROCEDURE[item: ItemDisplay] =
	BEGIN
	IF NOT item.active THEN RETURN;
	item.valueModeText↑ ← SELECT item.valueMode FROM
		rowSum => "rowSum",
		colSum => "colSum",
		expression => "expression",
		ENDCASE => ERROR;
	END;



NilAnItem: PROCEDURE[item: ItemDisplay] =
	BEGIN
	item.active ← FALSE;
	item.modeText↑ ← " ";
	item.justificationText↑ ← " ";
	item.idText↑ ← " ";
	item.textText↑ ← " ";
	item.zeroValTextText↑ ← " ";
	END;
	
	
-- main code

TestGCellTool: Commander.CommandProc = TRUSTED
	{MakeAGCellToolInstance[NIL]};
	
Commander.Register[key: "TestGCellTool", proc: TestGCellTool, doc: ""];

END..

-- October 5, 1982 5:35 pm: Sturgis, started GCellToolImpl.mesa

-- RTE: October 6, 1982 4:08 pm: used same nil rope ref for all rope refs.
-- RTE: October 6, 1982 4:29 pm: display had bad shape, not enough room for needed text, so changed form.
-- RTE: October 6, 1982 4:34 pm: now there is not text to edit in the text fileds, so replace initial NIL rope with non null rope.
remark: October 6, 1982 4:39 pm: well, it displays, and one can change the entries, but BOY, is it not esthetic.  Also it has some techinical problems in the way it deals with commands delivered beyond the active area.  BUT, I will live with it.
-- RTEs (3): October 7, 1982 2:47 pm: main commands appeared in each item, sending a cell sent only the first item, and nilanitem always nilled the item following the last active item, rather than the parameter item.
-- RTE: October 8, 1982 3:23 pm: add code to catch a syntax error before text is sent to a cell.  A syntax error in the cell for an an item which is not displayed leads to a mesa ERROR.
-- RTE: October 8, 1982 4:45 pm: do not check for syntax error on showText mode items.
-- change: October 11, 1982 3:18 pm: SetAGCellProc changed slightly.
-- change: October 14, 1982 5:15 pm: GetSelection had changed slightly.
-- change: October 16, 1982 3:42 pm: make into a monitor, set "entry" TRUE on calls to Get and Set GCellContents.
-- RTE: October 21, 1982 2:45 pm: too many procs set as entry.
-- RTE: October 21, 1982 2:53 pm: in getacell and setacell, test for tool.global = NIL is unjustified.
-- October 22, 1982 1:42 pm: add rowSumMode, colSumMode, valueMode.
-- RTE: October 23, 1982 12:18 pm: did not correctly label noew mode fields.
-- RTE: October 23, 1982 3:43 pm: it is now ok to permit some identifiers which are NIL, or length 0, or a single blank.
-- change: November 14, 1982 1:59 pm: convert to new selection logic.
-- change: November 14, 1982 2:52 pm: add setZero to row and col sum modes.
-- February 27, 1983 3:57 pm: replace display node, logical node etc with structurenode.  In particular, GCell.GCell is replaced with StructureNode.  Took about 5 minutes.
-- change: June 22, 1984 4:28:33 pm PDT: convert to 5.2