-- Push/Pop procedure module of silicon (pretty picture) program
-- last modified by McCreight, March 23, 1983  12:03 PM

DIRECTORY
  ChipOrient,
  ChipUserInt,
  InlineDefs,
  ppCache,
  ppdddefs,
  ppddefs,
  ppdefs,
  StringDefs;

pppush: PROGRAM
  IMPORTS ChipOrient, ChipUserInt, InlineDefs, ppCache,
    ppdddefs, ppdefs, ppddefs, StringDefs
  EXPORTS ppdddefs =
  BEGIN
  OPEN StringDefs, ppdefs, ppdddefs, ppddefs;

  pushName: PUBLIC cmdProc =
    BEGIN OPEN ChipUserInt;
    s: STRING = RequestString["Type name of cell to push into"
      ! Punt => GOTO ForgetIt];
    c: cListPtr = FindNamedCell[s];
    IF c.ob # NIL THEN {pushCell[LOOPHOLE[c.ob], NIL]};
    FreeString[s];
    EXITS
      ForgetIt => NULL;
    END;

  pushSel: PUBLIC cmdProc =
    BEGIN
    FOR lp: listPtr ← masterList, lp.nxt WHILE lp # NIL DO
      IF lp.selected AND lp.ob.otyp = cell THEN
        BEGIN
        pushCell[LOOPHOLE[lp↑.ob], lp];
        RETURN;
        END;
      ENDLOOP;
    END;

  pushPnt: PUBLIC cmdProc =
    BEGIN
    FOR lp: listPtr ← masterList, lp.nxt WHILE lp # NIL DO
      IF lp.ob.otyp = cell AND lp.ob.p.inMe[lp.ob, xx - lp.lx, yy - lp.ly, lp.idx] THEN
        BEGIN
        lp.selected ← FALSE;
        pushCell[LOOPHOLE[lp↑.ob], lp];
        RETURN;
        END;
      ENDLOOP;
    END;

  doPop: PUBLIC cmdProc =
    BEGIN OPEN ChipUserInt;
    reply: STRING ← NIL;
    IF cellStack = NIL THEN RETURN;
    IF NOT anyChanges THEN GOTO FlushChanges;
    reply ← RequestString[
      s1: "F to flush the changes you just made",
      s2: "R to replace old definition",
      s3: "N to create new cell (F/R/N):",
      breakChars: "FRN", appendBreakChar: TRUE
      ! Punt => CONTINUE];
    WHILE reply#NIL DO
      IF reply.length = 1 THEN
        BEGIN
        c: CHARACTER = reply[0];
        FreeString[reply];
        reply ← NIL;
        SELECT c FROM
          'F => GOTO FlushChanges;

          'R =>
            BEGIN
            changee: cellPtr = cellStack.dest;
            popCell[changee];
            IF BBoxHasChanged[changee] THEN
              AdjustCallersBBoxes[changee];
            dChange ← TRUE;
            RETURN;
            END;

          'N =>
            BEGIN
            cpp: cListPtr;
            changee: cellPtr;
            oldSize: Point;
            newBox: Rect;
            cellName: STRING ← RequestString[
              s1: "Type Name of NEW Cell",
              s2: (IF cellStack.instance = NIL THEN "<CR> to Retry"
                ELSE "<CR> for No Name")
              ! Punt => GOTO RetryPop];
            IF cellStack.instance = NIL AND cellName.length = 0 THEN
              {FreeString[cellName]; GOTO RetryPop};
            IF cellName.length>0 AND (cpp←FindNamedCell[cellName])#NIL THEN
              BEGIN
              FreeString[cellName];
              cellName ← NIL;
              IF NOT HeSaysYes["A cell already exists by that name.",
                "Want to redefine it?" ! Punt => GOTO RetryPop] THEN GOTO RetryPop;
              WITH dob: cpp.ob SELECT FROM
                cell => changee ← @dob;
                ENDCASE => ERROR;
              END
            ELSE
              BEGIN -- brand new cell
              changee ← makeCell[sx: cellStack.origSize.x, sy: cellStack.origSize.y,
                cnt: 0, ptr: NIL];
              IF cellName.length > 0 THEN
                BEGIN
                cp: cListPtr ← alocCList[];
                cp.nxt ← cellList;
                cp.ob ← changee.p.anotherme[changee];
                cp.ob.returnable ← FALSE;
                cp.name ← cellName;
                cellList ← cp;
                END
              ELSE FreeString[cellName];
              END;

            popCell[changee];
            oldSize ← [x: changee.size[0], y: changee.size[1]];
            IF (newBox ← AdjustBBox[changee]) #
              [x1: 0, y1: 0, x2: oldSize.x, y2: oldSize.y] THEN
              RepositionCalls[ob: changee, oldSize: oldSize, newBox: newBox];

            Explain["Now select items that should be replaced by",
              "the edited cell"];
            FOR lp: listPtr ← masterList, lp.nxt WHILE lp # NIL DO
              IF lp.selected THEN
                BEGIN
                anyChanges ← TRUE;
                oldSize ← [x: lp.ob.size[0], y: lp.ob.size[1]];
                lp.ob.p.release[lp.ob];
                lp.ob ← changee.p.anotherme[changee];
                [[x: lp.lx, y: lp.ly]] ←
                  RepositionCall[[x: lp.lx, y: lp.ly], lp.idx, oldSize, newBox];
                END;
              ENDLOOP;
            AdjustCallersBBoxes[changee];
            dChange ← TRUE;
            RETURN;
            END;
          ENDCASE => NULL;
        EXITS
          RetryPop => NULL;
        END;
      IF reply#NIL THEN {FreeString[reply]; reply ← NIL};
      reply ← RequestString[
        s1: "Try it again:  F to flush the changes you just made",
        s2: "R to replace old definition",
        s3: "N to create new cell (F/R/N):",
        breakChars: "FRN", appendBreakChar: TRUE
        ! Punt => CONTINUE];
      ENDLOOP;
    EXITS
      FlushChanges =>
        BEGIN
        [] ← popCell[];
        dChange ← TRUE;
        END;
    END;


  FindNamedCell: PUBLIC PROC [s: STRING]
    RETURNS [cp: cListPtr] =
    BEGIN
    cp ← NIL;
    IF s#NIL AND s.length>0 THEN
      FOR cp ← cellList, cp.nxt WHILE cp#NIL DO
        IF EquivalentString[s,cp.name] THEN RETURN;
        ENDLOOP;
    END;


  pushCell: PROC [cell: cellPtr, li: listPtr] =
    BEGIN OPEN ChipOrient;
    cellPos: Point = (IF li = NIL THEN [0, 0] ELSE [x: li.lx, y: li.ly]);
    cellSize: Point = [x: cell.size[0], y: cell.size[1]];
    cellOrient: orientationIndex = (IF li = NIL THEN 0 ELSE li.idx);
    bbox: Rect = [x1: cellPos.x, y1: cellPos.y,
      x2: cellPos.x+Size[cellSize, cellOrient].x, y2: cellPos.y+Size[cellSize, cellOrient].y];
    x, y: INTEGER;
    csp: cellSEPtr;
    [, x, y] ← deScaledCursor[colWidth/2, colHeight/2];
    csp ← GetSpace[SIZE[cellSE]];
    csp↑ ← [lp: masterList, instance: li, dest: cell, origSize: cellSize, origBB: bbox,
      changes: anyChanges, nxt: cellStack,
      unDel: unDelPnt, udg: unDelGrpCnt, udi: unDelItemCnt
      ];
    cellStack ← csp;
    masterList ← NIL;
    FOR lp: listPtr ← cell.ptr, lp.nxt WHILE lp # NIL DO
      size: Point = Size[size: [x: lp.ob.size[0], y: lp.ob.size[1]], orient: lp.idx];
      rect: Rect = MapRect[
        itemInCell: [x1: lp.lx, y1: lp.ly, x2: lp.lx+size.x, y2: lp.ly+size.y],
        cellSize: cellSize,
        cellInstOrient: cellOrient,
        cellInstPos: cellPos
        ];
      np: listPtr = makeList[lp.ob.p.anotherme[lp.ob], rect.x1, rect.y1, 0, 0];
      np.idx ← ComposeOrient[cellOrientInWorld: cellOrient, itemOrientInCell: lp.idx];
      np.ridx ← InlineDefs.BITXOR[np.idx, 1];
      np.selected ← FALSE;
      copyProps[np, lp];
      masterList ← insertList[masterList, np];
      ENDLOOP;
    pushLevel ← pushLevel + 1;
    anyChanges ← FALSE;
    unDelPnt ← NIL;
    unDelGrpCnt ← unDelItemCnt ← 0;
    reCount[];
    setCellNameDisplay[];
    dChange ← TRUE;
    END;


  popCell: PROC [cell: cellPtr ← NIL] =
    BEGIN OPEN ChipOrient;
    c: cellSEPtr ← cellStack;
    IF cell # NIL THEN
      BEGIN -- Produces an un-normalized cell with [x1, y1] # [0, 0] and
       -- cell.size incorrect as well.  This will be fixed up by our caller.
      origPos: Point = (IF c.instance = NIL THEN [0, 0]
        ELSE [x: c.instance.lx, y: c.instance.ly]);
      cellOrient: orientationIndex = (IF c.instance = NIL THEN 0
        ELSE c.instance.idx);

      ppCache.FlushObjectCache[cell];
      flushDel[cell.ptr];
      cell.ptr ← NIL;
      FOR lp: listPtr ← masterList, masterList WHILE lp # NIL DO
        instSize: Point = Size[size: [x: lp.ob.size[0], y: lp.ob.size[1]], orient: lp.idx];
        cellRect: Rect = DeMapRect[
          itemInWorld: [x1: lp.lx, y1: lp.ly, x2: lp.lx+instSize.x, y2: lp.ly+instSize.y],
          cellSize: c.origSize,
          cellInstOrient: cellOrient,
          cellInstPos: origPos
          ];

        [x1: lp.lx, y1: lp.ly] ← cellRect;
        lp.idx ← DecomposeOrient[cellOrientInWorld: cellOrient, itemOrientInWorld: lp.idx];
        lp.ridx ← InlineDefs.BITXOR[lp.idx, 1];
        masterList ← lp.nxt;
        cell.ptr ← insertList[cell.ptr, lp];
        ENDLOOP;
      sinceIOchanges ← TRUE;
      END; -- of non-NIL cell

    cellStack ← cellStack.nxt;
    anyChanges ← c.changes;
    flushDel[unDelPnt];
    unDelPnt ← c.unDel;
    unDelGrpCnt ← c.udg;
    unDelItemCnt ← c.udi;
    flushDel[masterList];
    masterList ← c.lp;
    FreeSpace[c];
    setCellNameDisplay[];
    pushLevel ← IF cellStack = NIL THEN 0 ELSE pushLevel - 1;
    reCount[];
    END;


  setCellNameDisplay: PROC =
    BEGIN
    IF cellStack = NIL THEN insideCell ← "-top level-"
    ELSE
      BEGIN
      FOR cp: cListPtr ← cellList, cp.nxt WHILE cp # NIL DO
        IF cp.ob = cellStack.dest THEN {insideCell ← cp.name; RETURN};
        ENDLOOP;
      insideCell ← "-no name-";
      END;
    END;


  AdjustBBox: PROC [ob: obPtr] RETURNS [newBox: Rect] =
    BEGIN
    WITH cob: ob SELECT FROM
      cell =>
        BEGIN
        [mix: newBox.x1, miy: newBox.y1, max: newBox.x2,may: newBox.y2] ←
          minmax[cob.ptr];
        IF Point[x: newBox.x1, y: newBox.y1] # [0,0] THEN
          FOR lp: listPtr ← cob.ptr, lp.nxt WHILE lp # NIL DO
            lp.lx ← lp.lx - newBox.x1;
            lp.ly ← lp.ly - newBox.y1;
            ENDLOOP;
        cob.size[0] ← cob.size[2] ← newBox.x2-newBox.x1;
        cob.size[1] ← newBox.y2-newBox.y1;
        END;
      ENDCASE => newBox ← [x1: 0, y1: 0, x2: ob.size[0], y2: ob.size[1]];
    END;


  RepositionCalls: PROC [ob: obPtr, oldSize: Point, newBox: Rect] =
    BEGIN
    FOR lp: listPtr ← GetSuperPointer[], lp.super WHILE lp # NIL DO
      IF lp.ob = ob THEN
        [[x: lp.lx, y: lp.ly]] ← RepositionCall[[x: lp.lx, y: lp.ly], lp.idx, oldSize, newBox];
      ENDLOOP;
    END;


  BBoxHasChanged: PUBLIC PROC [ob: obPtr] RETURNS [BOOLEAN] =
    BEGIN
    oldSize: Point = [x: ob.size[0], y: ob.size[1]];
    oldBox: Rect = [x1: 0, y1: 0, x2: oldSize.x, y2: oldSize.y];
    newBox: Rect = AdjustBBox[ob];
    IF newBox = oldBox THEN RETURN[FALSE];
    RepositionCalls[ob, oldSize, newBox];
    RETURN[TRUE];
    END;


  RepositionCall: PROC [oldP: Point, idx: orientationIndex, oldSize: Point, newBox: Rect]
    RETURNS [newP: Point] =
    BEGIN
    w: Rect = ChipOrient.MapRect[itemInCell: newBox,
      cellSize: oldSize,
      cellInstOrient: idx,
      cellInstPos: oldP];
    newP ← [x: w.x1, y: w.y1];
    IF newP#oldP THEN anyChanges ← TRUE;
    END;


  cellShift: PUBLIC PROC [lp: listPtr, ix, iy, ax, ay: locNum] =
    BEGIN
    SELECT InlineDefs.BITAND[13, lp.idx] FROM
      4 => BEGIN iy ← ix; ix ← ay; END;
      8 => BEGIN iy ← ay; ix ← ax; END;
      12 => BEGIN ix ← iy; iy ← ax; END;
      1 => BEGIN ix ← ax; END;
      5 => BEGIN ay ← iy; iy ← ix; ix ← ay; END;
      9 => BEGIN iy ← ay; END;
      13 => BEGIN ix ← ay; iy ← ax; END;
      ENDCASE;
    lp.lx ← lp.lx + ix;
    lp.ly ← lp.ly + iy;
    END;


  AdjustCallersBBoxes: PUBLIC PROC [ob: obPtr] =
    BEGIN
    FOR scob: cellPtr ← GetCellSuper[], scob.super WHILE scob#NIL DO
      FOR lp: listPtr ← scob.ptr, lp.nxt WHILE lp#NIL DO
        IF lp.ob=ob THEN
          BEGIN
          IF BBoxHasChanged[scob] THEN
            BEGIN
            ppCache.FlushObjectCache[scob];
            AdjustCallersBBoxes[scob];
            END;
          EXIT;
          END;
        ENDLOOP;
      ENDLOOP;
    END;

  END.