{ Graphics Vision for Windows: GVisible
  Copr. 1996 Matthias Koeppe
}

function NewVis(var Bounds: Objects.TRect): PVis;
var
  vis: PRectVis;
  R: TRECT;
begin
  New(vis);
  with vis^ do
  begin
    Region := CreateRectRgn(0, 0, 0, 0);
    DeleteObject(Region);
    { Mysterious Win 3.1: The following call occasionally failed. [TEditor]
      That's why I inserted the lines above. }
    Region := CreateRectRgn(Bounds.A.X, Bounds.A.Y, Bounds.B.X, Bounds.B.Y);
    Trans := nil;
    CurTrans := 0;
    Drawmode := 0;
  end;
  NewVis := vis
end;

function EmptyVis(Vis: PVis): Boolean;
var
  R: WinTypes.TRECT;
begin
  if Vis = nil then EmptyVis := true
  else begin
    with PRectVis(Vis)^ do
      EmptyVis := GetRgnBox(Region, R) = NULLREGION
  end
end;

procedure SubtractRegion(Vis: PVis; var Bounds: Objects.TRect);
var
  sub: HRgn;
  R: Objects.TRect;
begin
  If Vis <> nil then
  with PRectVis(Vis)^ do
  begin
    If (GetRgnBox(Region, WinTypes.TRect(R)) = NullRegion)
      or not DoesIntersect(R, Bounds)
    then Exit;
    sub := CreateRectRgn(Bounds.A.X, Bounds.A.Y, Bounds.B.X, Bounds.B.Y);
    CombineRgn(Region, Region, sub, RGN_DIFF);
    DeleteObject(sub);
  end
end;

procedure ClipVis(Vis: PVis; var Clip: Objects.TRect);
var
  sub: HRgn;
begin
  If Vis <> nil then
  with PRectVis(Vis)^ do
  begin
    sub := CreateRectRgn(Clip.A.X, Clip.A.Y, Clip.B.X, Clip.B.Y);
    CombineRgn(Region, Region, sub, RGN_AND);
    DeleteObject(sub);
  end
end;

procedure SubtractVis(Vis, SubVis: PVis);
begin
  If (Vis <> nil) and (SubVis <> nil) then
  with PRectVis(Vis)^ do
  begin
    CombineRgn(Region, Region, PRectVis(SubVis)^.Region, RGN_DIFF);
  end
end;

function IntersectVis(Vis1, Vis2: PVis): PVis;
var
  vis: PRectVis;
begin
  If (vis1 = nil) or (vis2 = nil)
  then IntersectVis := nil
  else begin
    New(vis);
    with vis^ do
    begin
      Region := CreateRectRgn(0, 0, 0, 0);
      CombineRgn(Region, PRectVis(Vis1)^.Region, PRectVis(Vis2)^.Region, RGN_AND);
      Trans := JoinViewCollections(PRectVis(Vis1)^.Trans, PRectVis(Vis2)^.Trans);
      CurTrans := 0;
      DrawMode := 0;
    end;
    IntersectVis := vis
  end
end;

function IntersectDelta(Vis: PVis; Delta: Objects.TPoint): PVis;
var
  res: PRectVis;
begin
  If Vis = nil
  then IntersectDelta := nil
  else begin
    New(res);
    with res^ do
    begin
      Region := CreateRectRgn(0, 0, 0, 0);
      CombineRgn(Region, PRectVis(Vis)^.Region, 0, RGN_COPY);
      OffsetRgn(Region, Delta.X, Delta.Y);
      CombineRgn(Region, Region, PRectVis(Vis)^.Region, RGN_AND);
      Trans := CopyViewCollection(PRectVis(Vis)^.Trans);
      CurTrans := PRectVis(Vis)^.CurTrans;
      DrawMode := 0;
    end;
    IntersectDelta := res
  end
end;

procedure MoveVis(Vis: PVis; Delta: Objects.TPoint);
begin
  If Vis <> nil then
  with PRectVis(Vis)^ do
  begin
    OffsetRgn(Region, Delta.X, Delta.Y)
  end
end;

function SortVis(Vis: PVis; Delta: Objects.TPoint): PVis;
begin
  SortVis := Vis;
end;

function RawCopyVis(Vis: PVis): PVis;
var
  copy: PRectVis;
begin
  If Vis = nil
  then RawCopyVis := nil
  else begin
    New(copy);
    with copy^ do
    begin
      Region := CreateRectRgn(0, 0, 0, 0);
      CombineRgn(Region, PRectVis(Vis)^.Region, 0, RGN_COPY);
      Trans := nil;
      CurTrans := -1; {indicate raw mode}
      DrawMode := 0;
    end;
    RawCopyVis := copy
  end
end;

procedure FreeVis(Vis: PVis);
begin
  If Vis <> nil
  then begin
    with PRectVis(Vis)^ do
    begin
      DeleteObject(Region);
      If Trans <> nil
      then Dispose(Trans, Done)
    end;
    Dispose(PRectVis(Vis))
  end
end;

function BeginDraw(Vis: PVis; Origin: Objects.TPoint;
  This: PGView; Modifying: Boolean): Boolean;
var
  P: PGView;
  R: TRect;
begin
  BeginDraw := false;
  If Vis = nil then Exit;

  This^.GetExtent(R);
  R.Move(Origin.x, Origin.y);
  if not RectInRegion(PRectVis(Vis)^.Region, WinTypes.TRect(R))
  then Exit; { This is a weak test whether it makes sense to draw at all. }
  { && A stronger test might use the owner-clipped bound of the view }

  If Modifying or (This^.State and sfTransparent <> 0)
  then HideTrans(Vis, This);
  P := This;
  while (P <> nil) and (P^.State and sfRoot = 0) do
    P := P^.GOwner;
  If (P = nil) or (P^.Wnd = 0) then Exit;
  WinGr.SelectWnd(P^.Wnd);
  WinGr.ExBeginDraw((not Modifying) and (This^.State and sfTransparent = 0)
    and (This^.Options and ofBufferOne <> 0));
  { && Actually, we could do ExBeginDraw(true) in the Modifying or Transparent
       cases, too. But we would have to copy the image from DevDC to MemDC.
       See `gvisrect' for detailed case discussion (including transparent
       overlappings handling. }

  This^.ViewLock := 1;
  with PRectVis(Vis)^ do
  begin
    ClipRgn := Region;
    GetRgnBox(Region, WinTypes.TRect(MetaClipRect));
    SetClipRectR(MetaClipRect);
    SetDrawOriginP(Origin);
  end;
  BeginDraw := true
end;

function EndDraw(Vis: PVis; This: PGView): Boolean;
begin
  If Vis = nil then Exit;
  If DC = MemDC
  then begin
    SetDrawOrigin(0, 0);
    DC := DevDC;
    SetClipRectR(MetaClipRect);
    SetDrawOrigin(0, 0);
    BitBlt(DevDC, MetaClipRect.A.X, MetaClipRect.A.Y,
      MetaClipRect.B.X - MetaClipRect.A.X, MetaClipRect.B.Y - MetaClipRect.A.Y,
      MemDC, MetaClipRect.A.X, MetaClipRect.A.Y, SrcCopy);
    DC := MemDC;
  end;
  WinGr.ForceEndDraw;
  WinGr.SelectWnd(0);
  ShowTrans(Vis, This);
  EndDraw := true;
  ClipRgn := 0;
  This^.ViewLock := 0
end;

procedure SubtractHiddenSources(Vis: PVis; Delta: Objects.TPoint; This: PGView);
var
  Wnd, W: HWnd;
  R: Objects.TRect;
  P: PGView;
begin
  P := This;
  while (P <> nil) and (P^.State and sfRoot = 0) do
    P := P^.GOwner;
  If (P = nil) or (P^.Wnd = 0) then Exit;
  Wnd := P^.Wnd;
  GetWindowRect(Wnd, WinTypes.TRect(R));
  { Consider Wnd's client area only
  }
  Dec(Delta.x, R.A.x + GetSystemMetrics(sm_CXFrame));
  Dec(Delta.y, R.A.y + GetSystemMetrics(sm_CYFrame)
    - GetSystemMetrics(sm_CYBorder) + GetSystemMetrics(sm_CYCaption));
  { Subtract siblings }
  W := GetWindow(Wnd, gw_HWndPrev);
  while W <> 0 do
  begin
    If GetWindowLong(W, gwl_Style) and ws_Visible <> 0
    then begin
      GetWindowRect(W, WinTypes.TRect(R));
      R.Move(Delta.x, Delta.y);
      SubtractRegion(vis, R);
    end;
    W := GetWindow(W, gw_HWndPrev);
  end;
  { Subtract children }
  W := GetWindow(Wnd, gw_Child);
  while W <> 0 do
  begin
    If GetWindowLong(W, gwl_Style) and ws_Visible <> 0
    then begin
      GetWindowRect(W, WinTypes.TRect(R));
      R.Move(Delta.x, Delta.y);
      SubtractRegion(vis, R);
    end;
    W := GetWindow(W, gw_HWndNext);
  end;
end;


