-- Copyright (C) 1982, 1983, 1984  by Xerox Corporation. All rights reserved. 
-- File: WindowImplB.mesa - last edit:
-- Haynes.PA           31-Aug-84 14:24:11
-- Bruce,	20-Dec-82 15:52:11
-- Daniels	31-Oct-83 15:51:32

DIRECTORY
  BitBlt: TYPE USING [BitAddress, BITBLT],
  Display: TYPE USING [replaceFlags],
  Inline: TYPE USING [LongDiv, LongMult],
  RecOps: TYPE USING [
    Alloc, Bite, Blt, ClipBox, Convert, ConvertBox, Copy, Free, FreeRecList,
    Intersect, RecList, ScreenToWindowBox, Shift],
  SpecialDisplay: TYPE USING [defaultContext, Special],
  Window: TYPE USING [
    Box, EnumerateTree, Error, MouseTransformerProc, nullBox, Place, Stack, 
    UnderChangedProc, ValidateTree],
  WindowExtra: TYPE USING [],
  WindowOps: TYPE USING [
    AssertUnobscured, bbPtr, InvalidateTree, GetBpl, lock, Object, Offset,
    rootWindow, SpecialTimesWpl];

WindowImplB: MONITOR LOCKS WindowOps.lock
  IMPORTS BitBlt, Inline, RecOps, SpecialDisplay, Window, WindowOps 
  EXPORTS Window, WindowExtra =
  BEGIN

  Handle: TYPE = LONG POINTER TO Object;
  Object: PUBLIC TYPE = WindowOps.Object;

  -- reading and writing fields in opaque record

  GetBox: PUBLIC PROC [w: Handle] RETURNS [Window.Box] = {RETURN[w.box]};
  GetParent: PUBLIC PROC [w: Handle] RETURNS [Handle] = {RETURN[w.parent]};
  GetSibling: PUBLIC PROC [w: Handle] RETURNS [Handle] = {RETURN[w.sibling]};
  GetChild: PUBLIC PROC [w: Handle] RETURNS [Handle] = {RETURN[w.child]};
  GetDisplayProc: PUBLIC PROC [w: Handle] RETURNS [PROC [Handle]] = {
    RETURN[w.display]};
  GetClearingRequired: PUBLIC PROC [w: Handle] RETURNS [BOOLEAN] = {
    RETURN[w.clearingRequired]};
  GetUseBadPhosphor: PUBLIC PROC [w: Handle] RETURNS [BOOLEAN] = {
    RETURN[w.useBadPhosphor]};

  IsDescendantOfRoot: PUBLIC PROC [w: Handle] RETURNS [BOOLEAN] = {
    RETURN[w.inTree]};

  SetParent: PUBLIC PROC [window, newParent: Handle] RETURNS [oldParent: Handle] =
    {
    IF window.inTree THEN ERROR Window.Error[windowInTree];
    oldParent ← window.parent;
    window.parent ← newParent};

  SetSibling: PUBLIC PROC [window, newSibling: Handle]
    RETURNS [oldSibling: Handle] = {
    IF window.inTree THEN ERROR Window.Error[windowInTree];
    oldSibling ← window.sibling;
    window.sibling ← newSibling};

  SetChild: PUBLIC PROC [window, newChild: Handle] RETURNS [oldChild: Handle] = {
    IF window.inTree THEN ERROR Window.Error[windowInTree];
    oldChild ← window.child;
    window.child ← newChild};

  SetDisplayProc: PUBLIC PROC [window: Handle, newProc: PROC [Handle]]
    RETURNS [oldProc: PROC [Handle]] = {
    oldProc ← window.display; window.display ← newProc};

  SetClearingRequired: PUBLIC PROC [window: Handle, required: BOOLEAN]
    RETURNS [old: BOOLEAN] = {
    old ← window.clearingRequired; window.clearingRequired ← required};

  SetUseBadPhosphor: PUBLIC ENTRY PROC [window: Handle, use: BOOLEAN]
    RETURNS [old: BOOLEAN] = {
    ENABLE UNWIND => NULL;
    old ← window.useBadPhosphor; window.useBadPhosphor ← use};

  InitializeWindow: PUBLIC PROC [
    window: Handle, display: PROC [Handle], box: Window.Box,
    parent: Handle ← WindowOps.rootWindow, sibling, child: Handle ← NIL,
    clearingRequired: BOOLEAN ← TRUE, under, cookieCutter: BOOLEAN ← FALSE] = {
    window↑ ← [
      beingDisplayed: FALSE, useBadPhosphor: TRUE,
      cookieCutterVariant: cookieCutter, cookie: FALSE, underVariant: under,
      underNow: FALSE, clearingRequired: clearingRequired, inTree: FALSE,
      parent: parent, sibling: sibling, child: child, box: box,
      place: [0, 0], display: display, invalid: NIL, badPhosphor: NIL]};

  -- bitmap under routines

  MinusLandBitmapUnder: PUBLIC TYPE = RECORD [
    pointer: LONG POINTER,
    underChanged: Window.UnderChangedProc,
    -- ↑↑ called when bitmapunder changes if # NIL
    mouseTransformer: Window.MouseTransformerProc] ← TRASH; -- make Ed happy...
    -- ↑↑ called to convert a mouse position if # NIL

  WordsForBitmapUnder: PUBLIC PROC [window: Handle] RETURNS [CARDINAL] = {
    RETURN[
      Inline.LongDiv[
        Inline.LongMult[window.box.dims.w + 31, window.box.dims.h], 16]]};

  GetBitmapUnder: PUBLIC PROC [window: Handle] RETURNS [LONG POINTER] = {
    minus: LONG POINTER TO MinusLandBitmapUnder;
    IF ~window.underVariant THEN RETURN[NIL];
    IF ~window.underNow THEN RETURN[NIL];
    minus ← LOOPHOLE[window - MinusLandBitmapUnder.SIZE];
    RETURN[minus.pointer]};

  SetBitmapUnder: PUBLIC PROC [
    window: Handle, pointer: LONG POINTER ← NIL,
    underChanged: Window.UnderChangedProc ← NIL,
    mouseTransformer: Window.MouseTransformerProc ← NIL]
    RETURNS [oldPointer: LONG POINTER] = {
    shouldValidate: BOOLEAN ← FALSE;
    SetBitmapUnderLocked: ENTRY PROC = {
      ENABLE UNWIND => NULL;
      atbu: LONG POINTER TO MinusLandBitmapUnder ←
        LOOPHOLE[window, LONG POINTER] - MinusLandBitmapUnder.SIZE;
      IF ~window.underVariant THEN ERROR Window.Error[noUnderVariant];
      oldPointer ← atbu.pointer;
      atbu↑ ← MinusLandBitmapUnder[
        pointer: pointer, underChanged: underChanged,
        mouseTransformer: mouseTransformer];
      window.underNow ← pointer # NIL;
      IF ~window.inTree OR ~window.underNow THEN RETURN;
      WindowOps.AssertUnobscured[window, illegalBitmap];
      {bitsToInvalidate: RecOps.RecList = RecOps.Convert[window];
      oldInvalidList: RecOps.RecList = window.invalid;
      window.invalid ← NIL;
      SetDst[bitsToInvalidate, window];
      WindowOps.InvalidateTree[window.parent, window.sibling, bitsToInvalidate];
      RecOps.FreeRecList[window.invalid]; window.invalid ← oldInvalidList};
      shouldValidate ← TRUE};
    IF pointer # NIL AND window.inTree THEN 
      Window.Stack[window, window.parent.child];
    SetBitmapUnderLocked[];
    IF shouldValidate THEN Window.ValidateTree[]};

  Float: PUBLIC PROC [
    window, temp: Handle,
    proc: PROC [window: Handle] RETURNS [place: Window.Place, done: BOOLEAN]] = {
    done: BOOLEAN;
    newPlace: Window.Place;
    IF window.parent = NIL THEN ERROR Window.Error[illegalFloat];
    IF ~window.inTree THEN ERROR Window.Error[illegalFloat];
    IF temp.inTree THEN ERROR Window.Error[illegalFloat];
    [newPlace, done] ← proc[window];
    IF done THEN RETURN;
    Window.Stack[window, window.parent.child];
    Window.ValidateTree[];
    FloatLocked[window, temp, proc, newPlace]};

  FloatLocked: ENTRY PROC [
    window, temp: Handle,
    proc: PROC [window: Handle] RETURNS [place: Window.Place, done: BOOLEAN],
    newPlace: Window.Place] = {
    ENABLE UNWIND => NULL;
    ctx: SpecialDisplay.Special = SpecialDisplay.defaultContext;
    icon: LONG POINTER = GetBitmapUnder[temp];
    iconBpl: CARDINAL = window.box.dims.w;
    done: BOOLEAN ← FALSE;
    IF icon = NIL THEN ERROR Window.Error[illegalFloat];
    IF GetBitmapUnder[window] = NIL THEN ERROR Window.Error[illegalFloat];
    IF temp.box.dims # window.box.dims THEN ERROR Window.Error[illegalFloat];
    WindowOps.AssertUnobscured[window, illegalFloat];
    WindowOps.bbPtr↑ ← [
      dst: [word: icon, bit: 0], dstBpl: iconBpl, 
      src: GetBitAddress[window.place, ctx], srcDesc: [srcBpl[ctx.bpl]], 
      width: iconBpl, height: window.box.dims.h, flags: Display.replaceFlags];
    BitBlt.BITBLT[WindowOps.bbPtr];
    UNTIL done DO
      dx: INTEGER = newPlace.x - window.place.x;
      dy: INTEGER = newPlace.y - window.place.y;
      old: RecOps.RecList = RecOps.Convert[window];
      new: RecOps.RecList = RecOps.Shift[RecOps.Copy[old], dx, dy];
      list: RecOps.RecList;
      Slide: INTERNAL PROC [w: Handle] = {
	w.place.x ← w.place.x + dx; w.place.y ← w.place.y + dy};
      list ← RecOps.Bite[list: RecOps.Copy[old], biter: RecOps.Copy[new]];
      SetSrc[list, window];
      RecOps.Blt[list, 0, 0];  -- place bits down from under
      ShiftUnder[window, old.box.left, old.box.top, dx, dy];
      Window.EnumerateTree[window, Slide];
      window.box.place.x ← window.box.place.x + dx;
      window.box.place.y ← window.box.place.y + dy;
      list ← RecOps.Bite[list: new, biter: old];
      SetDst[list, window];
      RecOps.Blt[list, 0, 0];  -- pick up new bits
      WindowOps.bbPtr↑ ← [  -- put back icon
	dst: GetBitAddress[window.place, ctx], dstBpl: ctx.bpl, 
	src: [word: icon, bit: 0], srcDesc: [srcBpl[iconBpl]], 
	width: iconBpl, height: window.box.dims.h, flags: Display.replaceFlags];
      BitBlt.BITBLT[WindowOps.bbPtr];
      [newPlace, done] ← proc[window]; 
      ENDLOOP};
  
  ShiftUnder: INTERNAL PROC [w: Handle, left, top, dx, dy: INTEGER] = {
    oldOffset: CARDINAL = CARDINAL[left]/16;
    underLeft: CARDINAL = oldOffset*16;
    myDx: INTEGER = (CARDINAL[left + dx]/16 - oldOffset)*16;
    old: RecOps.RecList = RecOps.Alloc[];
    new: RecOps.RecList;
    old↑ ← [
      box: [
	left: underLeft, top: top, 
	right: underLeft + WindowOps.GetBpl[w], bottom: top + w.box.dims.h], 
      link: NIL, src: w, dst: w];
    new ← RecOps.Shift[RecOps.Copy[old], myDx, dy];
    RecOps.Blt[RecOps.Intersect[old, new], -myDx, -dy];
    RecOps.FreeRecList[new];
    RecOps.FreeRecList[old]};
    
  SetSrc: PROC [list: RecOps.RecList, w: Handle] = INLINE {
    FOR r: RecOps.RecList ← list, r.link UNTIL r = NIL DO r.src ← w ENDLOOP};
    
  SetDst: PROC [list: RecOps.RecList, w: Handle] = INLINE {
    FOR r: RecOps.RecList ← list, r.link UNTIL r = NIL DO r.dst ← w ENDLOOP};

  GetBitAddress: PROC [place: Window.Place, ctx: SpecialDisplay.Special] 
    RETURNS [BitBlt.BitAddress] = INLINE {
    offset, bit: INTEGER;
    [word: offset, bit: bit] ← WindowOps.Offset[place.x];
    RETURN[[
      word: ctx.bmAddress + WindowOps.SpecialTimesWpl[place.y, ctx] + offset, 
      bit: bit]]};
  
  -- utilities used only by Tajo

  BitmapPlace: PUBLIC PROC [window: Handle, place: Window.Place ← [0, 0]]
    RETURNS [pl: Window.Place] = {
    pl.x ← place.x + window.place.x; pl.y ← place.y + window.place.y};

  BitmapPlaceToWindowAndPlace: PUBLIC PROC [bitmapPlace: Window.Place]
    RETURNS [window: Handle, place: Window.Place] = {
    w: Handle ← WindowOps.rootWindow;
    child: Handle;
    IF NOT w.inTree THEN RETURN [NIL, [0, 0]];
    IF bitmapPlace.x NOT IN [w.box.place.x..w.box.place.x + w.box.dims.w) OR
      bitmapPlace.y NOT IN [w.box.place.y..w.box.place.y + w.box.dims.h)
      THEN RETURN[NIL, [0, 0]];
    DO
      FOR child ← w.child, child.sibling UNTIL child = NIL DO
        IF bitmapPlace.x IN [child.place.x..child.place.x + child.box.dims.w) AND
          bitmapPlace.y IN [child.place.y..child.place.y + child.box.dims.h)
	  THEN EXIT;
        ENDLOOP;
      IF child = NIL THEN
        RETURN[w, [bitmapPlace.x - w.place.x, bitmapPlace.y - w.place.y]];
      w ← child;
      ENDLOOP};

  BoxesAreDisjoint: PUBLIC PROC [a, b: Window.Box] RETURNS [BOOLEAN] = {
    RETURN[
      (a.place.x + a.dims.w <= b.place.x) OR (a.place.x >= b.place.x + b.dims.w)
        OR (a.place.y + a.dims.h <= b.place.y)
        OR (a.place.y >= b.place.y + b.dims.h)]};

  IntersectBoxes: PUBLIC PROC [b1, b2: Window.Box] RETURNS [box: Window.Box] = {
    left: INTEGER = MAX[b1.place.x, b2.place.x];
    top: INTEGER = MAX[b1.place.y, b2.place.y];
    right: INTEGER = MIN[b1.place.x + b1.dims.w, b2.place.x + b2.dims.w];
    bottom: INTEGER = MIN[b1.place.y + b1.dims.h, b2.place.y + b2.dims.h];
    box ← [[left, top], [right - left, bottom - top]];
    IF box.dims.h <= 0 OR box.dims.w <= 0 THEN RETURN[Window.nullBox]};

  IsPlaceInBox: PUBLIC PROC [place: Window.Place, box: Window.Box]
    RETURNS [BOOLEAN] = {
    RETURN[
      place.x IN [box.place.x..box.place.x + box.dims.w)
        AND place.y IN [box.place.y..box.place.y + box.dims.h)]};

  ObscuredBySibling: PUBLIC PROC [w: Handle] RETURNS [BOOLEAN] = {
    FOR sib: Handle ← w.parent.child, sib.sibling UNTIL sib = w DO
      IF ~BoxesAreDisjoint[sib.box, w.box] THEN RETURN[TRUE] ENDLOOP;
    RETURN[FALSE]};

  TrimBoxStickouts: PUBLIC PROC [window: Handle, box: Window.Box]
    RETURNS [newBox: Window.Box] = {
    r: RecOps.RecList;
    IF ~window.inTree THEN RETURN[Window.nullBox];
    r ← RecOps.ClipBox[window, RecOps.ConvertBox[window, box]].clippedBox;
    IF r.link # NIL THEN {ClipBoxBug: SIGNAL = CODE; SIGNAL ClipBoxBug};
    newBox ← RecOps.ScreenToWindowBox[r.box];
    newBox.place.x ← newBox.place.x - window.place.x;
    newBox.place.y ← newBox.place.y - window.place.y;
    RecOps.Free[r]};

  END.