-- EasyToolImpl.mesa
-- last edit June 22, 1984 9:27:11 am PDT Sturgis
-- Swinehart, July 19, 1985 10:15:29 am PDT

DIRECTORY
Basics USING[LongNumber],
 Buttons USING[Button, ButtonProc, Create, ReLabel],
Containers USING[Create],
Convert USING[CardFromRope, IntFromRope, RopeFromCard, RopeFromInt],
EasyTool USING[DefineItemsProc],
Labels USING[Create],
Rope USING[Cat, ROPE],
VFonts USING[FontHeight, StringWidth],
ViewerClasses USING[Viewer],
ViewerOps USING[DestroyViewer],
ViewerTools USING[GetContents, MakeNewTextViewer, SetContents];


EasyToolImpl: PROGRAM IMPORTS Buttons, Containers, Convert, Labels, Rope, VFonts, ViewerOps, ViewerTools EXPORTS EasyTool =

BEGIN OPEN EasyTool, ViewerClasses;

-- public types

ToolHandle: TYPE = REF ToolBody;
ToolBody: PUBLIC TYPE = RECORD[
 container: ViewerClasses.Viewer,
 currentY: INTEGER,
 nextX: INTEGER,
 nextY: INTEGER,
 items: REF ANY
 ];

ItemHandle: TYPE = REF ItemBody;
ItemBody: PUBLIC TYPE = RECORD[
 w1, w2, w3: CARDINAL, -- min field widths
 height: CARDINAL,
 data: REF ANY,
 next: ItemHandle -- next is used in row chain in matrices
 ];


-- Public Procedures

