PWImpl.mesa
Copyright Ó 1985, 1987 by Xerox Corporation. All rights reserved.
Created by: Monier, June 24, 1985 4:30:52 pm PDT
Louis Monier August 22, 1985 12:39:11 pm PDT
Bertrand Serlet April 24, 1988 10:49:33 pm PDT
Don Curry September 11, 1987 6:53:38 pm PDT
Jean-Marc Frailong November 3, 1987 2:29:38 pm PST
DIRECTORY
Ascii, BasicTime,
CD, CDBasics, CDCells, CDCommandOps, CDDebug, CDDefaultProcs, CDDirectory, CDEnvironment, CDEvents, CDGenerate, CDInstances, CDIO, CDOps, CDOpsExtras, CDPrivate, CDProperties, CDSequencer,
GList, IO,
PW,
RefTab, Rope, RopeList,
TerminalIO, TokenIO;
PWImpl: CEDAR PROGRAM
IMPORTS Ascii, BasicTime, CD, CDBasics, CDCells, CDCommandOps, CDDebug, CDDefaultProcs, CDDirectory, CDEnvironment, CDEvents, CDGenerate, CDInstances, CDIO, CDOps, CDOpsExtras, CDPrivate, CDProperties, CDSequencer, GList, IO, RefTab, Rope, RopeList, TerminalIO, TokenIO
EXPORTS PW
SHARES CDDirectory =
BEGIN OPEN PW;
Abuts
abutXClass: PUBLIC CD.ObjectClass ← CD.RegisterObjectClass[$AbutX, [
quickDrawMe: QuickDrawAbut,
drawMe: DrawAbut,
internalRead: ReadAbutX,
internalWrite: WriteAbut,
interestRect: AbutInterestRect
]];
abutYClass: PUBLIC CD.ObjectClass ← CD.RegisterObjectClass[$AbutY, [
quickDrawMe: QuickDrawAbut,
drawMe: DrawAbut,
internalRead: ReadAbutY,
internalWrite: WriteAbut,
interestRect: AbutInterestRect
]];
AbutSpecific: TYPE = REF AbutSpecificRec;
AbutSpecificRec: TYPE = RECORD [
ir: CD.Rect,   -- for algorithmic efficiency
subObjects: SEQUENCE size: NAT OF CD.Object
];
CreateNewAbutX: PUBLIC CreateAbutProc = {
abutSpecific: AbutSpecific ← NEW [AbutSpecificRec[GList.Length[subObjects]]];
new: CD.Object;
FOR i: NAT IN [0 .. abutSpecific.size) DO
abutSpecific[i] ← subObjects.first; subObjects ← subObjects.rest;
ENDLOOP;
IF abutSpecific.size#0 THEN {
iry: INT = CD.InterestSize[abutSpecific[0]].y;
FOR i: NAT IN [1 .. abutSpecific.size) DO
IF CD.InterestSize[abutSpecific[i]].y#iry THEN ERROR; -- incompatible y dimension for an abutX
ENDLOOP;
};
newAbut ← NEW [CD.ObjectRep ← [class: abutXClass, specific: abutSpecific, properties: props]];
new ← ExpandAbut[newAbut].new;
newAbut.bbox ← new.bbox;
abutSpecific.ir ← CD.InterestRect[new];
SetName[newAbut, name];
};
CreateNewAbutY: PUBLIC CreateAbutProc = {
abutSpecific: AbutSpecific ← NEW [AbutSpecificRec[GList.Length[subObjects]]];
new: CD.Object;
FOR i: NAT IN [0 .. abutSpecific.size) DO
abutSpecific[i] ← subObjects.first; subObjects ← subObjects.rest;
ENDLOOP;
IF abutSpecific.size#0 THEN {
irx: INT = CD.InterestSize[abutSpecific[0]].x;
FOR i: NAT IN [1 .. abutSpecific.size) DO
IF CD.InterestSize[abutSpecific[i]].x#irx THEN ERROR; -- incompatible x dimension for an abutY
ENDLOOP;
};
newAbut ← NEW [CD.ObjectRep ← [class: abutYClass, specific: abutSpecific, properties: props]];
new ← ExpandAbut[newAbut].new;
newAbut.bbox ← new.bbox;
abutSpecific.ir ← CD.InterestRect[new];
SetName[newAbut, name];
};
EnumerateSubObjects: PUBLIC PROC [abut: CD.Object, eachSubObject: EachSubObjectProc] RETURNS [quit: BOOLFALSE] = {
abutSpecific: AbutSpecific = NARROW [abut.specific];
pos: CD.Position ← [0, 0];
FOR i: NAT IN [0 .. abutSpecific.size) DO
quit ← eachSubObject[abutSpecific[i], pos];
IF quit THEN RETURN;
SELECT abut.class FROM
abutXClass  => pos.x ← pos.x + CD.InterestSize[abutSpecific[i]].x;
abutYClass  => pos.y ← pos.y + CD.InterestSize[abutSpecific[i]].y;
ENDCASE  => ERROR;
ENDLOOP;
};
CountSubObjects: PUBLIC PROC [abut: CD.Object] RETURNS [size: NAT] = {
abutSpecific: AbutSpecific = NARROW [abut.specific];
size ← abutSpecific.size;
};
Takes an abut and returns the cell which is equivalent to this abut. Cell resulting always belongs to the from design if me belongs to the from design. It is included in the to design if to#NIL
ExpandAbut: CDDirectory.ExpandProc = {
EachSubObject: EachSubObjectProc = {
instances ← CONS [CDInstances.NewInst[subObject, [CDBasics.SubPoints[pos, CD.InterestBase[subObject]]]], instances];
};
instances: LIST OF CD.Instance;
[] ← EnumerateSubObjects[me, EachSubObject];
new ← CreateCell[instances: instances, name: Name[me]];
IF into#NIL THEN [] ← CDDirectory.Include[into, new, Name[me]];
};
DrawAbut: CD.DrawProc ~ {
mapClip: CD.Rect ← CDBasics.DeMapRect[pr.interestClip, trans].itemInCell;
clipping boundary in object coordinates
EachSubObject: EachSubObjectProc ~ {
pos ← CDBasics.SubPoints[pos, CD.InterestBase[subObject]];
IF CDBasics.Intersect[mapClip, CDBasics.MoveRect[subObject.bbox, pos]]
THEN pr.drawChild[pr, subObject, [off: CDBasics.MapPoint[pos, trans], orient: trans.orient]];
quit pr.stopFlag^;
};
[] ← EnumerateSubObjects[ob, EachSubObject];
};
abutSimplification: REAL ← 100.0; -- average size below which things get simplified
QuickDrawAbut: CD.DrawProc ~ {
mapClip: CD.Rect ← CDBasics.DeMapRect[pr.interestClip, trans].itemInCell;
clipping boundary in object coordinates
EachSubObject: EachSubObjectProc ~ {
pos ← CDBasics.SubPoints[pos, CD.InterestBase[subObject]];
IF CDBasics.Intersect[mapClip, CDBasics.MoveRect[subObject.bbox, pos]]
THEN subObject.class.quickDrawMe[pr, subObject, [off: CDBasics.MapPoint[pos, trans], orient: trans.orient]];
quit ← pr.stopFlag^;
};
IF DrawObjectOutline[pr, ob, trans, abutSimplification] THEN RETURN; -- object outlined
[] ← EnumerateSubObjects[ob, EachSubObject];
};
EnumerateChildObjectsAbut: CDDirectory.EnumerateChildObjectsProc = {
EachSubObject: EachSubObjectProc = {quit ← proc[subObject, data]};
quit ← EnumerateSubObjects[me, EachSubObject];
};
ReplaceDirectChildsAbut: CDDirectory.ReplaceDChildsProc = {
bbox: CD.Rect ← me.bbox;
abutSpecific: AbutSpecific = NARROW [me.specific];
FOR i: NAT IN [0 .. abutSpecific.size) DO
element: CD.Object = abutSpecific[i];
FOR l: CDDirectory.ReplaceList ← replace, l.rest WHILE l#NIL DO
IF l.first.old=element THEN {
IF l.first.trans#[] THEN ERROR;
IF (IF me.class=abutXClass THEN CD.InterestSize[element].y#CD.InterestSize[l.first.new].y ELSE CD.InterestSize[element].x#CD.InterestSize[l.first.new].x) THEN ERROR;
changed ← TRUE;
abutSpecific[i] ← l.first.new;
};
ENDLOOP;
ENDLOOP;
IF NOT changed THEN RETURN;
me.bbox ← ExpandAbut[me].new.bbox;
changed ← bbox = me.bbox;
CDDirectory.PropagateResize[design, me];
};
AbutInterestRect: CD.RectProc = {
abutSpecific: AbutSpecific = NARROW [ob.specific];
RETURN [abutSpecific.ir];
};
WriteAbut: CD.InternalWriteProc = {
data: AbutSpecific = NARROW [ob.specific];
CDIO.WriteRect[h, ob.bbox];
TokenIO.WriteInt[h, data.size];
CDIO.WriteRect[h, data.ir];
FOR x: NAT IN [0 .. data.size) DO CDIO.WriteObject[h, data[x]] ENDLOOP;
};
ReadAbutX: CD.InternalReadProc = {
bbox: CD.Rect ← CDIO.ReadRect[h];
size: NAT ← TokenIO.ReadInt[h];
data: AbutSpecific ← NEW [AbutSpecificRec[size]];
ob: CD.Object ← NEW [CD.ObjectRep ← [class: abutXClass, specific: data, bbox: bbox]];
data.ir ← CDIO.ReadRect[h];
FOR x: NAT IN [0 .. size) DO data[x] ← CDIO.ReadObject[h] ENDLOOP;
RETURN [ob];
};
ReadAbutY: CD.InternalReadProc = {
bbox: CD.Rect ← CDIO.ReadRect[h];
size: NAT ← TokenIO.ReadInt[h];
data: AbutSpecific ← NEW [AbutSpecificRec[size]];
ob: CD.Object ← NEW [CD.ObjectRep ← [class: abutYClass, specific: data, bbox: bbox]];
data.ir ← CDIO.ReadRect[h];
FOR x: NAT IN [0 .. size) DO data[x] ← CDIO.ReadObject[h] ENDLOOP;
RETURN [ob];
};
Abuts: Sugared
AbutX: PUBLIC PROC [t1, t2, t3, t4, t5, t6: CD.Object ← NIL] RETURNS [obj: CD.Object] = {
RETURN [AbutListX[LIST[t1, t2, t3, t4, t5, t6]]];
};
AbutY: PUBLIC PROC [t1, t2, t3, t4, t5, t6: CD.Object ← NIL] RETURNS [obj: CD.Object] = {
RETURN [AbutListY[LIST[t1, t2, t3, t4, t5, t6]]];
};
AbutListX: PUBLIC PROC [objects: LIST OF CD.Object, name: Rope.ROPENIL, props: CD.PropList ← NIL] RETURNS [obj: CD.Object] = {
newObjects: LIST OF CD.Object ← NIL;
WHILE objects#NIL DO
IF objects.first#NIL THEN newObjects ← CONS [objects.first, newObjects];
objects ← objects.rest;
ENDLOOP;
IF newObjects=NIL THEN RETURN [NIL];
obj ← CreateNewAbutX[NARROW [GList.Reverse[newObjects]], name, props];
};
AbutListY: PUBLIC PROC [objects: LIST OF CD.Object, name: Rope.ROPENIL, props: CD.PropList ← NIL] RETURNS [obj: CD.Object] = {
newObjects: LIST OF CD.Object ← NIL;
WHILE objects#NIL DO
IF objects.first#NIL THEN newObjects ← CONS [objects.first, newObjects];
objects ← objects.rest;
ENDLOOP;
IF newObjects=NIL THEN RETURN [NIL];
obj ← CreateNewAbutY[NARROW [GList.Reverse[newObjects]], name, props];
};
MapFunction: PUBLIC PROC [function: PROC [INT, INT] RETURNS [CD.Object], lx: INT ← 0, ux: INT, ly: INT ← 0, uy: INT] RETURNS [CD.Object] = {
rows: LIST OF CD.Object ← NIL;
FOR y: INT DECREASING IN [ly .. uy) DO
row: LIST OF CD.Object ← NIL;
FOR x: INT DECREASING IN [lx .. ux) DO row ← CONS [function[x, y], row] ENDLOOP;
rows ← CONS [AbutListX[row], rows];
ENDLOOP;
RETURN [AbutListY[rows]];
};
ArrayX: PUBLIC PROC [obj: CD.Object, nx: INT ← 1] RETURNS [CD.Object] = {
row: LIST OF CD.Object ← NIL;
FOR x: INT IN [0 .. nx) DO row ← CONS [obj, row] ENDLOOP;
RETURN [AbutListX[row]];
};
ArrayY: PUBLIC PROC [obj: CD.Object, ny: INT ← 1] RETURNS [CD.Object] = {
row: LIST OF CD.Object ← NIL;
FOR x: INT IN [0 .. ny) DO row ← CONS [obj, row] ENDLOOP;
RETURN [AbutListY[row]];
};
Array: PUBLIC PROC [obj: CD.Object, nx, ny: INT ← 1] RETURNS [new: CD.Object] = {
RETURN [ArrayY[ArrayX[obj, nx], ny]];
};
Rotations
rotationClass: PUBLIC CD.ObjectClass ← RegisterClass[objectType: $Rotation, expand: ExpandRotation, enumerateChildObjects: EnumerateChildObjectsRotation, replaceDirectChilds: ReplaceDirectChildsRotation, interestRect: RotationInterestRect];
CreateRotation: PUBLIC PROC [obj: CD.Object, orientation: CD.Orientation, name: Rope.ROPENIL, props: CD.PropList ← NIL] RETURNS [rotation: CD.Object] = {
trans: CD.Transformation = [[0, 0], orientation];
IF obj=NIL THEN RETURN [NIL];
rotation ← NEW [CD.ObjectRep ← [
class: rotationClass,
bbox: CDBasics.MapRect[obj.bbox, trans],
specific: NEW [RotationSpecificRec ← [ir: CDBasics.MapRect[CD.InterestRect[obj], trans], obj: obj, orientation: orientation]],
properties: props
]];
SetName[rotation, name];
};
ExpandRotation: CDDirectory.ExpandProc = {
specific: RotationSpecific ← NARROW [me.specific];
new ← CreateCell[
instances: LIST [CDInstances.NewInst[specific.obj, [[0, 0], specific.orientation]]],
name: Name[me]
];
};
EnumerateChildObjectsRotation: CDDirectory.EnumerateChildObjectsProc = {
specific: RotationSpecific ← NARROW [me.specific];
quit ← proc[specific.obj, data];
};
ReplaceDirectChildsRotation: CDDirectory.ReplaceDChildsProc = {
specific: RotationSpecific ← NARROW [me.specific];
bbox: CD.Rect ← me.bbox;
FOR l: CDDirectory.ReplaceList ← replace, l.rest WHILE l#NIL DO
IF l.first.old=specific.obj THEN {
IF l.first.trans#[] THEN ERROR;
changed ← TRUE; specific.obj ← l.first.new;
};
ENDLOOP;
IF ~changed THEN RETURN;
me.bbox ← CDBasics.MapRect[specific.obj.bbox, [[0, 0], specific.orientation]];
changed ← bbox = me.bbox;
CDDirectory.PropagateResize[design, me];
};
RotationInterestRect: CD.RectProc = {
specific: RotationSpecific ← NARROW [ob.specific];
RETURN [specific.ir];
};
Indirect
indirectClass: PUBLIC CD.ObjectClass ← RegisterClass[objectType: $Indirect, expand: ExpandIndirect, enumerateChildObjects: EnumerateChildObjectsIndirect, replaceDirectChilds: ReplaceDirectChildsIndirect];
In the future, add an IR proc, and maybe a Draw Proc?
CreateIndirect: PUBLIC PROC [sourceObject: CD.Object, name: Rope.ROPENIL, props: CD.PropList ← NIL] RETURNS [indirectObject: CD.Object] = {
IF sourceObject=NIL THEN RETURN [NIL];
indirectObject ← NEW [CD.ObjectRep ← [class: indirectClass, bbox: sourceObject.bbox, specific: sourceObject, properties: props]];
SetName[indirectObject, name];
};
ExpandIndirect: CDDirectory.ExpandProc = {new ← NARROW [me.specific]};
EnumerateChildObjectsIndirect: CDDirectory.EnumerateChildObjectsProc = {
quit ← proc[NARROW [me.specific], data];
};
ReplaceDirectChildsIndirect: CDDirectory.ReplaceDChildsProc = {
indirect: CD.Object = NARROW [me.specific];
bbox: CD.Rect ← me.bbox;
FOR l: CDDirectory.ReplaceList ← replace, l.rest WHILE l#NIL DO
IF l.first.old=indirect THEN {
IF l.first.trans#[] THEN ERROR;
changed ← TRUE; me.specific ← l.first.new;
};
ENDLOOP;
IF ~changed THEN RETURN;
me.bbox ← indirect.bbox;
changed ← bbox = me.bbox;
CDDirectory.PropagateResize[design, me];
};
Lazy
lazyClass: PUBLIC CD.ObjectClass ← RegisterClass[objectType: $Lazy, expand: ExpandLazy, interestRect: LazyIR];
LazyData: TYPE = REF LazyDataRec;
LazyDataRec: TYPE = RECORD [info: REF, createProc: CreateProc, ir: CD.Rect];
CreateLazy: PUBLIC PROC [info: REF, createProc: CreateProc, bbox, ir: CD.Rect, name: Rope.ROPENIL, props: CD.PropList ← NIL] RETURNS [newLazy: CD.Object] = {
newLazy ← NEW [CD.ObjectRep ← [
class: lazyClass,
specific: NEW [LazyDataRec ← [info: info, createProc: createProc, ir: ir]],
properties: props
]];
newLazy.bbox ← bbox;
SetName[newLazy, name];
};
LazyIR: CD.RectProc = {lazyData: LazyData ← NARROW [ob.specific]; RETURN [lazyData.ir]};
ExpandLazy: CDDirectory.ExpandProc = {
lazyData: LazyData ← NARROW [me.specific];
new ← lazyData.createProc[lazyData.info];
IF CD.InterestRect[new]#lazyData.ir OR new.bbox#me.bbox THEN ERROR;
IF into#NIL THEN [] ← CDDirectory.Include[into, new, Name[me]];
};
Tilings
tilingClass: PUBLIC CD.ObjectClass ← CD.RegisterObjectClass[$Tiling, [
quickDrawMe: QuickDrawTiling,
drawMe: DrawTiling,
internalRead: ReadTiling,
internalWrite: WriteTiling,
interestRect: TilingInterestRect,
description: "tiling object"
]];
TilingData: TYPE = REF TilingDataRec;
TilingDataRec: TYPE = RECORD [
ir: CD.Rect,    -- interest rect
c: SEQUENCE sizeY: NAT OF TileLines  -- tiles
];
Implementation detail: we could have one sequence instead of a sequence of sequence, it would be simpler. But it would allow only a limited number of tiles, while this raises the limit. Once Cedar changes, this can be changed!
TileLines: TYPE = REF TileLinesRec;
TileLinesRec: TYPE = RECORD [c: SEQUENCE sizeX: NAT OF CD.Object];
CreateTiling: PUBLIC PROC [sizeX, sizeY: NAT, tiles: PROC [x, y: NAT] RETURNS [CD.Object], name: Rope.ROPENIL, props: CD.PropList ← NIL] RETURNS [tiling: CD.Object] = {
new: CD.Object;
data: TilingData ← NEW [TilingDataRec[sizeY]];
Checking all right
IF sizeX=0 OR sizeY=0 THEN ERROR;
FOR y: NAT IN [0 .. sizeY) DO
height: INTCD.InterestSize[tiles[0, y]].y;
FOR x: NAT IN [1 .. sizeX) DO
IF CD.InterestSize[tiles[x, y]].y#height THEN ERROR; -- constraint of same height within a row not respected!
ENDLOOP;
ENDLOOP;
FOR x: NAT IN [0 .. sizeX) DO
width: INTCD.InterestSize[tiles[x, 0]].x;
FOR y: NAT IN [1 .. sizeY) DO
IF CD.InterestSize[tiles[x, y]].x#width THEN ERROR; -- constraint of same width within a column not respected!
ENDLOOP;
ENDLOOP;
Building the array!
FOR y: NAT IN [0 .. sizeY) DO
line: TileLines ← NEW [TileLinesRec[sizeX]];
FOR x: NAT IN [0 .. sizeX) DO
line[x] ← tiles[x, y];
ENDLOOP;
data[y] ← line;
ENDLOOP;
tiling ← NEW [CD.ObjectRep ← [class: tilingClass, specific: data, properties: props]];
new ← ExpandTiling[tiling].new;
tiling.bbox ← new.bbox;
data.ir ← CD.InterestRect[new];
SetName[tiling, name];
};
EnumerateTiles: PUBLIC PROC [tiling: CD.Object, eachTile: EachTileProc] RETURNS [quit: BOOLFALSE] = {
data: TilingData = NARROW [tiling.specific];
pos: CD.Position ← [0, 0];
FOR y: NAT IN [0 .. data.sizeY) DO
line: TileLines ← data[y];
pos.x ← 0;
FOR x: NAT IN [0 .. line.sizeX) DO
quit ← eachTile[x, y, line[x], pos];
IF quit THEN RETURN;
pos.x ← pos.x + CD.InterestSize[line[x]].x;
ENDLOOP;
pos.y ← pos.y + CD.InterestSize[line[0]].y;
ENDLOOP;
};
GetTilingSize: PUBLIC PROC [tiling: CD.Object] RETURNS [sizeX, sizeY: NAT] = {
data: TilingData = NARROW [tiling.specific];
sizeY ← data.sizeY;
sizeX ← data[0].sizeX;
};
GetTile: PUBLIC PROC [tiling: CD.Object, x, y: NAT] RETURNS [tile: CD.Object] = {
data: TilingData = NARROW [tiling.specific];
tile ← data[y][x];
};
ExpandTiling: CDDirectory.ExpandProc = {
EachTile: EachTileProc = {
instances ← CONS [CDInstances.NewInst[tile, [CDBasics.SubPoints[pos, CD.InterestBase[tile]]]], instances];
};
instances: LIST OF CD.Instance;
[] ← EnumerateTiles[me, EachTile];
new ← CreateCell[instances: instances, name: Name[me]];
IF into#NIL THEN [] ← CDDirectory.Include[into, new, Name[me]];
};
DrawTiling: CD.DrawProc = {
mapClip: CD.Rect ← CDBasics.DeMapRect[pr.interestClip, trans].itemInCell;
clipping boundary in object coordinates
EachTile: EachTileProc = {
pos ← CDBasics.SubPoints[pos, CDBasics.BaseOfRect[CD.InterestRect[tile]]];
IF CDBasics.Intersect[mapClip, CDBasics.MoveRect[tile.bbox, pos]]
THEN pr.drawChild[pr, tile, [off: CDBasics.MapPoint[pos, trans], orient: trans.orient]];
quit pr.stopFlag^;
};
[] ← EnumerateTiles[ob, EachTile];
};
tilingSimplification: REAL ← 100.0; -- average size below which things get simplified
QuickDrawTiling: CD.DrawProc = {
mapClip: CD.Rect ← CDBasics.DeMapRect[pr.interestClip, trans].itemInCell;
clipping boundary in object coordinates
EachTile: EachTileProc = {
pos ← CDBasics.SubPoints[pos, CDBasics.BaseOfRect[CD.InterestRect[tile]]];
IF CDBasics.Intersect[mapClip, CDBasics.MoveRect[tile.bbox, pos]]
THEN tile.class.quickDrawMe[pr, tile, [off: CDBasics.MapPoint[pos, trans], orient: trans.orient]];
quit ← pr.stopFlag^;
};
IF DrawObjectOutline[pr, ob, trans, tilingSimplification] THEN RETURN; -- object outlined
[] ← EnumerateTiles[ob, EachTile];
};
EnumerateTilingChildObjects: CDDirectory.EnumerateChildObjectsProc = {
EachTile: EachTileProc = {quit ← proc[tile, data]};
quit ← EnumerateTiles[me, EachTile];
};
ReplaceTilingDirectChilds: CDDirectory.ReplaceDChildsProc = {
EachTileCheckNoChange: EachTileProc = {
FOR l: CDDirectory.ReplaceList ← replace, l.rest WHILE l#NIL DO
quit ← l.first.old=tile;
ENDLOOP;
};
changed ← EnumerateTiles[me, EachTileCheckNoChange];
IF NOT changed THEN RETURN;
ERROR;
};
TilingInterestRect: CD.RectProc = {
data: TilingData = NARROW [ob.specific];
RETURN [data.ir];
};
WriteTiling: CD.InternalWriteProc = {
data: TilingData = NARROW [ob.specific];
CDIO.WriteRect[h, ob.bbox];
TokenIO.WriteInt[h, data[0].sizeX];
TokenIO.WriteInt[h, data.sizeY];
CDIO.WriteRect[h, data.ir];
FOR y: NAT IN [0 .. data.sizeY) DO
line: TileLines ← data[y];
FOR x: NAT IN [0 .. line.sizeX) DO
CDIO.WriteObject[h, data[y][x]];
ENDLOOP;
ENDLOOP;
};
ReadTiling: CD.InternalReadProc = {
bbox: CD.Rect ← CDIO.ReadRect[h];
sizeX: NAT ← TokenIO.ReadInt[h];
sizeY: NAT ← TokenIO.ReadInt[h];
data: TilingData ← NEW [TilingDataRec[sizeY]];
ob: CD.Object ← NEW [CD.ObjectRep ← [class: tilingClass, specific: data, bbox: bbox]];
data.ir ← CDIO.ReadRect[h];
FOR y: NAT IN [0 .. sizeY) DO
line: TileLines ← NEW [TileLinesRec[sizeX]];
FOR x: NAT IN [0 .. sizeX) DO
line[x] ← CDIO.ReadObject[h];
ENDLOOP;
data[y] ← line;
ENDLOOP;
RETURN [ob];
};
Implementors goodies
CreateCell: PUBLIC PROC [instances: CD.InstanceList, ir: CD.Rect ← [0,0,-1,-1], name: Rope.ROPENIL, props: CD.PropList ← NIL] RETURNS [cell: CD.Object]= {
cell ← CDCells.CreateCell[il: instances, ir: ir];
cell.properties ← props;
CDCells.ToSequenceMode[cell];
SetName[cell, name];
};
SetName: PUBLIC PROC [obj: CD.Object, name: Rope.ROPE] = {
CDProperties.PutObjectProp[obj, $Describe, name];
};
Name: PUBLIC PROC [obj: CD.Object] RETURNS [name: Rope.ROPE] = {
SearchEachDesign: CDPrivate.DesignEnumerator = {
name ← CDDirectory.Name[obj, design];
quit ← name#NIL;
};
name ← NARROW [CDProperties.GetObjectProp[obj, $Describe]];
IF name=NIL THEN [] ← CDPrivate.EnumDesigns[SearchEachDesign];
};
Get: PUBLIC PROC [design: CD.Design, name: Rope.ROPE] RETURNS [obj: CD.Object] = {
obj ← CDDirectory.Fetch[design, name].object;
IF obj=NIL THEN {TerminalIO.PutF["*** Object %g not found in the design.\n", IO.rope[name]]; ERROR};
IF NOT CDOpsExtras.MakeImmutableAndSetNameHints[ob: obj, design: design] THEN TerminalIO.PutF["*** Object %g could not be made immutable.\n", IO.rope[name]];
};
Flatten: PUBLIC PROC [cell: CD.Object] RETURNS [new: CD.Object] = {
TopEnumerate: CDCells.InstEnumerator = {
obj: CD.Object ← Flatten[inst.ob];
IF NOT CDCells.IsCell[obj] OR CDProperties.GetObjectProp[obj, $DontFlatten]#NIL
THEN instances ← CONS [CDInstances.NewInst[obj, inst.trans, CDProperties.DCopyProps[inst.properties]], instances]
ELSE {
trans: CD.Transformation ← inst.trans;
InsideEnumerate: CDCells.InstEnumerator = {
instances ← CONS [CDInstances.Composed[inst, trans], instances];
};
[] ← CDCells.EnumerateInstances[obj, InsideEnumerate];
};
};
instances: CD.InstanceList ← NIL;
IF CDProperties.GetObjectProp[cell, $DontFlatten]#NIL THEN RETURN [cell];
IF NOT CDCells.IsCell[cell] THEN {
new ← CDDirectory.Expand1[cell].new;
RETURN [IF new=NIL THEN cell ELSE Flatten[new]];
};
[] ← CDCells.EnumerateInstances[cell, TopEnumerate];
new ← CreateCell[instances: instances, ir: CD.InterestRect[cell], name: Name[cell]];
CDProperties.CopyProps[cell.properties, new];
};
Mechanism for flushing during edits
propertiesToFlushOnEdit: LIST OF ATOMNIL;
childToParents: RefTab.Ref ← RefTab.Create[];
Having a RefTab (of the right size) helps avoiding frequent allocations.
AddChildParent: PROC [child, parent: CD.Object] = {
parents: LIST OF CD.Object ← NARROW [RefTab.Fetch[childToParents, child].val];
IF GList.Member[parent, parents] THEN RETURN;
parents ← CONS [parent, parents];
[] ← RefTab.Store[childToParents, child, parents];
};
FlushPropertiesAfterReplace: CDEvents.EventProc = {
BuildTable: CDDirectory.EachEntryAction = {
EachChild: CDDirectory.EachObjectProc = {
IF NOT me.immutable THEN AddChildParent[me, ob];
};
sch: Rope.ROPENARROW [CDProperties.GetObjectProp[ob, $IconFor]];
schOb: CD.Object ← IF sch#NIL THEN CDDirectory.Fetch[design, sch].object ELSE NIL;
we include icons of sch
IF schOb#NIL THEN AddChildParent[schOb, ob];
In case obj is a mask, the corresponding sch (if any) "depends" on it. That's a hack!
schOb ← IF Rope.Match["*.mask", name] THEN CDDirectory.Fetch[design, Rope.Replace[base: name, start: Rope.Length[name]-4, with: "sch"]].object ELSE NIL;
IF schOb#NIL THEN AddChildParent[ob, schOb];
We add all the children
[] ← CDDirectory.EnumerateChildObjects[ob, EachChild];
};
changedObjects: LIST OF CD.Object ← LIST [NARROW [x]];
foundANewOne: BOOLTRUE;
flushedObjects: Rope.ROPENIL;
Slightly wrong here, because we miss the objects which are NOT in the directory (e.g. unnamed cells)
[] ← CDDirectory.Enumerate[design, BuildTable];
WHILE foundANewOne DO
foundANewOne ← FALSE;
FOR list: LIST OF CD.Object ← changedObjects, list.rest WHILE list#NIL DO
FOR parents: LIST OF CD.Object ← NARROW [RefTab.Fetch[childToParents, list.first].val], parents.rest WHILE parents#NIL DO
IF GList.Member[parents.first, changedObjects] THEN LOOP;
changedObjects ← CONS [parents.first, changedObjects]; foundANewOne ← TRUE;
ENDLOOP;
ENDLOOP;
ENDLOOP;
FOR list: LIST OF CD.Object ← changedObjects, list.rest WHILE list#NIL DO
flushed: BOOLFALSE;
FOR props: LIST OF ATOM ← propertiesToFlushOnEdit, props.rest WHILE props#NIL DO
IF CDProperties.GetObjectProp[list.first, props.first]=NIL THEN LOOP;
flushed ← TRUE;
CDProperties.PutObjectProp[list.first, props.first, NIL];
ENDLOOP;
IF flushed THEN flushedObjects ← Rope.Cat[flushedObjects, " ", CDDirectory.Name[list.first, design]];
ENDLOOP;
IF flushedObjects#NIL THEN TerminalIO.PutF["Flushed caches for %g.\n ", IO.rope[flushedObjects]];
RefTab.Erase[childToParents]; -- we clean up that table!
};
RegisterGenerator: PUBLIC PROC [generator: GeneratorProc, name: Rope.ROPE] = {
TerminalIO.PutF["Generator program %g %g.\n", IO.rope[name], IO.rope[
IF CDGenerate.Register[context: CDGenerate.AssertContext["PatchWork"], key: name, generator: generator, cache: FALSE] THEN "recorded" ELSE "overwritten"]];
};
RegisterProp: PUBLIC PROC [prop: ATOM, copy: BOOLFALSE, flushOnEdit: BOOLFALSE] RETURNS [sameAtom: ATOM] = {
[] ← CDProperties.RegisterProperty[prop, $PW];
CDProperties.InstallProcs[prop, [makeCopy: IF copy THEN CDProperties.CopyVal ELSE CDProperties.DontCopy]];
IF flushOnEdit THEN propertiesToFlushOnEdit ← CONS [prop, propertiesToFlushOnEdit];
sameAtom ← prop;
};
RegisterClass: PUBLIC PROC [objectType: ATOM, expand: CDDirectory.ExpandProc, enumerateChildObjects: CDDirectory.EnumerateChildObjectsProc ← NIL, replaceDirectChilds: CDDirectory.ReplaceDChildsProc ← NIL, interestRect: CD.RectProc ← NIL, drawMe, quickDrawMe, showMeSelected: CD.DrawProc ← NIL] RETURNS [objectClass: CD.ObjectClass] = {
dp: CDDirectory.DirectoryProcs ← [expand: expand];
objectClassRec: CD.ObjectClassRec ← [];
objectClassRec.showMeSelected ← CDDefaultProcs.ShowMeSelectedWithExpand;
objectClassRec.interestRect ← IF interestRect=NIL THEN CDDefaultProcs.InterestRectWithExpand ELSE interestRect;
objectClassRec.drawMe ← IF drawMe=NIL THEN CDDefaultProcs.DrawMe ELSE drawMe;
objectClassRec.quickDrawMe ← IF quickDrawMe=NIL THEN CDDefaultProcs.QuickDrawMe ELSE quickDrawMe;
objectClassRec.showMeSelected ← IF showMeSelected=NIL THEN CDDefaultProcs.ShowMeSelected ELSE showMeSelected;
objectClassRec.directoryProcs ← NIL; objectClassRec.xDesign ← FALSE;
IF enumerateChildObjects#NIL THEN dp.enumerateChildObjects ← enumerateChildObjects;
IF replaceDirectChilds#NIL THEN dp.replaceDirectChilds ← replaceDirectChilds;
objectClass ← CD.RegisterObjectClass[objectType, objectClassRec ! CD.Error => CONTINUE];
IF objectClass=NIL THEN objectClass ← CD.FetchObjectClass[objectType];
objectClass.directoryProcs ← CDDirectory.InstallDirectoryProcs[objectClass, dp ! CD.Error => CONTINUE];
};
DrawObjectOutline: PROC [pr: CD.DrawRef, ob: CD.Object, trans: CD.Transformation, factor: REAL] RETURNS [simplified: BOOL] ~ {
If the object may be simplified (small average size), display bbox and name if available and return TRUE, otherwise do nothing & return FALSE.
Factor is 50.0 for cells. Making it larger makes it harder to detail a class of objects.
This is for the use of Quick DrawProcs, in order to have a standardized clipping algorithm
The algorithm mimics the one implemented by CDCellsImpl
averageSize: INT = MAX [((ob.bbox.y2-ob.bbox.y1)+(ob.bbox.x2-ob.bbox.x1))/2, 1];
IF pr.stopFlag^ THEN RETURN [simplified: TRUE]; -- no drawing anyway...
simplified ← pr.scaleHint<(factor/averageSize) AND pr.scaleHint>0;
IF simplified THEN { -- we do the painting here
realIR: CD.Rect = CDBasics.MapRect[CD.InterestRect[ob], trans]; -- remapped interest rect
pr.drawOutLine[pr, realIR, CD.outlineLayer];
IF pr.scaleHint*averageSize>9 THEN { -- also paint name inside
name: Rope.ROPE ← CDDirectory.Name[ob, pr.design];
IF name=NIL THEN name ← NARROW [CDProperties.GetObjectProp[ob, $Describe]];
IF name#NIL THEN pr.drawComment[pr, realIR, name];
};
};
};
Utilities for debug
OpenDesign: PUBLIC PROC [fileName: Rope.ROPE] RETURNS [design: CD.Design] = {
design ← CDIO.ReadDesign[fileName, NIL, CDIO.GetWorkingDirectory[]];
};
Draw: PUBLIC PROC [obj: CD.Object, technologyName: ATOMNIL] RETURNS [design: CD.Design] = {
design ← CDDebug.Draw[obj, IF technologyName=NIL THEN $cmosB ELSE technologyName].dummyDesign;
design.mutability ← editable;
};
Commands
RunGenerator: PROC [comm: CDSequencer.Command] = {
inner: PROC = {
design: CD.Design ← comm.design;
pos: CD.Position ← comm.pos;
context: CDGenerate.Context ← CDGenerate.AssertContext["PatchWork"];
key: Rope.ROPE;
obj: CD.Object;
time: BasicTime.GMT ← BasicTime.Now[]; -- Start the stop watch
min, sec: INT;
TerminalIO.PutF["PatchWork menu selected\n"];
key ← CDGenerate.SelectOneOf[context, "select generate"];
IF Rope.IsEmpty[key] THEN {TerminalIO.PutF["no generator selected\n"]; RETURN};
-- Generate the object
obj ← CDGenerate.FetchNCall[context, design, key];
-- Now figure out how long it took to generate this wonderful piece of layout
sec ← BasicTime.Period[time, BasicTime.Now[]];
min ← sec/60; sec ← sec MOD 60;
TerminalIO.PutF["PW completed in "];
IF min#0 THEN TerminalIO.PutF["%g min ", IO.int[min]]; TerminalIO.PutF["%g sec\n", IO.int[sec]];
IF obj=NIL THEN {TerminalIO.PutF["No returned object\n"]; RETURN};
[] ← CDOps.IncludeObjectI[design, obj, pos];
};
Silly line, just because CD does not do the right thing!
CDEnvironment.DoWithWDir[CDEnvironment.GetWorkingDirectory[comm.design], inner];
};
ListIcons: PROC [comm: CDSequencer.Command] = {
EachEntry: CDDirectory.EachEntryAction = {
count ← count+1;
IF NOT Rope.Match["*.icon", name, FALSE] THEN RETURN;
IF NOT Ascii.Letter[Rope.Fetch[name]] AND NOT Ascii.Digit[Rope.Fetch[name]] THEN RETURN;
displayed ← displayed+1;
list ← CONS [name, list];
};
list: LIST OF Rope.ROPENIL; count: INT ← 0; displayed: INT ← 0;
TerminalIO.PutF["List icons\n"];
[] ← CDDirectory.Enumerate[comm.design, EachEntry];
list ← RopeList.Sort[list, RopeList.Compare];
FOR l: LIST OF Rope.ROPE ← list, l.rest WHILE l#NIL DO
TerminalIO.PutF["  %g", IO.rope[l.first]];
ENDLOOP;
TerminalIO.PutF["\n %g objects counted %g displayed\n", IO.int[count], IO.int[displayed]];
};
Initialization
CDSequencer.ImplementCommand[$PatchWork, RunGenerator];
CDCommandOps.RegisterWithMenu[$RectProgramMenu, "PatchWork generator", "proposes a menu of generators", $PatchWork];
CDEvents.RegisterEventProc[$AfterCellReplacement, FlushPropertiesAfterReplace];
[] ← RegisterProp[$DontFlatten, TRUE];
[] ← RegisterProp[$PWName, TRUE];
CDCommandOps.RegisterWithMenu[$DirectoryMenu, "list icons", "lists cells of the form *.icon", NIL, ListIcons, dontQueue];
[] ← CDDirectory.InstallDirectoryProcs[abutXClass, [
enumerateChildObjects: EnumerateChildObjectsAbut, replaceDirectChilds: ReplaceDirectChildsAbut, expand: ExpandAbut
]];
[] ← CDDirectory.InstallDirectoryProcs[abutYClass, [
enumerateChildObjects: EnumerateChildObjectsAbut, replaceDirectChilds: ReplaceDirectChildsAbut, expand: ExpandAbut
]];
[] ← CDDirectory.InstallDirectoryProcs[tilingClass, [
enumerateChildObjects: EnumerateTilingChildObjects, replaceDirectChilds: ReplaceTilingDirectChilds, expand: ExpandTiling
]];
END.