{***********************************************************************}
{									}
{ __TopWin    Make a hidden window visible				}
{									}
{ function  __TopWin(var WinPtr : _WindowPtr): boolean; 		}
{									}
{	      WinPtr	Pointer to _Window record to be placed on top	}
{			of the displayed windows and to be made current.}
{									}
{ Description This function makes any active window current.  If the	}
{	      window is not on top (that is, at the end of the window	}
{	      descriptor list), it is moved there and made current.	}
{									}
{	      If the window is not on top, it is promoted to the top	}
{	      so that the window screen images are correctly con-	}
{	      structed.  If the window is already current, no action	}
{	      is taken. 						}
{									}
{	      If WinPtr does not reference an active window, the	}
{	      function value is FALSE, otherwise the value returned is	}
{	      TRUE.							}
{									}
{	      __TopWin removes all windows on top of the desired	}
{	      window and the window itself, and then replaces the	}
{	      windows in the correct order.  Finally, the desired	}
{	      window is placed on top and made current.  In this	}
{	      manner the underlying screen images are saved so that the }
{	      windows can be removed normally.	For efficiency, the	}
{	      screen image is copied to memory and window redrawing is	}
{	      only performed in memory.  Consequently, __TopWin re-	}
{	      quires Heap space for both screen image (can be as much	}
{	      as 8000 bytes for 50-row mode) and the current window	}
{	      contents for each window.  Finally, just the top window	}
{	      is redrawn on the physical screen.			}
{									}
{ Version     4.00 (C)Copyright Blaise Computing Inc.  1987		}
{_______________________________________________________________________}