DefineTool: PUBLIC PROCEDURE[toolName: Rope.ROPE, toolData: REF ANY, obtainItems: DefineItemsProc] RETURNS[ToolHandle] =
 BEGIN
 tool: ToolHandle ← NEW[ToolBody ← [container: Containers.Create[[name: toolName]], currentY: 10, nextX: 0, nextY: 10 + VFonts.FontHeight[] + 2]];

 defineButton: PROCEDURE[buttonName: Rope.ROPE, buttonProc: PROCEDURE[REF ANY], data: REF ANY] RETURNS[ItemHandle] =
  {RETURN[AddButtonItemToTool[tool, buttonName, NIL, buttonProc, IF data # NIL THEN data ELSE toolData, TRUE]]};
  
 defineItem: PROCEDURE[
    prefix, label, suffix: Rope.ROPE ← NIL,
    ref: REF ANY, forSize: Rope.ROPE ← NIL,
    editable: BOOLEAN ← FALSE]
    RETURNS[ItemHandle] =
  {RETURN[AddLabeledItemToTool[tool, Rope.Cat[prefix, label, suffix], label, ref, forSize, editable]]};

 defineFiller: PROCEDURE[width, height: CARDINAL] RETURNS[ItemHandle]=
  {RETURN[AddFillerItemToTool[tool, width, height]]};

 defineMatrix: PROCEDURE[items: LIST OF LIST OF ItemHandle,
     rowLead, colSpacing: INTEGER] RETURNS[ItemHandle] =
  {RETURN[AddMatrixToTool[items, rowLead, colSpacing]]};

 defineRow: PROCEDURE[items: LIST OF ItemHandle, spacing: INTEGER] RETURNS[ItemHandle] =
  {RETURN[AddRowToTool[items, spacing]]};

 defineColumn: PROCEDURE[items: LIST OF ItemHandle, lead: INTEGER] RETURNS[ItemHandle] =
  {RETURN[AddColumnToTool[items, lead]]};
  
 context: ItemHandle ← obtainItems[defineButton, defineItem, defineFiller, defineMatrix, defineRow, defineColumn];
 PositionItem[context, 0, context.w1, context.w1, context.w2, context.w1+context.w2, context.w3, 0];
 RETURN[tool];
 END;

UpdateScreen: PUBLIC PROCEDURE[tool: ToolHandle] =
 BEGIN
 item: REF ANY ← tool.items;
 WHILE item # NIL DO
  SELECT TRUE FROM
   ISTYPE[item, TextItemHandle] => item ← UpdateScreenText[NARROW[item]];
   ISTYPE[item, UnsignedItemHandle] => item ← UpdateScreenUnsigned[NARROW[item]];
   ISTYPE[item, CardinalItemHandle] => item ← UpdateScreenCardinal[NARROW[item]];
   ISTYPE[item, SignedItemHandle] => item ← UpdateScreenSigned[NARROW[item]];
   ISTYPE[item, BooleanItemHandle] => item ← UpdateScreenBoolean[NARROW[item]];
   ENDCASE => ERROR;
  ENDLOOP;
 END;



-- Public Errors

UnimplimentedType: PUBLIC ERROR = CODE;



-- local types


ButtonItemHandle: TYPE = REF ButtonItemBody;
ButtonItemBody: TYPE = RECORD[
 tool: ToolHandle,
 proc: PROCEDURE[REF ANY],
 forSize: Rope.ROPE,
 name: Rope.ROPE,
 border: BOOLEAN,
 button: Buttons.Button,
 data: REF ANY
 ];

LabelItemHandle: TYPE = REF LabelItemBody;
LabelItemBody: TYPE = RECORD[
 tool: ToolHandle,
 name: Rope.ROPE,
 w, h: INTEGER,
 viewer: Viewer
 ];

SubTextItemHandle: TYPE = REF SubTextItemBody;
SubTextItemBody: TYPE = RECORD[
 parent: ViewerClasses.Viewer,
 v: Viewer,
mode: SubTextItemMode,
editable: BOOLEAN,
textRef: REF Rope.ROPE,
contents: Rope.ROPE,
x, y, w, h: INTEGER,
correspondingItem: REF ANY
 ];
SubTextItemMode: TYPE = {nil, button, textBox};

FillerItemHandle: TYPE = REF FillerItemBody;
FillerItemBody: TYPE = RECORD[
 justToBeNonEmpty: CARDINAL
 ];

TextItemHandle: TYPE = REF TextItemBody;
TextItemBody: TYPE = RECORD[
 forValSize: Rope.ROPE,
 label: LabelItemHandle,
 ref: REF Rope.ROPE,
 val: Rope.ROPE,
 text: REF Rope.ROPE,
 subTextItem: SubTextItemHandle,
 nextItem: REF ANY -- used in chain for update screen and value
 ];

UnsignedItemHandle: TYPE = REF UnsignedItemBody;
UnsignedItemBody: TYPE = RECORD[
 forValSize: Rope.ROPE,
 label: LabelItemHandle,
 ref: REF LONG CARDINAL,
 val: LONG CARDINAL,
 text: REF Rope.ROPE,
 subTextItem: SubTextItemHandle,
 nextItem: REF ANY -- used in chain for update screen and value
];

CardinalItemHandle: TYPE = REF CardinalItemBody;
CardinalItemBody: TYPE = RECORD[
 forValSize: Rope.ROPE,
 label: LabelItemHandle,
 ref: REF CARDINAL,
 val: CARDINAL,
 text: REF Rope.ROPE,
 subTextItem: SubTextItemHandle,
 nextItem: REF ANY -- used in chain for update screen and value
];

SignedItemHandle: TYPE = REF SignedItemBody;
SignedItemBody: TYPE = RECORD[
 forValSize: Rope.ROPE,
 label: LabelItemHandle,
 ref: REF INT,
 val: INT,
 text: REF Rope.ROPE,
 subTextItem: SubTextItemHandle,
 nextItem: REF ANY -- used in chain for update screen and value
];

BooleanItemHandle: TYPE = REF BooleanItemBody;
BooleanItemBody: TYPE = RECORD[
 tool: ToolHandle,
 label: LabelItemHandle,
 ref: REF BOOLEAN,
 val: BOOLEAN,
 button: Buttons.Button,
 editable: BOOLEAN,
 changed: BOOLEAN,
 x, y, w, h: INTEGER,
 nextItem: REF ANY -- used in chain for update screen and value
];

MatrixHandle: TYPE = REF MatrixBody;
MatrixBody: TYPE = RECORD[
 rowLead: INTEGER,
 colSpacing: INTEGER,
 columns: MatrixColumnHandle,
 rows: MatrixRowHandle];

-- following are used in matrices 

MatrixColumnHandle: TYPE = REF MatrixColumnBody;
MatrixColumnBody: TYPE = RECORD[
 w1, w2, w3: CARDINAL, -- sub column widths
 next: MatrixColumnHandle];

MatrixRowHandle: TYPE = REF MatrixRowBody;
MatrixRowBody: TYPE = RECORD[
 h: CARDINAL, -- row height
 items: ItemHandle,
 next: MatrixRowHandle];


-- localProcedures


AddLabelToTool: PROCEDURE[tool: ToolHandle, label: Rope.ROPE] RETURNS[LabelItemHandle] =
 BEGIN
 labelItem: LabelItemHandle ← NEW[LabelItemBody ← [tool, label, 0, 0, NIL]];
labelItem.w ← VFonts.StringWidth[label] + 10;
labelItem.h ← VFonts.FontHeight[] + 5;
RETURN[labelItem];
END;

AddSubTextItemToTool: PROCEDURE[tool: ToolHandle, textRef: REF Rope.ROPE, forSize: Rope.ROPE, editable: BOOLEAN, correspondingItem: REF ANY] RETURNS[SubTextItemHandle] =
 BEGIN
 w: INTEGER ← VFonts.StringWidth[IF forSize # NIL THEN forSize ELSE textRef^] + 10;
 h: INTEGER ← VFonts.FontHeight[] + 5;
 item: SubTextItemHandle ← NEW[SubTextItemBody ← [tool.container, NIL, nil, editable, textRef, textRef^, 0, 0, w, h, correspondingItem]];
 RETURN[item];
 END;

AddFillerItemToTool: PROCEDURE[tool: ToolHandle, width, height: CARDINAL] RETURNS[ItemHandle] =
 BEGIN
 fItem: FillerItemHandle ← NEW[FillerItemBody ← [0]];
 RETURN[NEW[ItemBody ← [width, 0, 0, height, fItem, NIL]]];
 END;

AddLabeledItemToTool: PROCEDURE[tool: ToolHandle, fullName: Rope.ROPE, label: Rope.ROPE, ref: REF ANY, forSize: Rope.ROPE, editable: BOOLEAN] RETURNS[ItemHandle] =
 BEGIN
 SELECT TRUE FROM
  ISTYPE[ref, REF Rope.ROPE] =>
   BEGIN
   textRef: REF Rope.ROPE ← NARROW[ref];
   RETURN[AddTextItemToTool[tool, label, textRef, forSize, editable, NIL]];
   END;
   
  ISTYPE[ref, REF LONG CARDINAL] =>
   BEGIN
   lcRef: REF LONG CARDINAL ← NARROW[ref];
   RETURN[AddUnsignedItemToTool[tool, label, lcRef, forSize, editable]];
   END;
   
  ISTYPE[ref, REF CARDINAL] =>
   BEGIN
   cRef: REF CARDINAL ← NARROW[ref];
   RETURN[AddCardinalItemToTool[tool, label, cRef, forSize, editable]];
   END;
   
  ISTYPE[ref, REF INT] =>
   BEGIN
   intRef: REF INT ← NARROW[ref];
   RETURN[AddSignedItemToTool[tool, label, intRef, forSize, editable]];
   END;
   
  ISTYPE[ref, REF BOOLEAN] =>
   BEGIN
   boolRef: REF BOOLEAN ← NARROW[ref];
   RETURN[AddBooleanItemToTool[tool, label, boolRef, editable]];
   END;
   
  ENDCASE => ERROR UnimplimentedType;

 END;

AddUnsignedItemToTool: PROCEDURE[tool: ToolHandle, labelName: Rope.ROPE, ref: REF LONG CARDINAL, forSize: Rope.ROPE, editable: BOOLEAN] RETURNS[ItemHandle] =
 BEGIN
 initialText: Rope.ROPE ← Convert.RopeFromCard[ref^];
 uItem: UnsignedItemHandle ← NEW[UnsignedItemBody ← [forSize, NIL, ref, ref^, NEW[Rope.ROPE ← initialText], NIL, NIL]];
 subTextItem: SubTextItemHandle ← AddSubTextItemToTool[tool, uItem.text, forSize, editable, uItem];
 labelItem: LabelItemHandle ← AddLabelToTool[tool, Rope.Cat[labelName, ":"]];
 uItem.subTextItem ← subTextItem;
 uItem.nextItem ← tool.items;
 tool.items ← uItem;
 uItem.label ← labelItem;
 RETURN[NEW[ItemBody ← [labelItem.w , subTextItem.w, 0, MAX[labelItem.h, subTextItem.h], uItem, NIL]]];
 END;

AddCardinalItemToTool: PROCEDURE[tool: ToolHandle, labelName: Rope.ROPE, ref: REF CARDINAL, forSize: Rope.ROPE, editable: BOOLEAN] RETURNS[ItemHandle] =
 BEGIN
 initialText: Rope.ROPE ← Convert.RopeFromCard[ref^];
 cItem: CardinalItemHandle ← NEW[CardinalItemBody ← [forSize, NIL, ref, ref^, NEW[Rope.ROPE ← initialText], NIL, NIL]];
 subTextItem: SubTextItemHandle ← AddSubTextItemToTool[tool, cItem.text, forSize, editable, cItem];
 labelItem: LabelItemHandle ← AddLabelToTool[tool, Rope.Cat[labelName, ":"]];
 cItem.subTextItem ← subTextItem;
 cItem.nextItem ← tool.items;
 tool.items ← cItem;
 cItem.label ← labelItem;
 RETURN[NEW[ItemBody ← [labelItem.w , subTextItem.w, 0, MAX[labelItem.h, subTextItem.h], cItem, NIL]]];
 END;

AddSignedItemToTool: PROCEDURE[tool: ToolHandle, labelName: Rope.ROPE, ref: REF INT, forSize: Rope.ROPE, editable: BOOLEAN] RETURNS[ItemHandle] =
 BEGIN
 initialText: Rope.ROPE ← Convert.RopeFromInt[ref^];
 sItem: SignedItemHandle ← NEW[SignedItemBody ← [forSize, NIL, ref, ref^, NEW[Rope.ROPE ← initialText], NIL, NIL]];
 subTextItem: SubTextItemHandle ← AddSubTextItemToTool[tool, sItem.text, forSize, editable, sItem];
 labelItem: LabelItemHandle ← AddLabelToTool[tool, Rope.Cat[labelName, ":"]];
 sItem.subTextItem ← subTextItem;
 sItem.nextItem ← tool.items;
 tool.items ← sItem;
 sItem.label ← labelItem;
 RETURN[NEW[ItemBody ← [labelItem.w , subTextItem.w, 0, MAX[labelItem.h, subTextItem.h], sItem, NIL]]];
 END;

AddTextItemToTool: PROCEDURE[tool: ToolHandle, labelName: Rope.ROPE, ref: REF Rope.ROPE, forSize: Rope.ROPE, editable: BOOLEAN, correspondingItem: REF ANY] RETURNS[ItemHandle] =
 BEGIN
 w: INTEGER ← VFonts.StringWidth[IF forSize # NIL THEN forSize ELSE ref^] + 10;
 h: INTEGER ← VFonts.FontHeight[] + 5;
 tItem: TextItemHandle ← NEW[TextItemBody ← [forSize, NIL, ref, ref^, NEW[Rope.ROPE ← ref^], NIL, NIL]];
 subTextItem: SubTextItemHandle ← AddSubTextItemToTool[tool, tItem.text, forSize, editable, tItem];
 labelItem: LabelItemHandle ← AddLabelToTool[tool, Rope.Cat[labelName, ":"]];
 tItem.subTextItem ← subTextItem;
 tItem.nextItem ← tool.items;
 tool.items ← tItem;
 tItem.label ← labelItem;
 RETURN[NEW[ItemBody ← [labelItem.w , subTextItem.w, 0, MAX[labelItem.h, subTextItem.h], tItem, NIL]]];
 END;

AddBooleanItemToTool: PROCEDURE[tool: ToolHandle, labelName: Rope.ROPE, ref: REF BOOLEAN, editable: BOOLEAN] RETURNS[ItemHandle] =
 BEGIN
 bItem: BooleanItemHandle ← NEW[BooleanItemBody ← [tool, NIL, ref, ref^, NIL, editable, FALSE, 0, 0, 0, 0, NIL]];
 labelItem: LabelItemHandle ← AddLabelToTool[tool, Rope.Cat[labelName, ":"]];
 bItem.nextItem ← tool.items;
 tool.items ← bItem;
 bItem.label ← labelItem;
 bItem.w ← VFonts.StringWidth["FALSE"] + 10;
bItem.h ← VFonts.FontHeight[] + 5;
 RETURN[NEW[ItemBody ← [labelItem.w, bItem.w, 0, MAX[labelItem.h,bItem.h], bItem, NIL]]];
 END;

AddButtonItemToTool: PROCEDURE[tool: ToolHandle, buttonName, forSize: Rope.ROPE, buttonProc: PROCEDURE[REF ANY], toolData: REF ANY, border: BOOLEAN ← FALSE] RETURNS[ItemHandle] =
 BEGIN
 buttonItem: ButtonItemHandle ← NEW[ButtonItemBody ← [tool, buttonProc, forSize, buttonName, border, NIL, toolData]];
 RETURN[NEW[ItemBody ← [VFonts.StringWidth[IF forSize # NIL THEN forSize ELSE buttonName], 0, 0, VFonts.FontHeight[] + 2, buttonItem, NIL]]];
 END;

NominalButtonProc: Buttons.ButtonProc = TRUSTED
 BEGIN
 buttonItem: ButtonItemHandle ← NARROW[clientData];
 UpdateValuesFromScreen[buttonItem.tool];
 buttonItem.proc[buttonItem.data];
 UpdateScreen[buttonItem.tool];
 END;

BooleanButtonProc: Buttons.ButtonProc = TRUSTED
 BEGIN
 item: BooleanItemHandle ← NARROW[clientData];
 IF item.editable THEN
  BEGIN
  item.changed ← TRUE;
  item.val ← NOT item.val;
  Buttons.ReLabel[item.button, IF item.val THEN "TRUE" ELSE "FALSE"]
  END;
 END;

MakeSubTextItemAsButton: PROCEDURE[item: SubTextItemHandle] =
 BEGIN
 contentsWidth: INTEGER ← VFonts.StringWidth[item.contents];
 button: Viewer;
 ClearSubTextItem[item];
 button ← Buttons.Create[
info: [
name: item.contents,
  wx: item.x+item.w-contentsWidth-10, wy: item.y,
  ww: contentsWidth+10, wh: VFonts.FontHeight[]+5,
  parent: item.parent,
  border: FALSE,
  scrollable: FALSE],
 proc: MakeSubTextItemAsTextBox,
 clientData: item,
 paint: TRUE];
item.mode ← button;
 item.v ← button;
 END;

MakeSubTextItemAsTextBox: Buttons.ButtonProc = TRUSTED
 BEGIN
 item: SubTextItemHandle ← NARROW[clientData];
 textBox: ViewerClasses.Viewer;
 IF NOT item.editable THEN RETURN;
 IF item.v # NARROW[parent, ViewerClasses.Viewer] THEN ERROR;
 ClearSubTextItem[item];
 textBox ← ViewerTools.MakeNewTextViewer[
  info: [
   parent: item.parent,
   wx: item.x, wy: item.y,
  ww: item.w, wh: item.h,
 border: FALSE,
  scrollable: FALSE],
paint: FALSE];
ViewerTools.SetContents[textBox, item.contents];
item.v ← textBox;
item.mode ← textBox;
 END;

ClearSubTextItem: PROCEDURE[item: SubTextItemHandle] =
 BEGIN
 IF item.mode = button THEN
  BEGIN
  Buttons.ReLabel[item.v, "", TRUE];
  END;
 IF item.mode = textBox THEN
  BEGIN
  ViewerTools.SetContents[item.v, "", TRUE];
  END;
 IF item.mode # nil THEN ViewerOps.DestroyViewer[item.v, FALSE];
 item.mode ← nil;
 item.v ← NIL;
 END;


BooleanItemProc: PROCEDURE[clientData: REF ANY] =
 BEGIN
 item: BooleanItemHandle ← NARROW[clientData];
 IF NOT item.editable THEN RETURN;
 item.val ← NOT item.val;
 item.changed ← TRUE;
 Buttons.ReLabel[item.button, IF item.val THEN "TRUE" ELSE "FALSE"];
 END;

UpdateScreenSubText: PROCEDURE[item: SubTextItemHandle] =
 BEGIN -- this code has been simplified to look for a bug, later it should avoid converting unchnged buttons to buttons.
 IF item.textRef^ # item.contents THEN
  BEGIN
  item.contents ← item.textRef^
  END;
 MakeSubTextItemAsButton[item];
 END;

UpdateScreenText: PROCEDURE[item: TextItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 IF item.ref^ # item.val THEN
  BEGIN
  item.val ← item.ref^;
  item.text^ ← item.val;
  UpdateScreenSubText[item.subTextItem];
  END;
 RETURN[item.nextItem];
 END;

UpdateScreenUnsigned: PROCEDURE[item: UnsignedItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 IF item.ref^ # item.val THEN
  BEGIN
  item.val ← item.ref^;
  item.text^ ← Convert.RopeFromCard[item.val];
  UpdateScreenSubText[item.subTextItem];
  END;
 RETURN[item.nextItem];
 END;

UpdateScreenCardinal: PROCEDURE[item: CardinalItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 IF item.ref^ # item.val THEN
  BEGIN
  item.val ← item.ref^;
  item.text^ ← Convert.RopeFromCard[item.val];
  UpdateScreenSubText[item.subTextItem];
  END;
 RETURN[item.nextItem];
 END;

UpdateScreenSigned: PROCEDURE[item: SignedItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 IF item.ref^ # item.val THEN
  BEGIN
  item.val ← item.ref^;
  item.text^ ← Convert.RopeFromInt[item.val];
  UpdateScreenSubText[item.subTextItem];
  END;
 RETURN[item.nextItem];
 END;

UpdateScreenBoolean: PROCEDURE[item: BooleanItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 IF item.ref^ # item.val THEN
  BEGIN
  item.val ← item.ref^;
  Buttons.ReLabel[item.button, IF item.ref^ THEN "TRUE" ELSE "FALSE"];
  END;
 RETURN[item.nextItem];
 END;

UpdateValuesFromScreen: PROCEDURE[tool: ToolHandle] =
 BEGIN
 item: REF ANY ← tool.items;
 WHILE item # NIL DO
  item ← UpdateOneItemValueFromScreen[item];
  ENDLOOP;
 END;

UpdateOneItemValueFromScreen: PROCEDURE[item: REF ANY] RETURNS[nextItem: REF ANY] =
 BEGIN
 SELECT TRUE FROM
  ISTYPE[item, TextItemHandle] => nextItem ← UpdateTextValue[NARROW[item]];
  ISTYPE[item, UnsignedItemHandle] => nextItem ← UpdateUnsignedValue[NARROW[item]];
  ISTYPE[item, CardinalItemHandle] => nextItem ← UpdateCardinalValue[NARROW[item]];
  ISTYPE[item, SignedItemHandle] => nextItem ← UpdateSignedValue[NARROW[item]];
  ISTYPE[item, BooleanItemHandle] => nextItem ← UpdateBooleanValue[NARROW[item]];   
  ENDCASE => ERROR;
 END;

UpdateSubTextValue: PROCEDURE[item: SubTextItemHandle] =
 BEGIN
 changed: BOOLEAN ← item.mode = textBox;
 IF changed THEN
  BEGIN
  item.contents ← ViewerTools.GetContents[item.v];
  MakeSubTextItemAsButton[item];
  END;
 item.textRef^ ← item.contents;
 END;

UpdateTextValue: PROCEDURE[item: TextItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 UpdateSubTextValue[item.subTextItem];
 item.ref^ ← item.val ← item.text^;
 RETURN[item.nextItem];
 END;

UpdateUnsignedValue: PROCEDURE[item: UnsignedItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 UpdateSubTextValue[item.subTextItem];
 item.val ← Convert.CardFromRope[item.text^];
 item.ref^ ← item.val;
 RETURN[item.nextItem];
 END;

UpdateCardinalValue: PROCEDURE[item: CardinalItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 ln: Basics.LongNumber;
 UpdateSubTextValue[item.subTextItem];
 ln.lc ← Convert.CardFromRope[item.text^];
 item.val ← ln.lowbits;
 item.ref^ ← item.val;
 RETURN[item.nextItem];
 END;

UpdateSignedValue: PROCEDURE[item: SignedItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 UpdateSubTextValue[item.subTextItem];
 item.val ← Convert.IntFromRope[item.text^];
 item.ref^ ← item.val;
 RETURN[item.nextItem];
 END;

UpdateBooleanValue: PROCEDURE[item: BooleanItemHandle] RETURNS[nextItem: REF ANY] =
 BEGIN
 item.ref^ ← item.val;
 item.changed ← FALSE;
 RETURN[item.nextItem];
 END;


-- structure routines

AddRowToTool: PROCEDURE[items: LIST OF ItemHandle, spacing: INTEGER] RETURNS[ItemHandle] =
 BEGIN
 RETURN[AddMatrixToTool[LIST[items], 0, spacing]]
 END;

AddColumnToTool: PROCEDURE[items: LIST OF ItemHandle, lead: INTEGER] RETURNS[ItemHandle] =
 BEGIN
 firstRowCell: LIST OF LIST OF ItemHandle ← NIL;
 lastRowCell: LIST OF LIST OF ItemHandle;
 FOR itemCell: LIST OF ItemHandle ← items, itemCell.rest WHILE itemCell # NIL DO
  rowCell: LIST OF LIST OF ItemHandle ← LIST[LIST[itemCell.first]];
  IF firstRowCell = NIL THEN firstRowCell ← rowCell ELSE lastRowCell.rest ← rowCell;
  lastRowCell ← rowCell;
  ENDLOOP;
 RETURN[AddMatrixToTool[firstRowCell, lead, 0]];
 END;

AddMatrixToTool: PROCEDURE[items: LIST OF LIST OF ItemHandle, rowLead, colSpacing: INTEGER] RETURNS[ItemHandle] =
 BEGIN
 firstRow: MatrixRowHandle ← NIL;
 lastRow: MatrixRowHandle ← NIL;
 firstColumn: MatrixColumnHandle ← NIL;
 lastColumn: MatrixColumnHandle ← NIL;
 matrix: MatrixHandle ← NEW[MatrixBody ← [rowLead, colSpacing, NIL, NIL]];
 matrixItem: ItemHandle ← NEW[ItemBody ← [0, 0, 0, 0, NIL, NIL]];

 FOR rowCell: LIST OF LIST OF ItemHandle ← items, rowCell.rest WHILE rowCell # NIL DO
  row: MatrixRowHandle ← NEW[MatrixRowBody ← [0, NIL, NIL]];
  column: MatrixColumnHandle;
  lastItem: ItemHandle ← NIL;
  IF firstRow = NIL THEN firstRow ← row ELSE lastRow.next ← row;
  lastRow ← row;
  IF firstRow = lastRow THEN -- this is first row, build up the columns
   FOR itemCell: LIST OF ItemHandle ← rowCell.first, itemCell.rest WHILE itemCell # NIL DO
    column ← NEW[MatrixColumnBody ← [0, 0, 0, NIL]];
    IF firstColumn = NIL THEN firstColumn ← column ELSE lastColumn.next ← column;
    lastColumn ← column;
    ENDLOOP;
  column ← firstColumn;
  FOR itemCell: LIST OF ItemHandle ← rowCell.first, itemCell.rest WHILE itemCell # NIL DO
   item: ItemHandle ← itemCell.first;
   IF item.w1 > column.w1 THEN column.w1 ← item.w1;
   IF item.w2 > column.w2 THEN column.w2 ← item.w2;
   IF item.w3 > column.w3 THEN column.w3 ← item.w3;
   IF item.height > row.h THEN row.h ← item.height;
   IF row.items = NIL THEN row.items ← item ELSE lastItem.next ← item;
   lastItem ← item;
   column ← column.next;
   ENDLOOP;
  ENDLOOP;
 FOR c: MatrixColumnHandle ← firstColumn, c.next WHILE c # NIL DO
  matrixItem.w1 ← matrixItem.w1 + c.w1 + c.w2 + c.w3 + matrix.colSpacing;
  ENDLOOP;
 matrixItem.w1 ← matrixItem.w1-matrix.colSpacing; -- correct for last column
 FOR r: MatrixRowHandle ← firstRow, r.next WHILE r # NIL DO
  matrixItem.height ← matrixItem.height+r.h+matrix.rowLead;
  ENDLOOP;
 matrixItem.height ← matrixItem.height-matrix.rowLead; -- correct for last row
 matrix.rows ← firstRow;
 matrix.columns ← firstColumn;
 matrixItem.data ← matrix;
 RETURN[matrixItem];
 END;


PositionItem: PROCEDURE[item: ItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 SELECT TRUE FROM
  ISTYPE[item.data, ButtonItemHandle] =>
    PositionButtonItem[NARROW[item.data], x1, w1, x2, w2, x3, w3, y];
  ISTYPE[item.data, MatrixHandle] =>
    PositionMatrix[NARROW[item.data], x1, w1, x2, w2, x3, w3, y];
  ISTYPE[item.data, FillerItemHandle] => NULL;
  ISTYPE[item.data, TextItemHandle] =>
    PositionTextItem[NARROW[item.data], x1, w1, x2, w2, x3, w3, y];
  ISTYPE[item.data, UnsignedItemHandle] =>
    PositionUnsignedItem[NARROW[item.data], x1, w1, x2, w2, x3, w3, y];
  ISTYPE[item.data, CardinalItemHandle] =>
    PositionCardinalItem[NARROW[item.data], x1, w1, x2, w2, x3, w3, y];
  ISTYPE[item.data, SignedItemHandle] =>
    PositionSignedItem[NARROW[item.data], x1, w1, x2, w2, x3, w3, y];
  ISTYPE[item.data, BooleanItemHandle] =>
    PositionBooleanItem[NARROW[item.data], x1, w1, x2, w2, x3, w3, y]; 
  ENDCASE => ERROR;
 END;

PositionButtonItem: PROCEDURE[item: ButtonItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 item.button ← Buttons.Create[info: [name: IF item.forSize # NIL THEN item.forSize ELSE item.name, wx: x1, wy: y, parent: item.tool.container, border: item.border], proc: NominalButtonProc, clientData: item, fork: TRUE];
 IF item.forSize # NIL THEN Buttons.ReLabel[item.button, item.name];
 END;

PositionMatrix: PROCEDURE[item: MatrixHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 FOR row: MatrixRowHandle ← item.rows, row.next WHILE row # NIL DO
  col: MatrixColumnHandle ← item.columns;
  x: INTEGER ← x1;
  FOR subItem: ItemHandle ← row.items, subItem.next WHILE subItem # NIL DO
   PositionItem[subItem, x, col.w1, x+col.w1, col.w2, x+col.w1+col.w2, col.w3, y];
   
   x ← x + col.w1 + col.w2 + col.w3 + item.colSpacing;
   col ← col.next;
   ENDLOOP;
  
  y ← y + row.h + item.rowLead;
  ENDLOOP;
 END;

PositionSubTextItem: PROCEDURE[item: SubTextItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 item.x ← x2; item.w ← w2; item.y ← y;
 MakeSubTextItemAsButton[item];
 END;

PositionTextItem: PROCEDURE[item: TextItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 PositionLabel[item.label, x1, w1, x2, w2, x3, w3, y];
 PositionSubTextItem[item.subTextItem, x1, w1, x2, w2, x3, w3, y]
 END;

PositionUnsignedItem: PROCEDURE[item: UnsignedItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 PositionLabel[item.label, x1, w1, x2, w2, x3, w3, y];
 PositionSubTextItem[item.subTextItem, x1, w1, x2, w2, x3, w3, y]
 END;

PositionCardinalItem: PROCEDURE[item: CardinalItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 PositionLabel[item.label, x1, w1, x2, w2, x3, w3, y];
 PositionSubTextItem[item.subTextItem, x1, w1, x2, w2, x3, w3, y]
 END;

PositionSignedItem: PROCEDURE[item: SignedItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 PositionLabel[item.label, x1, w1, x2, w2, x3, w3, y];
 PositionSubTextItem[item.subTextItem, x1, w1, x2, w2, x3, w3, y]
 END;

PositionBooleanItem: PROCEDURE[item: BooleanItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 PositionLabel[item.label, x1, w1, x2, w2, x3, w3, y];
 item.button ← Buttons.Create[info: [name: "FALSE", wx: x2, wy: y+2, parent: item.tool.container, border: FALSE], proc: BooleanButtonProc, clientData: item, fork: TRUE];
 IF item.val THEN Buttons.ReLabel[item.button, "TRUE"];
 END;

PositionLabel: PROCEDURE[item: LabelItemHandle, x1, w1, x2, w2, x3, w3: INTEGER, y: INTEGER] =
 BEGIN
 item.viewer ← Labels.Create[[parent: item.tool.container, wx: x1, wy: y, ww: item.w, wh: item.h, name: item.name, border: FALSE]];
 END;



--UserExec.RegisterCommand[name: "EasyTool", proc: BuildTool, briefDoc: ""];
--[] ← EasyTool[UserExec.GetExecHandle[]];



END..


-- approx Sept 5, 82: Sturgis, started EasyToolImpl.mesa, by edit of NewCalc.mesa

-- September 6, 1982 7:50 pm: Sturgis: spent today re-implementing to conform to the new easy interface. now works. Several RTEs involving not getting screen or values updated at appropriate times. Tested unsigned, boolean, and buttons. Have not implemented save or restore.
-- September 6, 1982 7:52 pm: position buttons down 2 units to try for better allignment.
-- RTE: approx September 6, 1982 8:11 pm: found that when text became editable, it overlaid line directly above it. so, made some changes to calculation of next y etc. In particular, replaced currentH by currentY.
-- RTE: September 6, 1982 8:11 pm: now have a stair case for each line, clearly currentY is getting advanced when it should not.
-- RTE: September 6, 1982 8:18 pm: more staircase.
-- September 6, 1982 8:25 pm: add skipOver to obtainItems
-- RTE: September 6, 1982 8:38 pm: overlay test in tabTo was inverted.
-- Sept 17: begin conversion to Matrices, Rows, and Columns.
-- RTE: September 18, 1982 12:31 pm: forgot to position items.
-- RTE: September 18, 1982 12:41 pm: matrix advanced x incorrectly.
-- RTE: September 18, 1982 1:03 pm: add column to tool incorrectly constructed the LIST of LIST of with which to call add matrix to tool.
-- RTE: September 18, 1982 1:30 pm: matrix items should have only a w1, which should be sum of w1, w2 and w3 for each column. Incorrect code computed a w1 and a w2 and a w3. but these created troble outside?
-- RTE: September 18, 1982 1:43 pm: forgot to attach labels to items. during position item code.
-- RTE: September 18, 1982 1:50 pm: always used "forSize" in computing width of buttons.
-- RET: September 18, 1982 2:01 pm: update one item from screen still had "secondLook" logic for calls from TextItems. THus text type items were never updated from screen.
-- RTE: September 18, 1982 2:26 pm: positionItem code needed to have a (null) case for FIllerItem.
-- change: September 18, 1982 2:40 pm: right adjust sub text when as buttons.
-- RTE: September 18, 1982 2:47 pm: can't see the text, adjust a few fiddle factors.
-- change: September 18, 1982 3:05 pm: (above not fixed, but) add automatic row lead and col spacing.
-- change: September 18, 1982 3:38 pm: work on previous problem, and also try to get right justification to work.
-- ugh: September 18, 1982 4:32 pm: replace viewer seems to be putting the new viewer in the same position as the old viewer.
-- RTE: September 18, 1982 5:27 pm: remarks on preceeding period: to change local position, and contents of a viewer without repainting whole container: set contents to empty (different routine for textbos and button), with paint=true. destroy viewer. create new one, with paint=true?. Don't know if this will work with borders. Also, text items did not work because I passed the wrong ref rope to the subtextitem.
-- change: October 6, 1982 3:19 pm: defineButton now takes an obtional data param specific to the button.
-- change: October 10, 1982 12:04 pm: Change to 3.4: in a button proc, viewer becomes NARROW[parent, Viewer].
-- change: May 31, 1983 11:33 am: change " = BEGIN " to "= TRUSTED BEGIN " for NominalButtonProc, BooleanButtonProc, and MakeSubTextItemAsTextBox.
-- Change: June 22, 1984 9:27:29 am PDT: conversion to Cedar 5.2