DIRECTORY Buttons USING [Button, ButtonProc, Create, SetDisplayStyle], Graphics USING [black, DrawBox, SetColor], Labels USING [Create, SetDisplayStyle], Rope USING [Flatten, ROPE, Size], Runtime USING [BoundsFault], UserTerminal USING [BlinkDisplay], VFonts USING [defaultFont, StringWidth, FontHeight, Font], ViewerClasses USING [DestroyProc, PaintProc, Viewer, ViewerClass, ViewerClassRec], ViewerOps USING [CreateViewer, DestroyViewer, EstablishViewerPosition, PaintViewer, RegisterViewerClass], VTables USING [Border, FullBorder, NullBorder, VTable]; VTablesImpl: CEDAR MONITOR LOCKS data USING data: TableData IMPORTS VFonts, Buttons, Graphics, Labels, Rope, Runtime, ViewerOps, UserTerminal EXPORTS VTables = BEGIN OPEN Rope, VTables; Viewer: TYPE = ViewerClasses.Viewer; TableData: TYPE = REF TableDataRep; TableDataRep: TYPE = MONITORED RECORD [rows, columns: NAT _ 0, installed, locked, staticSize: BOOL _ FALSE, live: BOOL _ TRUE, condition: CONDITION, xRuleWidth: INTEGER _ 1, yRuleWidth: INTEGER _ 1, rowWidths: WidthSeq _ NIL, colWidths: WidthSeq _ NIL, width: INTEGER _ 0, height: INTEGER _ 0, request: INTEGER _ 0, rowSeq: RowSeq]; RowSeq: TYPE = REF RowSeqRep; RowSeqRep: TYPE = RECORD [rowData: SEQUENCE nRows: NAT OF RowData]; RowData: TYPE = REF RowDataRep; RowDataRep: TYPE = RECORD [colData: SEQUENCE nCols: NAT OF Entry]; Entry: TYPE = RECORD [border: Border, useMaxSize: BOOL, xoff,yoff: Offset, viewer: Viewer] _ NullEntry; NullEntry: Entry = [NullBorder, TRUE, 0, 0, NIL]; Offset: TYPE = INTEGER; textXoff: Offset _ 4; -- hack for $Text x offset textYoff: Offset _ 1; -- hack for $Text y offset wHack: INTEGER _ 8; -- hack for entry size calculated by text hHack: INTEGER _ 3; -- hack for entry size calculated by text WidthSeq: TYPE = REF WidthSeqRep; WidthSeqRep: TYPE = RECORD [widths: SEQUENCE size: NAT OF INTEGER]; AcquireLock: ENTRY PROC [data: TableData, painting: BOOL _ FALSE] RETURNS [BOOL] = { ENABLE UNWIND => NULL; DO IF data = NIL OR NOT data.live THEN RETURN [FALSE]; IF painting AND NOT data.installed THEN {data.request _ data.request + 1; RETURN [FALSE]}; IF NOT data.locked THEN {data.locked _ TRUE; RETURN [TRUE]}; WAIT data.condition; ENDLOOP; }; ReleaseLock: ENTRY PROC [data: TableData, install: BOOL _ FALSE] = { data.locked _ FALSE; IF install THEN data.installed _ TRUE; BROADCAST data.condition; }; NewRowSeq: PROC [rows,columns: NAT] RETURNS [RowSeq] = { rowSeq: RowSeq _ NEW[RowSeqRep[rows]]; FOR i: NAT IN [0..rows) DO rowSeq[i] _ NEW[RowDataRep[columns]] ENDLOOP; RETURN [rowSeq]; }; NewWidthSeq: PROC [width: NAT] RETURNS [ws: WidthSeq] = { ws _ NEW[WidthSeqRep[width]]; FOR i: NAT IN [0..width) DO ws[i] _ 0 ENDLOOP }; RecalculateWidths: PROC [data: TableData] = { rowWidths: WidthSeq _ data.rowWidths; colWidths: WidthSeq _ data.colWidths; FOR row: NAT IN [0..data.rows) DO rowWidths[row] _ 0; FOR col: NAT IN [0..data.columns) DO ent: Entry _ data.rowSeq[row][col]; v: Viewer _ ent.viewer; IF row = 0 THEN colWidths[col] _ 0; IF v # NIL THEN {w: INTEGER _ v.ww + ABS[ent.xoff]; h: INTEGER _ v.wh + ABS[ent.yoff]; IF w > colWidths[col] THEN colWidths[col] _ w; IF h > rowWidths[row] THEN rowWidths[row] _ h}; ENDLOOP; ENDLOOP; }; Create: PUBLIC PROC [columns, rows: INTEGER _ 1, name: ROPE _ NIL, parent: Viewer _ NIL, xRuleWidth: INTEGER _ 1, yRuleWidth: INTEGER _ 1, x, y, w, h: INTEGER _ 0, scrollable: BOOL _ FALSE] RETURNS [table: VTable] = TRUSTED { data: TableData _ NIL; IF columns <= 0 OR rows <= 0 THEN RETURN [NIL]; IF name = NIL THEN name _ " "; data _ NEW[TableDataRep]; data.rows _ rows; data.columns _ columns; data.rowSeq _ NewRowSeq[rows, columns]; data.rowWidths _ NewWidthSeq[rows]; data.colWidths _ NewWidthSeq[columns]; data.xRuleWidth _ xRuleWidth; data.yRuleWidth _ yRuleWidth; data.staticSize _ w > 0 OR h > 0; data.width _ w; data.height _ h; IF parent = NIL THEN {parent _ ViewerOps.CreateViewer [flavor: $Container, info: [name: name, column: right, iconic: TRUE], paint: TRUE]}; table _ ViewerOps.CreateViewer [flavor: $VTable, info: [name: name, parent: parent, wx: x, wy: y, ww: w, wh: h, data: data, border: FALSE, scrollable: scrollable], paint: FALSE] }; SetRowsAndColumns: PUBLIC PROC [table: VTable, rows, columns: NAT _ 0] = TRUSTED { data: TableData _ NARROW[table.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF rows # data.rows OR columns # data.columns THEN {newRowSeq: RowSeq _ NewRowSeq[rows, columns]; data.installed _ FALSE; -- demand installation IF rows # data.rows THEN { newRowWidths: WidthSeq _ NewWidthSeq[rows]; FOR i: NAT IN [0..MIN[rows, data.rows]) DO newRowWidths[i] _ data.rowWidths[i]; ENDLOOP; data.rowWidths _ newRowWidths; }; IF columns # data.columns THEN { newColWidths: WidthSeq _ NewWidthSeq[columns]; FOR i: NAT IN [0..MIN[columns, data.columns]) DO newColWidths[i] _ data.colWidths[i]; ENDLOOP; data.colWidths _ newColWidths; }; FOR row: NAT IN [0..data.rows) DO FOR col: NAT IN [0..data.columns) DO oldEntry: Entry _ data.rowSeq[row][col]; IF row < rows AND col < columns THEN {-- copy the entry (if any) from the old world newRowSeq[row][col] _ oldEntry; } ELSE {-- delete the old viewer IF oldEntry.viewer # NIL THEN ViewerOps.DestroyViewer[oldEntry.viewer, FALSE]; }; ENDLOOP; ENDLOOP; data.rows _ rows; data.columns _ columns; data.rowSeq _ newRowSeq; -- make this the last gasp }; ReleaseLock[data]; }; Install: PUBLIC PROC [table: VTable, paint: BOOL _ TRUE] = TRUSTED { data: TableData _ NARROW[table.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; {rows: NAT _ data.rows; columns: NAT _ data.columns; minX: INTEGER _ 0; minY: INTEGER _ 0; maxX: INTEGER _ minX; maxY: INTEGER _ minY; xRule: CARDINAL _ data.xRuleWidth; yRule: CARDINAL _ data.yRuleWidth; rowWidths: WidthSeq _ data.rowWidths; colWidths: WidthSeq _ data.colWidths; w: INTEGER _ table.ww; h: INTEGER _ table.wh; RecalculateWidths[data]; minY _ xRule; FOR i: NAT IN [0..rows) DO row: RowData _ data.rowSeq[i]; rowWidth: INTEGER _ rowWidths[i]; minX _ yRule; FOR j: NAT IN [0..columns) DO e: Entry _ row[j]; v: Viewer _ e.viewer; colWidth: INTEGER _ colWidths[j]; IF v # NIL THEN {vw: INTEGER _ colWidth-ABS[e.xoff]; vh: INTEGER _ rowWidth-ABS[e.yoff]; IF NOT e.useMaxSize THEN {vh _ v.wh; vw _ v.ww}; ViewerOps.EstablishViewerPosition [v, minX+e.xoff, minY+e.yoff, vw, vh]}; minX _ minX + colWidth + yRule ENDLOOP; minY _ minY + rowWidth + xRule ENDLOOP; data.width _ minX; data.height _ minY; IF NOT data.staticSize THEN {w _ data.width; h _ data.height}; ViewerOps.EstablishViewerPosition [table, table.wx, table.wy, w, h]; ReleaseLock[data, TRUE]; IF paint OR data.request # 0 THEN ViewerOps.PaintViewer[table, client]; }}; GetTableEntry: PUBLIC PROC [table: VTable, row, column: NAT _ 0] RETURNS [v: Viewer] = TRUSTED { data: TableData _ NARROW[table.data]; v _ NIL; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF row >= data.rows OR column >= data.columns THEN ReleaseAndFault[data]; v _ data.rowSeq[row][column].viewer; ReleaseLock[data]; }; SwapTableEntries: PUBLIC PROC [table: VTable, row1, column1, row2, column2: NAT _ 0, swapBorders: BOOL _ FALSE] = TRUSTED { data: TableData _ NARROW[table.data]; v: Viewer _ NIL; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF row1 >= data.rows OR column1 >= data.columns OR row2 >= data.rows OR column2 >= data.columns THEN ReleaseAndFault[data]; {e1: Entry _ data.rowSeq[row1][column1]; e2: Entry _ data.rowSeq[row2][column2]; IF swapBorders THEN {data.rowSeq[row1][column1] _ e2; data.rowSeq[row2][column2] _ e1} ELSE {data.rowSeq[row1][column1].viewer _ e2.viewer; data.rowSeq[row2][column2].viewer _ e1.viewer}}; data.installed _ FALSE; -- demand installation ReleaseLock[data]; }; SetTableEntry: PUBLIC PROC [table: VTable, row, column: NAT _ 0, name: ROPE _ NIL, flavor: ATOM _ NIL, proc: Buttons.ButtonProc _ NIL, clientData: REF _ NIL, w, h: INTEGER _ 0, xoff, yoff: INTEGER _ 0, border: Border _ FullBorder, font: VFonts.Font _ NIL, displayStyle: ATOM _ NIL, useMaxSize: BOOL _ FALSE] = TRUSTED { v: Viewer _ NIL; parent: Viewer _ table; data: TableData _ NARROW[table.data]; flat: ROPE _ name.Flatten[]; textH, textW: INTEGER _ 0; old: Entry _ NullEntry; new: Entry _ NullEntry; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF row >= data.rows OR column >= data.columns THEN ReleaseAndFault[data]; old _ data.rowSeq[row][column]; IF font = NIL THEN font _ VFonts.defaultFont; IF flat.Size[] # 0 THEN {textW _ VFonts.StringWidth[flat, font]; textH _ VFonts.FontHeight[font]}; data.installed _ FALSE; -- demand installation IF flavor = NIL THEN flavor _ $Label; IF proc # NIL THEN flavor _ $Button; IF name = NIL THEN name _ ""; IF textW > w THEN w _ textW + wHack; IF textH > h THEN h _ textH + hHack; new.xoff _ xoff; new.yoff _ yoff; new.useMaxSize _ useMaxSize; SELECT flavor FROM $Button => {v _ Buttons.Create [info: [name: name, parent: parent, wx: 0, wy: 0, ww: w, wh: h, border: FALSE], proc: proc, clientData: clientData, fork: TRUE, paint: FALSE, font: font]; IF displayStyle # NIL THEN Buttons.SetDisplayStyle[v, displayStyle, FALSE]}; $Label => {v _ Labels.Create [info: [parent: parent, name: name, wx: 0, wy: 0, ww: w, wh: h, border: FALSE], font: font, paint: FALSE]; IF displayStyle # NIL THEN Labels.SetDisplayStyle[v, displayStyle, FALSE]}; $Text => {h _ h - textYoff; -- make height of text, labels & buttons similar v _ ViewerOps.CreateViewer [flavor: $Text, info: [parent: parent, wx: 0, wy: 0, ww: w, wh: h, scrollable: FALSE, border: FALSE], paint: FALSE]; v.class.set[v, name, FALSE]; new.xoff _ textXoff + xoff; new.yoff _ textYoff + yoff}; $Viewer => v _ NARROW[clientData]; ENDCASE => v _ ViewerOps.CreateViewer [flavor: flavor, info: [name: name, parent: parent, wx: 0, wy: 0, ww: w, wh: h, data: clientData, border: FALSE]]; IF old.viewer # v AND old.viewer # NIL THEN ViewerOps.DestroyViewer[old.viewer, FALSE]; new.viewer _ v; new.border _ border; data.rowSeq[row][column] _ new; ReleaseLock[data]; }; GetEntryBorder: PUBLIC PROC [table: VTable, row, column: NAT _ 0] RETURNS [border: Border] = { data: TableData _ NARROW[table.data]; border _ NullBorder; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF row >= data.rows OR column >= data.columns THEN ReleaseAndFault[data]; border _ data.rowSeq[row][column].border; ReleaseLock[data]; }; SetEntryBorder: PUBLIC PROC [table: VTable, row, column: NAT _ 0, border: Border _ FullBorder] = { data: TableData _ NARROW[table.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF row >= data.rows OR column >= data.columns THEN ReleaseAndFault[data]; data.installed _ FALSE; data.rowSeq[row][column].border _ border; ReleaseLock[data]; }; GetEntryOffset: PUBLIC PROC [table: VTable, row, column: NAT _ 0] RETURNS [xoff,yoff: INTEGER] = { data: TableData _ NARROW[table.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF row >= data.rows OR column >= data.columns THEN ReleaseAndFault[data]; xoff _ data.rowSeq[row][column].xoff; yoff _ data.rowSeq[row][column].yoff; ReleaseLock[data]; }; SetEntryOffset: PUBLIC PROC [table: VTable, row, column: NAT _ 0, xoff,yoff: INTEGER _ 0] = { data: TableData _ NARROW[table.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF row >= data.rows OR column >= data.columns THEN ReleaseAndFault[data]; data.installed _ FALSE; data.rowSeq[row][column].xoff _ xoff; data.rowSeq[row][column].yoff _ yoff; ReleaseLock[data]; }; GetRowsAndColumns: PUBLIC PROC [table: VTable] RETURNS [rows, columns: NAT] = { data: TableData _ NARROW[table.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; rows _ data.rows; columns _ data.columns; ReleaseLock[data]; }; ExchangeRows: PUBLIC PROC [table: VTable, row1,row2: NAT _ 0] = { data: TableData _ NARROW[table.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF row1 >= data.rows OR row2 >= data.rows THEN ReleaseAndFault[data]; data.installed _ FALSE; {temp: RowData _ data.rowSeq[row1]; data.rowSeq[row1] _ data.rowSeq[row2]; data.rowSeq[row2] _ temp}; ReleaseLock[data]; }; ExchangeColumns: PUBLIC PROC [table: VTable, column1,column2: NAT _ 0] = { data: TableData _ NARROW[table.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; IF column1 >= data.columns OR column2 >= data.columns THEN ReleaseAndFault[data]; data.installed _ FALSE; FOR row: NAT IN [0..data.rows) DO temp: Entry _ data.rowSeq[row][column1]; data.rowSeq[row][column1] _ data.rowSeq[row][column2]; data.rowSeq[row][column2] _ temp; ENDLOOP; ReleaseLock[data]; }; TablePainter: ViewerClasses.PaintProc = TRUSTED { table: VTable _ self; data: TableData _ NARROW[self.data]; IF data = NIL OR NOT AcquireLock[data, TRUE] THEN RETURN; {xPos: INTEGER _ 0; xLim: INTEGER _ data.width; yPos: INTEGER _ 0; yLim: INTEGER _ data.height; rows: NAT _ data.rows; columns: NAT _ data.columns; xWidth: CARDINAL _ data.xRuleWidth; yWidth: CARDINAL _ data.yRuleWidth; Graphics.SetColor[context, Graphics.black]; FOR row: NAT IN [0..rows] DO yLast: INTEGER _ yPos + xWidth; xPos _ 0; FOR col: NAT IN [0..columns) DO prev: Border _ IF row = 0 THEN NullBorder ELSE data.rowSeq[row-1][col].border; this: Border _ IF row = rows THEN NullBorder ELSE data.rowSeq[row][col].border; xLast: INTEGER _ xPos + data.colWidths[col] + yWidth + yWidth; IF prev.down OR this.up THEN {-- include this edge Graphics.DrawBox[context, [xPos, yPos, xLast, yLast]]}; xPos _ xLast - yWidth; ENDLOOP; IF row = rows THEN EXIT; yPos _ yLast + data.rowWidths[row]; ENDLOOP; xPos _ 0; FOR col: NAT IN [0..columns] DO xLast: INTEGER _ xPos + yWidth; yPos _ 0; FOR row: NAT IN [0..rows) DO prev: Border _ IF col = 0 THEN NullBorder ELSE data.rowSeq[row][col-1].border; this: Border _ IF col = columns THEN NullBorder ELSE data.rowSeq[row][col].border; yLast: INTEGER _ yPos + data.rowWidths[row] + xWidth + xWidth; IF prev.right OR this.left THEN {-- include this edge Graphics.DrawBox[context, [xPos, yPos, xLast, yLast]]}; yPos _ yLast - yWidth; ENDLOOP; IF col = columns THEN EXIT; xPos _ xLast + data.colWidths[col]; ENDLOOP; data.request _ 0; ReleaseLock[data]; }}; TableDestroy: ViewerClasses.DestroyProc = { data: TableData _ NARROW[self.data]; IF data = NIL OR NOT AcquireLock[data] THEN RETURN; data.live _ FALSE; self.data _ NIL; ReleaseLock[data]; }; MakeViewerClass: PROC = TRUSTED { vTableClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec]; vTableClass.icon _ tool; vTableClass.paint _ TablePainter; vTableClass.destroy _ TableDestroy; vTableClass.coordSys _ top; ViewerOps.RegisterViewerClass[$VTable, vTableClass] }; ReleaseAndFault: PROC [data: TableData] = TRUSTED { ReleaseLock[data]; ERROR Runtime.BoundsFault}; DummyButtonProc: Buttons.ButtonProc = TRUSTED { UserTerminal.BlinkDisplay[] }; instance: Viewer _ NIL; TestInstance: PROC [useText: BOOL _ TRUE] = { instance _ Create[3, 3, "test"]; SetTableEntry[instance, 0, 0, "0-0 button *", $Button, DummyButtonProc]; IF useText THEN SetTableEntry[instance, 0, 1, "0-1 text **", $Text]; SetTableEntry[instance, 0, 2, "0-2 label ***"]; SetTableEntry[instance, 1, 0, "1-0 label ****"]; SetTableEntry[instance, 1, 1, "1-1 button *", $Button, DummyButtonProc]; IF useText THEN SetTableEntry[instance, 1, 2, "1-2 text **", $Text]; IF useText THEN SetTableEntry[instance, 2, 0, "2-0 text ***", $Text]; SetTableEntry[instance, 2, 1, "2-1 label ****"]; SetTableEntry[instance, 2, 2, "2-2 button ****", $Button, DummyButtonProc]; SetEntryBorder[instance, 2, 2, NullBorder]; Install[instance] }; MakeViewerClass[] END. vVTablesImpl.mesa Russ Atkinson, April 6, 1983 6:08 pm acquire the lock on the data if requireInstalled then we wait for installation as well return the live flag if FALSE is returned, the lock is not held release the data lock if install, then set the installed flag as well then notify everyone else that the world is OK sets the number of rows and columns can be used to either grow or shrink the table determine the row spacing retrieve the viewer at the given row and column swap the given table entries get the current table entry at the given row and column get the current table entry at the given row and column get the current offsets of the given entry set the current table border at the given row and column reinstallation is required exchange the two given rows re-installation will be necessary exchange the two given columns re-installation will be necessary paint and destroy procedures [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOLEAN] just paint the rules for the table the children will be painted after us paint the horizontals paint the verticals [self: Viewer] other procedures Êò˜J˜Jšœ™Jšœ$™$J˜šÏk ˜ šœœ˜J˜/—šœ ˜J˜—šœ˜ J˜—šœœ˜ Jšœ œ˜—šœ˜ J˜—šœ ˜J˜—šœ˜ J˜-—šœ˜J˜?—šœ ˜˜J˜<——šœ˜ J˜)J˜——š œ œœœœ˜;šœ2˜9J˜—Jšœ˜Jšœœœ˜J˜Jšœœ˜%J˜Jšœ œœ˜$šœœ˜š œœ˜šœœ˜Jšœœœ˜-Jšœœœ˜Jšœ œ˜Jšœ œ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœ œ˜J˜J˜———Jšœœœ ˜šœ œ˜Jšœ œœœ ˜*J˜—Jšœ œœ ˜ Jš œ œœ œœœ ˜Cšœœ˜šœœ˜"J˜/——Jšœ œœ˜1Jšœœœ˜J˜JšœÏc˜1Jšœž˜1J˜Jšœœž)˜>Jšœœž)˜>J˜Jšœ œœ˜"Jš œ œœ œœœœ˜DJ˜šÏn œœ˜Jš œœœœœ˜šœ œ ˜šœž˜J˜7——J˜Jšœ˜—Jšœ œœ˜J˜#Jšœ˜ —J™Jšœ™J™J˜ šœœœœ˜ Jšœœ˜J˜ šœœœ œ˜˜šœ ˜Jšœ ˜J˜——˜šœ˜Jšœ ˜Jšœ˜"——Jšœœ0˜>šœ œ ˜šœž˜J˜7——J˜Jšœ˜ —Jšœœœ˜J˜#Jšœ˜—J˜J˜J˜J˜—˜+Jšœ™Jšœœ ˜$Jš œœœœœœ˜3Jšœ œ˜Jšœ œ˜J˜J˜J˜—Jšœ™J˜šŸœœœ˜!Jšœ)œ˜KJ˜J˜!J˜$J˜J˜3J˜J˜—šŸœœœ˜3J˜Jšœ˜J˜—šœ&œ˜/J˜J˜J˜—Jšœœ˜šŸ œœ œœ˜-J˜!J˜Išœ ˜J˜5—J˜0J˜0J˜Išœ ˜J˜5—šœ ˜J˜6—J˜0J˜KJ˜,J˜J˜J˜—J˜J˜Jšœ˜J˜J˜J˜——…—=V|