function  __TopWin(WinPtr : _WindowPtr) : boolean;

  var
    ScnImage : _ScnImagePtr;	       { Pointer to memory screen image }

  {+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  { MoveImg  Move screen images to and from a buffer			}
  {									}
  { This routine is very much like __MoveScn except that it moves	}
  { screen images (rectangles) to a memory buffer rather than the video }
  { adapter memory. The memory buffer is pointed to by ScnImage, which	}
  { is initialized by the main function code.				}
  {_____________________________________________________________________}

  procedure MoveImg(X1,Y1,X2,Y2 : byte;
		      BufferPtr : pointer; ToImage : boolean);

    var
      BufPtr	   : _ScnImagePtr absolute BufferPtr;
      OffSet,Width : word;
      Delta,Lines  : word;
      I,J,K	   : word;

    begin {MoveImg}

      { Compute the constants associated with the access area so that	}
      { the video image can be correctly accessed.			}

      OffSet := ((Y1 - 1) * _CurColumns) + X1;
      Width  := X2 - X1 + 1;
      Delta  := _CurColumns - X2 + X1 - 1;
      Lines  := Y2 - Y1 + 1;

      J := Offset;
      K := 1;

      for I := 1 to Lines do
	begin
	  if (ToImage) then
	     Move(BufPtr^[K],ScnImage^[J],2 * Width)
	  else
	     Move(ScnImage^[J],BufPtr^[K],2 * Width);
	  Inc(J,Width + Delta);
	  Inc(K,Width)
	end

    end;  {MoveImg}


  {+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  { Covers  Determine if two windows overlap				}
  {									}
  { This function returns TRUE if the windows referenced by the window	}
  { pointers represent screen rectangles that overlap (intersect).  If	}
  { the windows have borders, they are considered part of the screen	}
  { rectangle.								}
  {_____________________________________________________________________}

  function Covers(TopWin,BotWin : _WindowPtr) : boolean;

    var
      X1,Y1,X2,Y2 : byte;
      U1,W1,U2,W2 : byte;

    begin {Covers}

      with TopWin^._Dim do
	begin
	  X1 := _XUpper;
	  Y1 := _YUpper;
	  X2 := _XLower;
	  Y2 := _YLower;
	end;
      if (TopWin^._Border._BorType <> _NOBORD_WIN) then
	begin
	  Dec(X1);
	  Dec(Y1);
	  Inc(X2);
	  Inc(Y2)
	end;
      with BotWin^._Dim do
	begin
	  U1 := _XUpper;
	  W1 := _YUpper;
	  U2 := _XLower;
	  W2 := _YLower
	end;
      if (BotWin^._Border._BorType <> _NOBORD_WIN) then
	begin
	  Dec(U1);
	  Dec(W1);
	  Inc(U2);
	  Inc(W2)
	end;

    { Two windows overlap, if one is to the left of the other, but not	}
    { so far to the left that no overlap occurs (that is the first	}
    { part of the conjunction), and one is above the other, but not too }
    { far above.							}

    Covers := ( (((X1 <= U2) and (X2 >= U1)) or
			    ((U1 <= X2) and (U2 >= X1))) and
		(((Y1 <= W2) and (Y2 >= W1)) or
			    ((W1 <= Y2) and (W2 >= Y1))) )

  end;	{Covers}


  type
    ActWinPtr = ^ActWin;	       { Doubly linked list of active	}
    ActWin    = record		       { windows lying above the window }
		  ActWinPtr   : _WindowPtr;
		  WinContents : pointer;
		  WinSize     : word;
		  NextActWin  : ActWinPtr;
		  PrevActWin  : ActWinPtr
		end;
  var
    ActWinRoot	 : ActWinPtr;
    ActWinLast	 : ActWinPtr;
    NewActWin	 : ActWinPtr;
    NewTopWin	 : ActWin;
    TempPtr	 : _WindowPtr;
    Found	 : boolean;
    Mode,Cols	 : byte;
    Rows,APage	 : byte;
    DPage,Device : byte;
    ScnImageSize : word;

  begin {__TopWin}

    { If WinPtr is already current, there is nothing to do.		}

    if (WinPtr = _WinCur) then
       begin
	 __TopWin := TRUE;
	 Exit
       end;

    { Make sure that WinPtr points to a window that is active.		}

    Found   := FALSE;
    TempPtr := _WinRoot;
    while ((not Found) and (TempPtr <> NIL)) do
      if ((TempPtr = WinPtr) and (TempPtr^._Active)) then
	 Found := TRUE
      else
	 TempPtr := TempPtr^._WinTop;
    if (not Found) then
       begin
	 __TopWin := FALSE;
	 Exit			       { No record found, so return.	}
       end;

    { Allocate space for the screen image, and make a copy of the	}
    { screen image on the heap.  This is used for the window image	}
    { calculations so that the physical screen need not be updated.	}

    Device	 := __RetDvScn(Mode,Cols,Rows,APage,DPage);
    ScnImageSize := 2 * (Rows * Cols);
    GetMem(ScnImage,ScnImageSize);
    __MoveScn(1,1,Cols,Rows,ScnImage,FALSE);

    { Now construct the list of all active windows that are on top of	}
    { WinPtr^ and intersect its window boundaries.  These are the	}
    { windows whose saved screen images may change when the window is	}
    { moved to the top. 						}

    ActWinRoot := NIL;
    ActWinLast := NIL;
    TempPtr    := WinPtr^._WinTop;
    while (TempPtr <> NIL) do
      begin
	if (TempPtr^._Active and Covers(TempPtr,WinPtr)) then
	   begin
	     New(NewActWin);
	     if (ActWinRoot = NIL) then
		ActWinRoot := NewActWin;
	     with NewActWin^ do
	       begin
		 NextActWin := NIL;
		 if (ActWinLast <> NIL) then
		    ActWinLast^.NextActWin := NewActWin;
		 PrevActWin := ActWinLast;
		 ActWinPtr  := TempPtr;
		 with TempPtr^._Dim do
		   if (TempPtr^._Border._BorType = _NOBORD_WIN) then
		      begin
			WinSize := (_XLower - _XUpper + 1) *
						  (_YLower - _YUpper + 1) * 2;
			GetMem(WinContents,WinSize);
		      end
		   else
		      begin
			WinSize := (_XLower - _XUpper + 3) *
						  (_YLower - _YUpper + 3) * 2;
			GetMem(WinContents,WinSize);
		      end
	       end;
	     ActWinLast := NewActWin;
	   end;
	TempPtr := TempPtr^._WinTop
      end;

    { Move through the active window list in reverse order displaying	}
    { the original screen contents.  Notice that the window records	}
    { are not altered in any way.					}

    NewActWin := ActWinLast;
    while (NewActWin <> NIL) do
      begin
	with NewActWin^,NewActWin^.ActWinPtr^._Dim do
	  if (ActWinPtr^._Border._BorType = _NOBORD_WIN) then
	     begin
	       MoveImg(_XUpper,_YUpper,_XLower,_YLower,
						      WinContents,FALSE);
	       MoveImg(_XUpper,_YUpper,_XLower,_YLower,
						  ActWinPtr^._Image,TRUE)
	     end
	  else
	     begin
	       MoveImg(_XUpper - 1,_YUpper - 1,
			      _XLower + 1,_YLower + 1,WinContents,FALSE);
	       MoveImg(_XUpper - 1,_YUpper - 1,
			 _XLower + 1,_YLower + 1,ActWinPtr^._Image,TRUE)
	     end;
	NewActWin := NewActWin^.PrevActWin
      end;

    { Remove the window in question from the screen.  This is done by	}
    { restoring the original screen image.  First, however, the window	}
    { contents are saved.  For convenience an ActWin record is used.	}
    { This need only be done if there are active windows on top, that	}
    { is, if ActWinRoot is not NIL.					}

    if (ActWinRoot <> NIL) then
      with WinPtr^._Dim,NewTopWin do
	begin
	  if (WinPtr^._Border._BorType = _NOBORD_WIN) then
	     begin
	       WinSize := (_XLower - _XUpper + 1) *
					     (_YLower - _YUpper + 1) * 2;
	       GetMem(WinContents,WinSize);
	       MoveImg(_XUpper,_YUpper,_XLower,_YLower,
						      WinContents,FALSE);
	       MoveImg(_XUpper,_YUpper,_XLower,_YLower,
						    WinPtr^._Image,TRUE)
	     end
	  else
	     begin
	       WinSize := (_XLower - _XUpper + 3) *
					     (_YLower - _YUpper + 3) * 2;
	       GetMem(WinContents,WinSize);
	       MoveImg(_XUpper - 1,_YUpper - 1,
			      _XLower + 1,_YLower + 1,WinContents,FALSE);
	       MoveImg(_XUpper - 1,_YUpper - 1,
			    _XLower + 1,_YLower + 1,WinPtr^._Image,TRUE)
	     end;
	  ActWinPtr  := WinPtr;
	  NextActWin := NIL;
	  PrevActWin := NIL
	end;

    { Now replace each of the removed windows by passing through the	}
    { active list in order.  A new screen image is recovered, and then	}
    { previous window image is displayed.				}

    NewActWin := ActWinRoot;
    while (NewActWin <> NIL) do
      begin
	with NewActWin^,NewActWin^.ActWinPtr^._Dim do
	  if (ActWinPtr^._Border._BorType = _NOBORD_WIN) then
	     begin
	       MoveImg(_XUpper,_YUpper,_XLower,_YLower,
					       ActWinPtr^._Image,FALSE);
	       MoveImg(_XUpper,_YUpper,_XLower,_YLower,
						      WinContents,TRUE)
	     end
	  else
	     begin
	       MoveImg(_XUpper - 1,_YUpper - 1,_XLower + 1,_YLower + 1,
						 ActWinPtr^._Image,FALSE);
	       MoveImg(_XUpper - 1,_YUpper - 1,
				_XLower + 1,_YLower + 1,WinContents,TRUE)
	     end;
	NewActWin := NewActWin^.NextActWin
      end;

    { At last, place the new top window on the screen.	Like the other	}
    { windows, the new screen image is saved, and then the previous	}
    { contents are displayed.  Then call __CurWin to make the window	}
    { current.	__CurWin shuffles the linked list of windows so that	}
    { it reflects that fact that the window is on top.			}

    if (ActWinRoot <> NIL) then
       with NewTopWin,NewTopWin.ActWinPtr^._Dim do
	 begin
	   if (ActWinPtr^._Border._BorType = _NOBORD_WIN) then
	      begin
		MoveImg(_XUpper,_YUpper,_XLower,_YLower,
						ActWinPtr^._Image,FALSE);
		__MoveScn(_XUpper,_YUpper,_XLower,_YLower,
						       WinContents,TRUE)
	      end
	   else
	      begin
		MoveImg(_XUpper - 1,_YUpper - 1,_XLower + 1,_YLower + 1,
						ActWinPtr^._Image,FALSE);
		__MoveScn(_XUpper - 1,_YUpper - 1,
			       _XLower + 1,_YLower + 1,WinContents,TRUE)
	      end;
	   FreeMem(WinContents,WinSize);
	 end;
    __TopWin := __CurWin(WinPtr);

    { Finally, release all memory used by the active windows and the	}
    { screen image.							}

    NewActWin := ActWinRoot;
    while (NewActWin <> NIL) do
      begin
	with NewActWin^ do
	  begin
	    FreeMem(WinContents,WinSize);
	    ActWinLast := NewActWin^.NextActWin
	  end;
	Dispose(NewActWin);
	NewActWin := ActWinLast
      end;
   FreeMem(ScnImage,ScnImageSize)

  end;	{__WinTop}
