
{*******************************************************}
{                                                       }
{       Graphics Vision Unit                            }
{                                                       }
{	Portions copyright (c) 1992 Borland Intl.       }
{       Copyright (c) 1994 Stefan Milius                }
{	Copyright (c) 1997 Matthias Koeppe              }
{                                                       }
{*******************************************************}

{ $Id: gvviews.pas 1.3 1998/11/21 19:00:29 mkoeppe Exp mkoeppe $	}

Unit GVViews;

{$ifndef FPK}
{$A+,B-,D+,F-,G+,O-,R-,S-,X+,I-}
{$endif}

interface

{$ifdef FPK}
uses CallSpec, Objects, Drivers, Views, Memory,
  {VgaMem,} BGI, GvFPK, ExtGraph, GvWinNum;
{$i fpkdef.pp}
{$else}
{$ifdef Windows}
uses Wintypes, {$ifndef VER80} Win31, {$else} Messages, {$endif}
  Objects, Drivers, Views, WinProcs, WinGr;
{$else}
uses Objects, Drivers, Views, Memory;
{$endif}
type
  Sw_Integer = Integer;
  Sw_Word    = Word;
{$endif}

Const

{ Color Palettes }

  CFrame         = #1#2#3#4#5#6#7#8#9#10#11#12#13;
  CScrollBar     = #14#15#16#17#18;
  CScroller      = #19#20#21#22;
  CListViewer    = #134#135#136#137#138#139;
{$ifdef FPK}
  CScrollbarBack = #18#18;
{$else}
  CScrollbarBack = #18;
{$endif}

  CWhiteWindow   = #10#11#12#13#14#15#16#17#18#19#20#21#22#23#24#25#26#27#28+
		   #29#30#31;
  CGrayWindow    = #32#33#34#35#36#37#38#39#40#41#42#43#44#45#46#47#48#49#50+
		   #51#52#53;
  CCyanWindow    = #54#55#56#57#58#59#60#61#62#63#64#65#66#67#68#69#70#71#72+
		   #73#74#75;

{ TGView State masks }

  sfVisible     = $0001;    sfCursorVis   = $0002;    sfCursorIns   = $0004;
  sfRoot	= $0008;    sfActive      = $0010;    sfSelected    = $0020;
  sfFocused     = $0040;    sfDragging    = $0080;    sfDisabled    = $0100;
  sfModal       = $0200;    sfDefault     = $0400;    sfExposed     = $0800;
  sfFirstPass   = $4000;    sfTransparent = $8000;

{ TGView Option masks }

  ofSelectable  = $0001;    ofTopSelect   = $0002;    ofFirstClick  = $0004;
  ofStoreBack   = $0008;    ofPreProcess  = $0010;    ofPostProcess = $0020;
  ofBuffer      = $0040;    ofTileable    = $0080;    ofCenterX     = $0100;
  ofCenterY     = $0200;    ofCentered    = $0300;    ofValidate    = $0400;
  ofMetaFile    = $0800;    ofHoldFirst   = $1000;    ofBufferOne   = $2000;
  ofFirstPass   = $4000;    ofRooting     = $8000;

{ TGView GrowMode masks }

  gfGrowLoX     = $01;      gfGrowLoY     = $02;      gfGrowHiX    = $04;
  gfGrowHiY     = $08;      gfGrowAll     = $0F;      gfGrowRel    = $10;
  gfRedrawX     = $20;      gfRedrawY     = $40;      gfAlign      = $80;

{ TGView DragMode masks }

  dmDragMove  = $01;
  dmDragGrow  = $02;
  dmClickThru = $08;
  dmLimitLoX  = $10;
  dmLimitLoY  = $20;
  dmLimitHiX  = $40;
  dmLimitHiY  = $80;
  dmLimitAll  = $F0;

{ Standard command codes }

  cmValid   = 0;            cmQuit    = 1;            cmError   = 2;
  cmMenu    = 3;            cmClose   = 4;            cmZoom    = 5;
  cmResize  = 6;            cmNext    = 7;            cmPrev    = 8;
  cmHelp    = 9;

{ TDialog standard commands }

  cmOK      = 10;           cmCancel  = 11;           cmYes     = 12;
  cmNo      = 13;           cmDefault = 14;

{ TButton message commands }

  cmGrabDefault = 15;       cmReleaseDefault = 16;

{ Application command codes }

  cmCut     = 20;           cmCopy    = 21;             cmPaste   = 22;
  cmUndo    = 23;           cmClear   = 24;             cmTile    = 25;
  cmCascade = 26;           cmAltTile = 27;

{ TWindow message commands }

  cmHeyYou = 40;
  cmSelectWindowNum = 55;

{ Standard messages }

  cmReceivedFocus     = 50;
  cmReleasedFocus     = 51;
  cmCommandSetChanged = 52;

{ TScrollBar messages }

  cmScrollBarChanged  = 53;
  cmScrollBarClicked  = 54;

{ TListViewer messages }

  cmListItemSelected  = 56;

{ Help contexts }

  hcNoContext = 0;
  hcDragging = 1000;
  hc = 1000;

{ ListViewer flags }

  lfPartialLines    = $0001;
  lfVerticalFirst   = $0000;
  lfHorizontalFirst = $0002;

{ Event class }

  evPositionalCtx = $800;

{ StandardScrollbar flags }

  sbHorizontal     = $0001;
  sbVertical       = $0002;
  sbHandleKeyBoard = $0004;
  sbDoScrolling    = $0008;

{ ScrollBar flags }

  sbLeftArrow  = 0;  sbRightArrow = 1;  sbPageLeft   = 2;  sbPageRight  = 3;
  sbUpArrow    = 4;  sbDownArrow  = 5;  sbPageUp     = 6;  sbPageDown   = 7;
  sbIndicator  = 8;

{ TWindow Flags masks }

  wfMove       = $01;
  wfGrow       = $02;
  wfClose      = $04;
  wfZoom       = $08;
  wfShowNumber = $10;
  wfBackground = $20;
  wfShowTitle  = $40;
  wfModal      = $80;
  wfHideClose  = $100;
  wfRootFrame  = $200;
  wf3DFrame    = $400;

{ TWindow number constants }

  wnNoNumber    =  0;
  wnJustANumber = 10;

{ TWindow palette entries }

  wpWhiteWindow = 0;
  wpGrayWindow  = 1;
  wpCyanWindow  = 2;

{ ResMode values }

  rmDnRi  = 1;  rmUpRi  = 2;  rmDnLe  = 3;  rmUpLe  = 4;  rmLeft  = 5;
  rmRight = 6;  rmUp    = 7;  rmDown  = 8;

{ Minimal window size }

  GMinWinSize : TPoint = (X: 170; Y: 100);

{ Window field size }

  IconSize = 20;

{ Window dragging add constant }

  WinDragAdd: Integer = 5;

{ Window commands to enable/disable }

  WindowCmds: TCommandSet = [cmNext, cmPrev, cmTile, cmCascade, cmAltTile];

{ Maximum view size for FirstPass draw in square pixels
  (size calcule of TGView.CalcFirstPass)
}
  FirstPassMaximum: LongInt = 10000;

type
  TPhase = (phFocused, phPreProcess, phPostProcess);

{ Visibility abstract data structure
}
  PVis = pointer;

{ Rectangle pointer
}
  PRect = ^TRect;

{ TView class
}
  PGGroup = ^TGGroup;
  PGView = ^TGView;

  TGView = Object (TView)
    GOwner: PGGroup;
    GNext: PGView;
    CursorSize: TPoint;
    CursorFlag: Boolean;
    CursorLock: ShortInt;
    ViewLock: Integer;
  {$ifdef Windows}
    Wnd: TWinHandle;
  {$endif Windows}
    constructor Init (var Bounds: TRect);
    constructor Load (var S: TStream);
    destructor Done; virtual;
    procedure BeginScroll(Delta: TPoint; SubRect: PRect);
    procedure CalcBounds(var Bounds: TRect; Delta: TPoint); virtual;
    procedure CalcFirstPass; virtual;
    procedure ChangeBounds (var R: TRect); virtual;
    procedure ChMCursor; virtual;
    function CreateDragView: PGView; virtual;
    procedure DragView(Event: TEvent; Mode: Byte; var Limits: TRect;
       MinSize, MaxSize: TPoint; ResMode: Byte);
    procedure Draw; virtual;
    procedure DrawClipped(Clip: PVis; VisOwner: PGView); virtual;
    procedure DrawCursor;
    procedure DrawView;
    procedure DrawVisible; virtual;
    procedure DrawVisibleLocal(Local: pointer);
    procedure EndModal(Command: Word); virtual;
    procedure EndScroll;
    function Exposed: Boolean;
    function Focus: Boolean;
    procedure FreeBack; virtual;
    function GetColor(Color: Word):Word;
    function GetHelpCtx: Word; virtual;
    procedure GetPeerViewPtr(var S: TStream; var P);
    function GetStandardFont: Word; virtual;
    function GetVisibility: PVis;
    procedure GrowTo(X, Y: Integer);
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure HideCursor;
    procedure HideDrawClipped(Clip: PVis; VisOwner: PGView); virtual;
    {procedure KeyEvent (var Event: TEvent);}
    procedure Locate (var Bounds: TRect); virtual;
    procedure MakeFirst;
    procedure MakeSemiGlobal(Source: TPoint; var Dest: TPoint);
    function MouseEvent(var Event: TEvent; Mask: Word): Boolean;
    procedure MoveTo(X, Y: Integer);
    function NextView: PGView;
    function Prev: PGView;
    function PrevView: PGView;
    procedure PutInFrontOf(Target: PGView); virtual;
    procedure PutPeerViewPtr(var S: TStream; P: PGView);
    procedure RestoreBackground; virtual;
    procedure RestoreViewPort; virtual;
    procedure Select;
    procedure SetCursor(X, Y: Integer);
    procedure SetState(AState: Word; Enable: Boolean); virtual;
    function SetSubRect(var SubRect: TRect): Boolean;
    procedure SetTitle(ATitle: string); virtual;
    procedure SetViewPort; virtual;
    procedure ShowCursor;
    procedure ShowDrawClipped(Clip: PVis; VisOwner: PGView); virtual;
    procedure Store(var S: TStream);
    procedure StoreBack; virtual;
    function TopView: PGView;
  {$ifdef Windows}
    procedure CreateWin;
    function CreateRootWindow: Integer; virtual;
    procedure DestroyWin;
    function FindWin: PGView;
    procedure MakeClient(var R: TRect);
    procedure MakeNonClient(var R: TRect);
    procedure UpdateWinOrder;
    procedure UpdateWinPos;
    procedure UpdateWinTitle; virtual;
    procedure UseWinPos(var Pos: TWINDOWPOS);
  {$endif Windows}
  private
    Vis: PVis;
    Back: pointer;
    procedure CopyDrawView(Source: PVis; var Bounds: TRect; Delta: TPoint);
    function GetOwnerClippedBounds(var Bounds: TRect): Boolean;
    function HasInvisiblePart: Boolean;
    procedure SubtractInvisible(var AVis: PVis; VisOwner: PGView;
      SubTrans: Boolean);
  End;

{ TFrame object }

  { Palette layout }
  { 1  = Selected frame }
  { 2  = Normal frame }
  { 3  = Modal frame }
  { 4  = Zoomfield }
  { 5  = Normal background }
  { 6  = Selected background }
  { 7  = Normal title }
  { 8  = Selected title }
  { 9  = Close-/Zoomfield outside }
  { 10 = Closefield frame }
  { 11 = Closefield inside }
  { 12 = Closefield shadow }

  PFrame = ^TFrame;
  TFrame = object(TGView)
    TitleSize: Integer;
    constructor Init(var Bounds: TRect);
    constructor Load(var S: TStream);
    procedure CalcFirstPass; virtual;
    procedure ChMCursor; virtual;
    procedure Draw; virtual;
    procedure DrawZoomField(Down: Boolean); virtual;
    procedure GetClientRect(var R: TRect); virtual;
    procedure GetTitleRect(var R: TRect); virtual;
    function GetPalette: PPalette; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure SetState(AState: Word; Enable: Boolean); virtual;
    procedure Store(var S: TStream);
    function Valid(Command: Word): Boolean; virtual;
  private
    OldC: Integer;
    function Zoomed: Boolean;
  End;

{ TBackGround object }

  PBackGround = ^TBackGround;
  TBackGround = Object(TGView)
    constructor Init(var Bounds: TRect);
    procedure Draw; virtual;
  End;

{ TScrollbarBack object }

  PScrollBarBack = ^TScrollBarBack;
  TScrollBarBack = object(TBackground)
    function GetPalette: PPalette; virtual;
  end;

{ TScrollBar object }

  { Palette layout }
  { 1 = Background }
  { 2 = Frame }
  { 3 = Buttons }
  { 4 = Button shadows }
  { 5 = Page area }

  PScrollBar = ^TScrollBar;
  TScrollBar = object(TGView)
    ButtonSize: Byte;
    Flags: Byte;
    Value: Integer;
    Min: Integer;
    Max: Integer;
    PgStep: Integer;
    ArStep: Integer;
    constructor Init(var Bounds: TRect);
    constructor Load(var S: TStream);
     { procedure ChangeBounds (var Bounds: TRect); virtual; }
    procedure Draw; virtual;
    procedure DrawUpButton(Down: Boolean); virtual;
    procedure DrawDnButton(Down: Boolean); virtual;
    procedure DrawIndicator(V: Integer; Down: Boolean); virtual;
    function GetPalette: PPalette; virtual;
    function GetPosition(V: Integer): Integer; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure RestoreIndicator(OldV: Integer); virtual;
    procedure ScrollDraw; virtual;
    function ScrollStep(Part: Integer): Integer; virtual;
    procedure SetParams(AValue, AMin, AMax, APgStep, AArStep: Integer);
    procedure SetRange(AMin, AMax: Integer);
    procedure SetState(AState: Word; Enable: Boolean); virtual;
    procedure SetStep(APgStep, AArStep: Integer);
    procedure SetValue(AValue: Integer);
    procedure Store(var S: TStream);
  end;

{ TScroller object }

  { Palette Layout }
  { 1 = Normal text }
  { 2 = Selected text }
  { 3 = Normal background }
  { 4 = Selected background }

  PScroller = ^TScroller;
  TScroller = object(TGView)
    HScrollBar: PScrollBar;
    VScrollBar: PScrollBar;
    Delta: TPoint;
    Limit: TPoint;
    TextSize: TPoint;
    constructor Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar);
    constructor Load(var S: TStream);
    procedure ChangeBounds(var Bounds: TRect); virtual;
    function GetPalette: PPalette; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure ScrollDraw; virtual;
    procedure ScrollTo(X, Y: Integer);
    procedure SetLimit(X, Y: Integer);
    function ShownRows: Integer;
    function ShownCols: Integer;
    procedure Store(var S: TStream);
  private
    ScrollLock: Byte;
  end;

{ TListViewer object }

  { Palette layout }
  { 1 = Background }
  { 2 = Frame }
  { 3 = Normal Text }
  { 4 = Selected Text }
  { 5 = Normal Background }
  { 6 = Selected Background }

  PListViewer = ^TListViewer;
  TListViewer = object(TGView)
    ScrollBar: PScrollBar;
    TopItem: Integer;
    Focused: Integer;
    Range: Integer;
    Flags: Word;
    NumCols: Integer;
    constructor Init(var Bounds: TRect; AScrollBar: PScrollBar);
    constructor Load(var S: TStream);
    procedure ChangeBounds(var Bounds: TRect); virtual;
    procedure Draw; virtual;
    procedure DrawItem(Item: Integer); virtual;
    procedure DrawItemText(Item: Integer; R: TRect); virtual;
    procedure FocusItem(Item: Integer); virtual;
    function GetItemHeight: Integer; virtual;
    procedure GetItemRect(Item: Integer; var R: TRect); virtual;
    procedure GetItemSubRect(var R: TRect); virtual;
    function GetNumRows: Integer;
    function GetPageSize: Integer; virtual;
    function GetPalette: PPalette; virtual;
    function GetText(Item: Integer; MaxLen: Integer): String; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    function IsSelected(Item: Integer): Boolean; virtual;
    procedure SelectItem(Item: Integer); virtual;
    procedure SetNumCols(Num: Integer);
    procedure SetRange(ARange: Integer);
    procedure SetState(AState: Word; Enable: Boolean); virtual;
    procedure Store(var S: TStream);
  end;

{ TDragRect object }

  PDragRect = ^TDragRect;
  TDragRect = object(TGView)
    constructor Init(var Bounds: TRect);
    procedure Draw; virtual;
    procedure HideDrawClipped(Clip: PVis; VisOwner: PGView); virtual;
    procedure Locate(var Bounds: TRect); virtual;
  end;

{ TPeerList record }

  PPeerList = ^TPeerList;
  TPeerList = record
    Number: Integer;
    Master: ^PGView;
    Next: PPeerList
  End;

{ TGGroup object }

  TGGroup = object(TGView)
    Last: PGView;
    Current: PGView;
    Phase: TPhase;
    StandardFont: Word;
    EndState: Word;
    constructor Init(var Bounds: TRect);
    constructor Load(var S: TStream);
    destructor Done; virtual;
    procedure Awaken; virtual;
    procedure CalcFirstPass; virtual;
    procedure ChangeBounds(var Bounds: TRect); virtual;
    procedure ChMCursor; virtual;
    function DataSize: Sw_Word; virtual;
    procedure Delete(P: PGView);
    procedure Draw; virtual;
    procedure DrawClipped(Clip: PVis; VisOwner: PGView); virtual;
    procedure DrawVisible; virtual;
    procedure EndModal(Command: Word); virtual;
    procedure EventError(var Event: TEvent); virtual;
    function Execute: Word; virtual;
    function ExecView(P: PGView): Word;
    function First: PGView;
    function FirstThat (Test: Pointer): PGView;
    function FirstThatInMethod(Test: pointer; Obj: pointer): PGView;
    function FocusNext(Forwards: Boolean): Boolean;
    procedure ForEach (Action: Pointer);
    procedure ForEachInMethod(Action: pointer; Obj: pointer);
    procedure GetData(var Rec); virtual;
    function GetHelpCtx: Word; virtual;
    procedure GetSubViewPtr(var S: TStream; var P);
    procedure HandleEvent(var Event: TEvent); virtual;
    function Insert(P: PGView): pointer;
    function InsertBefore(P, Target: PGView): pointer;
    procedure Lock;
    procedure PutSubViewPtr(var S: TStream; P: PGView);
    procedure Redraw;
    procedure SelectNext(Forwards: Boolean);
    procedure SetData(var Rec); virtual;
    procedure SetState(AState: Word; Enable: Boolean); virtual;
    procedure Store(var S: TStream);
    procedure UnLock;
    function Valid(Command: Word): Boolean; virtual;
  private
    LockFlag: Byte;
    function At(Index: Integer): PGView;
    function FirstMatch(AState: Word; AOptions: Word): PGView;
    function FindNext(Forwards: Boolean): PGView;
    function IndexOf(P: PGView): Integer;
    procedure InsertView(P, Target: PGView);
    procedure RemoveView(P: PGView);
    procedure ResetCurrent;
    procedure SetCurrent(P: PGView; Mode: SelectMode);
  end;

{ TWindow object }

  { Palette layout }
  { 1  = Background }
  { 2  = Frame active }
  { 3  = Frame not active }
  { 4  = Frame modal }
  { 5  = Zoomfield }
  { 6  = Title background normal}
  { 7  = Title background selected }
  { 8  = Title normal}
  { 9  = Title selected }
  { 10 = Close- and Zoomfield outside }
  { 11 = Closefield frame }
  { 12 = Closefield inside }
  { 13 = Closefield shadow }
  { 14 = Scrollbar background }
  { 15 = Scrollbar frame }
  { 16 = Scrollbar buttons }
  { 17 = Scrollbar button shadows }
  { 18 = Scrollbar page area }
  { 19 = Scroller normal text }
  { 20 = Scroller selected text }
  { 21 = Scroller normal background }
  { 22 = Scroller selected background }

  PWindow = ^TWindow;
  TWindow = Object (TGGroup)
    Flags: Word;
    ZoomRect: TRect;
    Number: Integer;
    Palette: Integer;
    Frame: PFrame;
    Title: PString;
    ScrBBck: PBackground;
    constructor Init(var Bounds: TRect; ATitle: TTitleStr; ANumber: Integer);
    constructor Load(var S: TStream);
    destructor Done; virtual;
    procedure Close; virtual;
    procedure GetClientRect(var R: TRect); virtual;
    function GetPalette: PPalette; virtual;
    function GetTitle(MaxSize: Integer): TTitleStr; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure InitFrame; virtual;
    procedure SetFlags(AFlags: Word); virtual;
    procedure SetState(AState: Word; Enable: Boolean); virtual;
    procedure SetTitle(ATitle: string); virtual;
    procedure SizeLimits(var Min, Max: TPoint); virtual;
    function StandardScrollBar(AOptions: Word): PScrollBar;
    procedure Store(var S: TStream);
    function Valid(Command: Word): Boolean; virtual;
    procedure Zoom; virtual;
    {$ifdef Windows}
    function CreateRootWindow: Integer; virtual;
    procedure UpdateWinTitle; virtual;
    {$endif Windows}
  private
    function AmLastWindow: Boolean;
  End;

{ the drag view
}
const
  TheDragView: PGView = nil;
  TheDraggedView: PGView = nil;

{ the application group
}
const
  ApplicationGroup: PGGroup = nil;

{ GVViews registration procedure }

  procedure RegisterGVViews;

{ Stream registration records }

const

  RGView: TStreamRec = (
    ObjType: 102;
    VmtLink: Ofs(TypeOf(TGView)^);
    Load: @TGView.Load;
    Store: @TGView.Store);

  RFrame: TStreamRec = (
    ObjType: 103;
    VmtLink: Ofs(TypeOf(TFrame)^);
    Load: @TFrame.Load;
    Store: @TFrame.Store);

  RBackground: TStreamRec = (
    ObjType: 104;
    VmtLink: Ofs(TypeOf(TBackground)^);
    Load: @TBackground.Load;
    Store: @TBackground.Store);

  RScrollBar: TStreamRec = (
    ObjType: 105;
    VmtLink: Ofs(TypeOf(TScrollBar)^);
    Load: @TScrollBar.Load;
    Store: @TScrollBar.Store);

  RScroller: TStreamRec = (
    ObjType: 106;
    VmtLink: Ofs(TypeOf(TScroller)^);
    Load: @TScroller.Load;
    Store: @TScroller.Store);

  RListViewer: TStreamRec = (
    ObjType: 107;
    VmtLink: Ofs(TypeOf(TListViewer)^);
    Load: @TListViewer.Load;
    Store: @TListViewer.Store);

  RGGroup: TStreamRec = (
    ObjType: 108;
    VmtLink: Ofs(TypeOf(TGGroup)^);
    Load: @TGGroup.Load;
    Store: @TGGroup.Store);

  RWindow: TStreamRec = (
    ObjType: 109;
    VmtLink: Ofs(TypeOf(TWindow)^);
    Load: @TWindow.Load;
    Store: @TWindow.Store);

{ *** Visibility Detection & Drawing Support ***
}

{ Utility macros
}
{$ifndef FPK}
type
  FramePointer = Word;

function PrevBP: FramePointer;
inline(
  $8B/ $46/ $00 { mov ax, [bp] }
);

function CurBP: FramePointer;
inline(
  $8B/ $C5 { mov ax, bp }
);
{$endif FPK}

{ Operations on visibility structures
}
function NewVis(var Bounds: TRect): PVis;
procedure SubtractRegion(Vis: PVis; var Bounds: TRect);
procedure IndirectRegion(Vis: PVis; var Bounds: TRect; Transparent: PGView);
procedure Unindirect(Vis: PVis; GOwner: PGView);
function Unraw(Vis: PVis): PVis;
procedure ClipVis(Vis: PVis; var Clip: TRect);
procedure SubtractVis(Vis, SubVis: PVis);
function IntersectVis(Vis1, Vis2: PVis): PVis;
function IntersectDelta(Vis: PVis; Delta: TPoint): PVis;
procedure MoveVis(Vis: PVis; Delta: TPoint);
function SortVis(Vis: PVis; Delta: TPoint): PVis;
function CopyVis(Vis: PVis): PVis;
procedure FreeVis(Vis: PVis);
procedure SubtractHiddenSources(Vis: PVis;
  Delta: TPoint; This: PGView);
function EmptyVis(Vis: PVis): Boolean;

{ Visibility drawing support
}
procedure DrawVis(Vis: PVis; Origin: TPoint; This: PGView);
procedure DrawVisLocal(Vis: PVis; Origin: TPoint;
  This: PGView; Local: pointer; Frame: FramePointer);
procedure DrawFirstPass(P: PGView; This: PGView);

{ Drawing operation frame
}
function BeginDraw(Vis: PVis; Origin: TPoint;
  This: PGView; Modifying: Boolean): Boolean;
function EndDraw(Vis: PVis; This: PGView): Boolean;

{ Scroll view proc
}
procedure CritUnionDelta(Delta: TPoint);
procedure ScrollView(This: PGView; Delta: TPoint; SubRect: PRect);
procedure ScrollGroup(This: PGGroup; Delta: TPoint);

const
  GlobalOptions: Word = $FFFF {and not (ofBuffer + ofMetafile)};

implementation

{$ifdef FPK}
{$else}
{$ifdef Windows}
uses WinRes, ExtGraph, VgaMem, GVisible, GvWinNum, GvApp;
{$else}
uses Crt, Gr, MetaGr, ExtGraph, MyFonts, GVDriver, MyMouse, VGAMem,
     GVisible, GVWinNum, WinRes;
{$endif}
{$endif}

type
  TFixupList = array[1..4096] of Pointer;
  PFixupList = ^TFixupList;

const
  OwnerGroup: PGGroup = nil;
  FixupList: PFixupList = nil;
  TheTopView: PGView = nil;

{ Helper functions }

{$ifndef FPK}

function Min (x, y: Integer): Integer; Assembler;
Asm
	MOV	AX, X
	CMP	AX, Y
	JLE	@@1
	MOV	AX, Y
@@1:
End;

function Max (x, y: Integer): Integer; Assembler;
Asm
	MOV	AX, X
	CMP 	AX, Y
	JGE	@@1
	MOV	AX, Y
@@1:
End;

function Ranges (x, Lx, Hx: Integer): Boolean; Assembler;
Asm
	MOV	AX, X
	CMP	AX, Lx
	JL	@@1
	CMP	AX, Hx
	JG	@@1
	MOV	AX, 1
	JMP	@@2
@@1:	XOR	AX, AX
@@2:
End;
{$endif}

{************************** TGView object ***********************************}

constructor TGView.Init(var Bounds: TRect);
Begin
  TView.Init (Bounds);
  EventMask := EventMask or (evTimer + evPositionalCtx {$ifdef Windows} + evSystem {$endif Windows} );
  Options := Options or (ofBuffer + ofMetafile + ofFirstPass);
  CursorLock := 2;
End;

constructor TGView.Load(var S: TStream);
Begin
  TView.Load (S);
  S.Read (CursorSize, SizeOf (CursorSize));
  S.Read (CursorLock, SizeOf (CursorLock));
  GOwner:=nil;
  GNext:=nil;
End;

destructor TGView.Done;
Begin
  Hide;
  If GOwner <> nil then GOwner^.Delete(@Self);
End;

procedure HideTrans(Vis: PVis; This: PGView); forward;
procedure ShowTrans(Vis: PVis; This: PGView); forward;

var
  ScrollView_avis, ScrollView_ivis: PVis;
  ScrollView_Delta: TPoint;

procedure TGView.BeginScroll(Delta: TPoint; SubRect: PRect);
var
  P: TPoint;
  R: TRect;
begin
  P.X := 0; P.Y := 0;
  MakeSemiGlobal(P, P);
  ScrollView_Delta := Delta;
  ScrollView_avis := GetVisibility;
  If GetVgaMemCaps and vmcCopy <> 0
  then begin
    If SubRect <> nil then Begin
      R := SubRect^;
      R.Move(P.x, P.y);
      ClipVis(ScrollView_avis, R)
    End;
    ScrollView_ivis := IntersectDelta(ScrollView_avis, Delta);
    SubtractHiddenSources(ScrollView_ivis, Delta, @Self);
    SubtractVis(ScrollView_avis, ScrollView_ivis);
    HideTrans(ScrollView_avis, @Self);     { remove trans source overlaps }
  end;
end; { TGView.BeginScroll }

procedure TGView.CalcBounds(var Bounds: TRect; Delta: TPoint);
var S, D: Integer;
    Min, Max: TPoint;

 procedure Grow(var I: {$ifdef FPK} Sw_Integer {$else} Integer {$endif});
 Begin
   if GrowMode and gfGrowRel = 0 then Inc(I, D) else
     I := LongDiv((LongMul(I, S) + (S - D) shr 1), (S - D));
 End;

 function Range(Val, Min, Max: Integer): Integer;
 Begin
   if Val < Min then Range := Min else
     if Val > Max then Range := Max else
       Range := Val;
 End;

Begin
  GetBounds(Bounds);
  S := GOwner^.Size.X;
  D := Delta.X;
  if GrowMode and gfGrowLoX <> 0 then Grow(Bounds.A.X);
  if GrowMode and gfGrowHiX <> 0 then Grow(Bounds.B.X);
  S := GOwner^.Size.Y;
  D := Delta.Y;
  if GrowMode and gfGrowLoY <> 0 then Grow(Bounds.A.Y);
  if GrowMode and gfGrowHiY <> 0 then Grow(Bounds.B.Y);
  SizeLimits(Min, Max);
  Bounds.B.X := Bounds.A.X + Range(Bounds.B.X - Bounds.A.X, Min.X, Max.X);
  Bounds.B.Y := Bounds.A.Y + Range(Bounds.B.Y - Bounds.A.Y, Min.Y, Max.Y);
End;

procedure TGView.CalcFirstPass;
begin
  SetState(sfFirstPass,
    (State and sfTransparent = 0) and
    (Options and GlobalOptions and ofFirstPass <> 0) and
    (LongMul(Size.X, Size.Y) < FirstPassMaximum))
end;

procedure TGView.ChangeBounds(var R: TRect);
var
  aVis: PVis;
  P: TPoint;

	procedure DoDrawFirstPass; {$ifndef FPK}far;{$endif}
	begin
	  DrawFirstPass(GOwner, @Self);
	end;

Begin
  SetBounds(R);
  CalcFirstPass;
  {$ifdef Windows}
  If Wnd <> 0
  then UpdateWinPos else
  {$endif Windows}
  If (Options and GlobalOptions and ofBuffer <> 0) and GetState(sfTransparent)
  then begin
    aVis := GetVisibility;
    Unindirect(aVis, GOwner);
    P.X := 0; P.Y := 0; MakeSemiGlobal(P, P);
    DrawVisLocal(aVis, P, @Self, @DoDrawFirstPass, CurBP);
    FreeVis(aVis);
    {SetViewport;
    DrawFirstPass(GOwner, @Self);
    RestoreViewport}
  end
  else
    DrawVisible
End;

{$ifdef NOIASM}
procedure TGView.ChMCursor;
begin
  NewNum := MCurStandard
end;
{$else}
procedure TGView.ChMCursor; Assembler;
Asm
	mov     ax, MCurStandard
	mov	NewNum, ax
End;
{$endif}

procedure TGView.CopyDrawView(Source: PVis; var Bounds: TRect; Delta: TPoint);
var
  AVis, IVis, SVis: PVis;
  P1, P2, P3: TPoint;
  Opt: Word;
  OldBounds: TRect;

 procedure DoCopy; {$ifndef FPK}far;{$endif}
 Begin
   CritUnionDelta(Delta);
   CopyScreen(P1.X, P1.Y, P2.X, P2.Y, P3.X, P3.Y);
   SetBounds(Bounds);
 End;

Begin
  GetBounds(OldBounds);
  SetBounds(Bounds);
  If GetVgaMemCaps and vmcCopy = 0
  then begin
    FreeVis(Source);
    DrawView
  end
  else begin
    AVis := GetVisibility;
    If AVis <> nil
    then begin
      MoveVis(Source, Delta);
      SubtractHiddenSources(Source, Delta, @Self);

      IVis := IntersectVis(AVis, Source);	{ joins Trans }
      SubtractVis(AVis, Source);
      FreeVis(Source);

      If IVis <> nil
      then begin
	P3.X := 0; P3.Y := 0; MakeSemiGlobal(P3, P3);
	P1.X := P3.X - Delta.X;
	P1.Y := P3.Y - Delta.Y;
	P2.X := P1.X + Size.X;
	P2.Y := P1.Y + Size.Y;
	SVis := SortVis(IVis, Delta);	{ now destroys IVis }
	Opt := Options;
	SetBounds(OldBounds);
	Options := Opt and not (ofBuffer+ofMetafile); { CopyScreen is not bufferable }
	DrawVisLocal(SVis, P3, @Self, @DoCopy, CurBP);
	Options := Opt;
	SetBounds(Bounds);
	FreeVis(SVis);
      end;
      DrawClipped(AVis, @Self);		{ now destroys AVis }
    end
  end
End;

{$ifdef Windows}
procedure TGView.CreateWin;
var
  Orig: TPoint;
  P: PGView;
  W: TWinHandle;
  ex: LongInt;
  Cmd: Integer;
begin
  P := GOwner;
  Orig := Origin;
  while (P <> nil) and (P^.Wnd = 0) do
  begin
    Inc(Orig.X, P^.Origin.X);
    Inc(Orig.Y, P^.Origin.Y);
    P := P^.GOwner;
  end;
  If P = nil
  then W := 0
  else W := P^.Wnd;
  If W = 0
  then begin
    Cmd := CreateRootWindow;
    UpdateWinPos;
  end
  else begin
    ex := 0;
    If GetState(sfTransparent)
    then ex := ex or ws_ex_Transparent;
    Wnd := CreateWindowEx(ex, WindowClass, nil, ws_Child or ws_ClipSiblings or ws_ClipChildren,
      Orig.X, Orig.Y, Size.X, Size.Y, W, 0, hInstance, @Self);
    Cmd := sw_Show
  end;
  UpdateWinOrder;
  UpdateWinTitle;
  ShowWindow(Wnd, Cmd)
end;

function TGView.CreateRootWindow: Integer;
begin
  Wnd := CreateWindow(WindowClass, nil,
    ws_Overlapped or ws_DlgFrame or ws_ClipSiblings or ws_ClipChildren or gvs_NoFrame,
    0, 0, 100, 100, Main^.Wnd, 0, hInstance, @Self);
  CreateRootWindow := sw_ShowNoActivate;
end;

procedure TGView.DestroyWin;
begin
  if Main <> nil then SetFocus(Main^.Wnd);
  DestroyWindow(Wnd);
  Wnd := 0;
end;

function TGView.FindWin: PGView;
var
  P: PGView;
begin
  P := @Self;
  while (P <> nil) and (P^.Wnd = 0) do
    P := P^.GOwner;
  FindWin := P
end;

function LockWinPos(Delta: Integer): Integer;
const
  Lock: Integer = 0;
begin
  Inc(Lock, Delta);
  LockWinPos := Lock
end;

procedure TGView.UpdateWinOrder;
var
  P: PGView;
  W: TWinHandle;
begin
  P := PrevView;
  while (P <> nil) and (P^.Wnd = 0) do
    P := P^.PrevView;
  If P = nil
  then W := 0
  else W := P^.Wnd;
  LockWinPos(+1);
  SetWindowPos(Wnd, W, 0, 0, 0, 0, swp_NoMove + swp_NoSize + swp_NoActivate);
  LockWinPos(-1);
end;

procedure TGView.MakeClient(var R: TRect);
var
  Info: TNCCALCSIZE_PARAMS;
begin
  TRect(Info.rgrc[0]) := R;
  Info.lppos := nil;
  SendMessage(Wnd, wm_NCCalcSize, 0, LongInt(@Info));
  R := TRect(Info.rgrc[0]);
end;

procedure TGView.MakeNonClient(var R: TRect);
var
  Info: TNCCALCSIZE_PARAMS;
begin
  TRect(Info.rgrc[0]).Assign(0, 0, 100, 100);
  Info.lppos := nil;
  SendMessage(Wnd, wm_NCCalcSize, 0, LongInt(@Info));
  with TRect(Info.rgrc[0]) do
  begin
    Dec(R.A.X, A.X);
    Dec(R.A.Y, A.Y);
    Inc(R.B.X, 100 - B.X);
    Inc(R.B.Y, 100 - B.Y)
  end;
end;

procedure TGView.UpdateWinPos;
var
  R: TRect;
begin
  {If LockWinPos(0) <= 0
  then} begin
    R.A := Origin;
    If GOwner <> nil then GOwner^.MakeSemiGlobal(R.A, R.A);
    R.B.X := R.A.X + Size.X;
    R.B.Y := R.A.Y + Size.Y;
    MakeNonClient(R);
    LockWinPos(+1);
    SetWindowPos(Wnd, 0, R.A.X, R.A.Y, R.B.X - R.A.X, R.B.Y - R.A.Y, swp_NoZOrder + swp_NoActivate);
    LockWinPos(-1);
  end
end;

procedure TGView.UpdateWinTitle;
begin
end;

procedure TGView.UseWinPos(var Pos: TWINDOWPOS);
var
  P: TPoint;
  R: TRect;
begin
  If LockWinPos(0) <= 0
  then begin
    P.X := 0; P.Y := 0;
    If GOwner <> nil then GOwner^.MakeSemiGlobal(P, P);
    R.Assign(Pos.x - P.X, Pos.y - P.Y,
      Pos.x - P.x + Pos.cx, Pos.y - P.y + Pos.cy);
    MakeClient(R);
    LockWinPos(+1);
    Locate(R);
    LockWinPos(-1);
  end;
end;

{$endif Windows}

{$ifdef Linux}
function TGView.CreateDragView: PGView;
begin
  { We can't use XORPUT, so self-dragging is the fastest way. }
  CreateDragView := @Self;
end;
{$else}
function TGView.CreateDragView: PGView;
var
  R: TRect;
  Rect: PGView;
begin
  GetBounds(R);
  Rect := New(PDragRect, Init(R));
  Rect^.State := Rect^.State or (State and sfRoot);
  GOwner^.InsertBefore(Rect, @Self);
  CreateDragView := Rect
end;

{$endif}

procedure TGView.DragView(Event: TEvent; Mode: Byte; var Limits: TRect;
       MinSize, MaxSize: TPoint; ResMode: Byte);
var P, S: TPoint;
    SaveBounds: TRect;
    SaveOpt: Word;
    Rect: PGView;

 procedure DoneRect;
 var
   R: TRect;
 Begin
   If Rect <> @Self
   then begin
     Rect^.GetBounds(R);
     Dispose(Rect, Done);
 {$ifndef FPK}
 {$ifndef Windows}
     If (GrowMode and gfAlign <> 0) and
	(R.B.X - R.A.X = Size.X) and (R.B.Y - R.A.Y = Size.Y) and
	(GrFlags and 1 = 0)
     then R.Move(((R.A.X - Origin.X + 4) and $FFF8) + Origin.X - R.A.X, 0);
 {$endif}
 {$endif}
     Locate(R);
   end
 End;

 procedure MoveGrow(X1, Y1, X2, Y2: Integer);
 var
   R: TRect;
 begin
   X2 := Min(Max(X2, MinSize.X), MaxSize.X);
   Y2 := Min(Max(Y2, MinSize.Y), MaxSize.Y);
   X1 := Min(Max(X1, Limits.A.X - X2 + 1), Limits.B.X - 1 - IconSize * 2);
   Y1 := Min(Max(Y1, Limits.A.Y - Y2 + 1), Limits.B.Y - 1 - IconSize);
   if Mode and dmLimitLoX <> 0 then X1 := Max(X1, Limits.A.X);
   if Mode and dmLimitLoY <> 0 then Y1 := Max(Y1, Limits.A.Y);
   if Mode and dmLimitHiX <> 0 then X1 := Min(X1, Limits.B.X - X2);
   if Mode and dmLimitHiY <> 0 then Y1 := Min(Y1, Limits.B.Y - Y2);
   R.Assign(X1, Y1, X1 + X2, Y1 + Y2);
   Rect^.Locate(R)
 end;

 procedure Change(DX, DY: Integer);
 begin
   if (Mode and dmDragMove <> 0) and (GetShiftState and $03 = 0) then
   begin
     Inc(P.X, DX);
     Inc(P.Y, DY);
   end else
   if (Mode and dmDragGrow <> 0) and (GetShiftState and $03 <> 0) then
   begin
     Inc(S.X, DX);
     Inc(S.Y, DY);
   end;
 end;

 procedure Update(X, Y: Integer);
 begin
   if Mode and dmDragMove <> 0 then
   begin
     P.X := X;
     P.Y := Y;
   end;
 end;

 procedure Correct(X, Y: Boolean);
 Begin
   With Rect^ do Begin
     If Y then Begin
       If Size.Y - Event.Where.Y + Origin.Y < MinSize.Y then
	 Event.Where.Y := Origin.Y + Size.Y - MinSize.Y;
       If Size.Y - Event.Where.Y + Origin.Y > MaxSize.Y then
	 Event.Where.Y := Origin.Y + Size.Y - MaxSize.Y;
       If (Mode and dmLimitLoY <> 0) and (Event.Where.Y < Limits.A.Y)
	 Then Event.Where.Y := Max(Limits.A.Y, Event.Where.Y);
     End;
     If X then Begin
       If Size.X - Event.Where.X + Origin.X < MinSize.X then
	 Event.Where.X := Origin.X + Size.X - MinSize.X;
       If Size.X - Event.Where.X + Origin.X > MaxSize.X then
	 Event.Where.X := Origin.X + Size.X - MaxSize.X;
       If (Mode and dmLimitLoX <> 0) and (Event.Where.X < Limits.A.X)
	 Then Event.Where.X := Max(Limits.A.X, Event.Where.X);
     End;
   End;
 End;

 procedure SetMouse;
 var P: TPoint;
 Begin
   With Rect^ do Begin
     P.X := Size.X div 2; P.Y := Size.Y div 2;
     MakeGlobal(P, P);
     SetMousePos(P.X, P.Y);
   End;
 End;

Begin
  If GOwner = nil then Exit;
  SaveOpt := Options;
  Options := Options and not ofHoldFirst;
  Rect := CreateDragView;
  SetState(sfDragging, True);
  TheDragView := Rect;
  TheDraggedView := @Self;
  GetBounds(SaveBounds);
  if Event.What = evMouseDown then
  begin
    If Mode and dmDragMove <> 0 then SetCurrentCursor (mcMove);
    If Mode and dmDragGrow <> 0 then
      Case ResMode Of
	rmDnRi, rmUpLe : SetCurrentCursor (mcResizeDnRi);
	rmDnLe, rmUpRi : SetCurrentCursor (mcResizeDnLe);
	rmUp, rmDown   : SetCurrentCursor (mcResizeVert);
	rmLeft, rmRight: SetCurrentCursor (mcResizeHori);
      End;

    If Mode and dmDragMove <> 0 then
    begin
      P.X := Origin.X - Event.Where.X;
      P.Y := Origin.Y - Event.Where.Y;
      repeat
	Inc(Event.Where.X, P.X);
	Inc(Event.Where.Y, P.Y);
	MoveGrow(Event.Where.X, Event.Where.Y, Size.X, Size.Y);
      until not MouseEvent(Event, evMouseMove);
    end else
    begin
      If ResMode in [rmUpLe, rmLeft, rmDnLe] then
	P.X := Origin.X - Event.Where.X
      Else P.X := Size.X - Event.Where.X;
      If ResMode in [rmUpRi, rmUp, rmUpLe] then
	P.Y := Origin.Y - Event.Where.Y
      Else P.Y := Size.Y - Event.Where.Y;
      repeat
	Inc(Event.Where.X, P.X);
	Inc(Event.Where.Y, P.Y);
	Case ResMode Of
	  rmUpLe : Begin
		     Correct(true, true);
		     MoveGrow(Event.Where.X, Event.Where.Y,
		       Size.X - Event.Where.X + Origin.X,
		       Size.Y - Event.Where.Y + Origin.Y);
		   End;
	  rmUp   : Begin
		     Correct(false, true);
		     MoveGrow(Origin.X, Event.Where.Y, Size.X,
		       Size.Y - Event.Where.Y + Origin.Y);
		   End;
	  rmUpRi : Begin
		     Correct(false, true);
		     MoveGrow(Origin.X, Event.Where.Y, Event.Where.X,
		       Size.Y - Event.Where.Y + Origin.Y);
		   End;
	  rmLeft : Begin
		     Correct(true, false);
		     MoveGrow(Event.Where.X, Origin.Y,
		       Size.X - Event.Where.X + Origin.X, Size.Y);
		   End;
	  rmDnLe : Begin
		     Correct(true, false);
		     MoveGrow(Event.Where.X, Origin.Y,
		       Size.X - Event.Where.X + Origin.X, Event.Where.Y);
		   End;
	  rmDnRi : MoveGrow(Origin.X, Origin.Y, Event.Where.X, Event.Where.Y);
	  rmRight: MoveGrow(Origin.X, Origin.Y, Event.Where.X, Size.Y);
	  rmDown : MoveGrow(Origin.X, Origin.Y, Size.X, Event.Where.Y);

	End;
      until not MouseEvent(Event, evMouseMove);
    end;
  end else
  begin
    SetCurrentCursor(mcMove);
    SetMouse;
    repeat
      P := Rect^.Origin;
      S := Rect^.Size;
      KeyEvent(Event);
      case Event.KeyCode and $FF00 of
	kbLeft: If GetShiftState and kbCtrlShift <> 0 then
		  Change(0, -WinDragAdd*8)
		Else Change(-WinDragAdd, 0);
	kbRight: If GetShiftState and kbCtrlShift <> 0 then
		   Change(0, WinDragAdd*8)
		 Else Change(WinDragAdd, 0);
	kbUp: Change(0, -WinDragAdd);
	kbDown: Change(0, WinDragAdd);
	kbCtrlLeft: Change(-WinDragAdd*8, 0);
	kbCtrlRight: Change(WinDragAdd*8, 0);
	{
	kbCtrlUp: Change(0, -WinDragAdd*8);
	kbCtrlDn: Change(0, WinDragAdd*8);
	}
	kbHome: Update(Limits.A.X, P.Y);
	kbEnd: Update(Limits.B.X - S.X, P.Y);
	kbPgUp: Update(P.X, Limits.A.Y);
	kbPgDn: Update(P.X, Limits.B.Y - S.Y);
      end;
      MoveGrow(P.X, P.Y, S.X, S.Y);
      SetMouse;
    until (Event.KeyCode = kbEnter) or (Event.KeyCode = kbEsc);
    if Event.KeyCode = kbEsc then Rect^.Locate(SaveBounds);
  end;
  SetCurrentCursor(mcHourGlass);
  TheDragView := nil;
  TheDraggedView := nil;
  SetState(sfDragging, False);
  DoneRect;
  Options := SaveOpt;
  SetCurrentCursor(mcNoCursor);
End;

procedure TGView.Draw;
var C: Byte;
Begin
  If not GetState(sfTransparent)
  then begin
    C := GetColor(1);
    SetFillStyle (SolidFill, C);
    Bar (0, 0, Size.X-1, Size.Y-1);
  end
End;

procedure TGView.DrawClipped(Clip: PVis; VisOwner: PGView);
  { has to destroy Clip }
var
  Bounds: TRect;
  P: TPoint;
begin
  If GetState(sfVisible + sfExposed) and
     GetOwnerClippedBounds(Bounds)
  then begin
    ClipVis(Clip, Bounds);
    SubtractInvisible(Clip, VisOwner, false);
    {DrawRectList(Clip);}
    P.X := 0; P.Y := 0; MakeSemiGlobal(P, P);
    DrawVis(Clip, P, @Self)
  end;
  FreeVis(Clip)
end;

procedure TGView.DrawCursor;
var R: TRect;
    SaveOpt: Word;
Begin
  SaveOpt := Options;
  Options := Options and not ofBuffer;
  SetViewPort;
  R.Assign(Cursor.X, Cursor.Y, Cursor.X+CursorSize.X, Cursor.Y+CursorSize.Y);
{$ifdef LINUX} {SVGALIB doesn't have XORPUT}
  if CursorFlag then SetFillStyle(SolidFill, Black)
  else SetFillStyle(SolidFill, White);
{$else}
  SetFillStyle (SolidFill, White);
  SetWriteMode (XORPut);
{$endif}
  With R do
    Bar (A.X, A.Y, B.X - 1, B.Y - 1);
  SetWriteMode (NormalPut);
  RestoreViewport;
  Options := SaveOpt;
End;

procedure TGView.DrawView;
var
  SaveOpt: Word;
Begin
  If GetState(sfTransparent)
  then begin
    SaveOpt := Options;
    Options := Options and not ofSelectable;
    Hide;
    Show;
    Options := SaveOpt
  end
  else
    DrawVisible
End;

procedure TGView.DrawVisible;
var
  aVis: PVis;
  P: TPoint;
Begin
  aVis := GetVisibility;
  If aVis = nil then Exit;
  P.X := 0; P.Y := 0; MakeSemiGlobal(P, P);
  DrawVis(aVis, P, @Self);
  FreeVis(aVis)
End;

procedure TGView.DrawVisibleLocal(Local: pointer);
var
  aVis: PVis;
  P: TPoint;
Begin
  aVis := GetVisibility;
  If aVis = nil then Exit;
  P.X := 0; P.Y := 0; MakeSemiGlobal(P, P);
  DrawVisLocal(aVis, P, @Self, Local, PrevBP);
  FreeVis(aVis)
End;

procedure TGView.EndModal(Command: Word);
var P: PView;
Begin
  P := TopView;
  If TopView <> nil then TopView^.EndModal(Command);
End;

procedure TGView.EndScroll;
var
  Opt: Word;
  P: TPoint;

 procedure DoCopy; {$ifndef FPK}far;{$endif}
 Begin
   CritUnionDelta(ScrollView_Delta);
   CopyScreen(DrawOrigin.X, DrawOrigin.Y,
	      Size.X + DrawOrigin.X, Size.Y + DrawOrigin.Y,
	      ScrollView_Delta.X + DrawOrigin.X, ScrollView_Delta.Y + DrawOrigin.Y)
 End;

begin
  P.X := 0; P.Y := 0;
  MakeSemiGlobal(P, P);
  if GetVgaMemCaps and vmcCopy <> 0
    then begin
      Opt := Options;
      Options := Opt and not (ofBuffer + ofMetafile); { CopyScreen is not bufferable }
      DrawVisLocal(ScrollView_ivis, P, @Self, @DoCopy, CurBP);	{ copy region }
      Options := Opt;
      FreeVis(ScrollView_ivis);
    end;
  DrawVis(ScrollView_avis, P, @Self);			{ draw region }
  FreeVis(ScrollView_avis)
end; { TGView.EndScroll }

function TGView.Exposed: Boolean;
var G: PGGroup;
Begin
  If GetState (sfExposed+sfVisible) then
    If GOwner<>nil then Begin
      G:=GOwner;
      While (G<>nil) and (G^.LockFlag=0) do
	G:=G^.GOwner;
      Exposed:=G=nil
    End
    Else Exposed:=True
  Else Exposed := false;
End;

function TGView.Focus: Boolean;
var Result_: Boolean;
Begin
  Result_ := True;
  If State and (sfSelected + sfModal) = 0 then Begin
    If GOwner <> nil then Begin
      Result_ := GOwner^.Focus;
      If Result_ then
	If ((GOwner^.Current = nil) or
	  (GOwner^.Current^.Options and ofValidate = 0) or
	  (GOwner^.Current^.Valid(cmReleasedFocus))) then Select
						     else Result_ := False;
    End;
  End;
  Focus := Result_;
End;

procedure TGView.FreeBack;
begin
  If Back <> nil
  then begin
    FreeScreenBuf(Back);
    Back := nil
  end
end;

{$ifdef NOIASM}
function TGView.GetColor(Color: Word): Word;
var
  Pal: PPalette;
  V: PGView;
begin
  V := @Self;
  repeat
    Pal := V^.GetPalette;
    if (Pal <> nil) and (Length(Pal^) >= Color)
    then Color := Ord(Pal^[Color]);
    V := V^.GOwner
  until V = nil;
  GetColor := Color
end;
{$else}
function TGView.GetColor(Color: Word): Word; Assembler;
Const
{$IFDEF VER60}
  GetPal = 4 + $28;
{$ELSE}
  GetPal = vmtHeaderSize + $2C;
{$ENDIF}
Asm
	LES	SI, Self
@@3:	PUSH	ES
	PUSH	SI
	MOV	DI, ES:[SI]
	CALL	DWORD PTR [DI].GetPal
	PUSH	ES
	MOV     ES, DX
	MOV	DI, AX
	OR	AX, DX
	JZ	@@1
	XOR	BX, BX
	MOV     BL, [ES:DI]
	CMP	BX, Color
	JB	@@1
	ADD	DI, Color
	MOV     BL, [ES:DI]
	MOV	Color, BX
@@1:    POP	ES
	LES	SI, ES:[SI].TGView.GOwner
	MOV	AX, ES
	OR	AX, SI
	JNZ	@@3
@@2:	MOV	AX, Color
End;
{$endif NOIASM}

function TGView.GetHelpCtx: Word;
begin
  if State and sfDragging <> 0
  then GetHelpCtx := hcDragging
  else GetHelpCtx := HelpCtx
end;

{$ifdef NOIASM}
function TGView.GetOwnerClippedBounds(var Bounds: TRect): Boolean;
var
  V: PGView;
begin
  GetExtent(Bounds);
  V := @Self;
  repeat
    {$ifdef Windows}
    if V^.GetState(sfRoot)
    then begin
      GetOwnerClippedBounds := true;
      Exit
    end;
    {$endif Windows}
    Bounds.Move(V^.Origin.x, V^.Origin.y);
    V := V^.GOwner;
    if V = nil
    then begin
      GetOwnerClippedBounds := true;
      Exit
    end;
    { V is a TGGroup }
    if PGGroup(V)^.LockFlag <> 0
    then begin
      GetOwnerClippedBounds := false;
      Exit
    end;
    if Bounds.A.x < 0 then Bounds.A.x := 0;
    if Bounds.A.y < 0 then Bounds.A.y := 0;
    if Bounds.B.x > V^.Size.x then Bounds.B.x := V^.Size.x;
    if Bounds.B.y > V^.Size.y then Bounds.B.y := V^.Size.y;
  until false;
end;
{$else}
function TGView.GetOwnerClippedBounds(var Bounds: TRect): Boolean; assembler;
asm
	push	ds
	lds	si, Bounds
	les	di, Self
	mov	[si].TRect.A.x, 0
	mov	[si].TRect.A.y, 0
	mov	ax, es:[di].Size.x
	mov	dx, es:[di].Size.y
	mov	[si].TRect.B.x, ax
	mov	[si].TRect.B.y, dx
	jmp	@@5
@@1:
	les	di, es:[di].TGView.GOwner
	mov	ax, es
	or	ax, di
	mov	ax, 1
	jz	@@0

	xor	ax, ax
	cmp	es:[di].TGGroup.LockFlag, 0
	jnz	@@0

	cmp	[si].TRect.A.X, 0
	jg	@@2
	mov	[si].TRect.A.X, 0
@@2:
	cmp	[si].TRect.A.Y, 0
	jg	@@3
	mov	[si].TRect.A.Y, 0
@@3:
	mov	ax, es:[di].TGView.Size.x
	cmp	[si].TRect.B.x, ax
	jl	@@4
	mov	[si].TRect.B.x, ax
@@4:
	mov	ax, es:[di].TGView.Size.y
	cmp	[si].TRect.B.y, ax
	jl	@@5
	mov	[si].TRect.B.y, ax
@@5:
{$ifdef Windows}
	test	es:[di].TGView.State, sfRoot
	mov	ax, 1
	jnz	@@0
{$endif Windows}
	mov	ax, es:[di].TGView.Origin.x
	add	[si].TRect.A.x, ax
	add	[si].TRect.B.x, ax
	mov	ax, es:[di].TGView.Origin.y
	add	[si].TRect.A.y, ax
	add	[si].TRect.B.y, ax
	jmp	@@1
@@0:
	pop	ds
end;
{$endif}

procedure TGView.GetPeerViewPtr(var S: TStream; var P);
var
  Index: Integer;
begin
  S.Read(Index, SizeOf(Word));
  if (Index = 0) or (OwnerGroup = nil) then Pointer(P) := nil
  else
  begin
    Pointer(P) := FixupList^[Index];
    FixupList^[Index] := @P;
  end;
end;

function TGView.GetStandardFont: Word;
Begin
  If GOwner <> nil then GetStandardFont:=GOwner^.StandardFont
		   else GetStandardFont:=ftSansSerif;
End;

function TGView.GetVisibility: PVis;
var
  aVis: PVis;
  Bounds: TRect;
Begin
  GetVisibility := nil;
  if (State and (sfVisible + sfExposed) <> (sfVisible + sfExposed)) or
     not GetOwnerClippedBounds(Bounds) then Exit;
  aVis := NewVis(Bounds);
  SubtractInvisible(aVis, nil, false);
  GetVisibility := aVis
End;

procedure TGView.GrowTo(X, Y: Integer);
var R: TRect;
Begin
  R.Assign(Origin.X, Origin.Y, Origin.X + X, Origin.Y + Y);
  Locate(R);
End;

procedure TGView.HandleEvent(var Event: TEvent);
var
  Ctx: Word;

{$ifdef Windows}
       function IsIcon: Boolean;
       var
         W: PGView;
       begin
         W := FindWin;
         if W <> nil
         then IsIcon := IsIconic(W^.Wnd)
         else IsIcon := true;
       end;

       procedure ProvideMinMaxInfo(var Info: TMINMAXINFO);
       var
         R: TRect;
         Min, Max: TPoint;
       begin
         SizeLimits(Min, Max);
	 R.A.X := 0; R.A.Y := 0;
         R.B := Min;
         MakeNonClient(R);
         Info.ptMinTrackSize.x := R.B.x - R.A.x;
         Info.ptMinTrackSize.y := R.B.y - R.A.y;
         R.A.X := 0; R.A.Y := 0;
	 R.B := Max;
         MakeNonClient(R);
         Info.ptMaxTrackSize.x := R.B.x - R.A.x;
         Info.ptMaxTrackSize.y := R.B.y - R.A.y
       end;
 {$endif Windows}

Begin
  {$ifdef Windows}
  If Event.What = evSystem then
  with PMSG(Event.InfoPtr)^ do
    case message of
      wm_GetMinMaxInfo:
        begin
	  {If not IsIcon then} ProvideMinMaxInfo(PMINMAXINFO(lParam)^);
	  ClearEvent(Event);
	end;
      wm_WindowPosChanged:
	begin
	  If not IsIcon then UseWinPos(PWINDOWPOS(lParam)^);
	  ClearEvent(Event)
	end;
      wm_Close:
        ClearEvent(Event);
    end;
  {$endif Windows}
  If Event.What = evMouseDown
  then begin
    If (State and (sfSelected + sfDisabled) = 0) and
       (Options and ofSelectable <> 0) then
      If not Focus or (Options and ofFirstClick = 0) then
	ClearEvent(Event);
  end else
  if Event.What = evTimer
  then begin
    If (State and (sfCursorVis + sfExposed + sfSelected) = sfCursorVis + sfExposed + sfSelected) and
       (Event.InfoWord mod 8 = 0)
    then begin
      DrawCursor;
      CursorFlag:=not CursorFlag;
    End;
  end else
  if Event.What = evPositionalCtx
  then begin
    Ctx := GetHelpCtx;
    If Ctx <> 0
    then begin
      Event.What := evNothing;
      Event.InfoLong := Ctx
    end
  end
End;

function TGView.HasInvisiblePart: Boolean;
var
  Bounds: TRect;
  Orig: TPoint;
  Res: Boolean;

 procedure SubtractPeers(Orig: TPoint; G: PGGroup; Cur: PGView);
 var
   P: PGView;
   R: TRect;
 Begin
   If G = nil then Exit;
   P := G^.First;
   while P <> Cur do Begin
     If P^.State and (sfVisible + sfRoot) = sfVisible then Begin
       P^.GetBounds(R);
       R.Move(Orig.x, Orig.y);
       R.Intersect(Bounds);
       if not R.Empty
       then begin
	 Res := true;
	 Exit
       end
     End;
     P := P^.GNext
   End
 End;

 procedure Subtract(Orig: TPoint; P: PGView);
 Begin
   while (P <> nil) and not Res
   {$ifdef Windows} and (P^.State and sfRoot = 0) {$endif Windows} do
   Begin
     Dec(Orig.x, P^.Origin.x);
     Dec(Orig.y, P^.Origin.y);
     SubtractPeers(Orig, P^.GOwner, P);
     P := P^.GOwner;
   End
 End;

Begin
  Orig.X := 0; Orig.Y := 0;
  Res := false;
  GetOwnerClippedBounds(Bounds);
  MakeSemiGlobal(Orig, Orig);
  Subtract(Orig, @Self);
  HasInvisiblePart := Res
end;

procedure TGView.HideCursor;
Begin
  Inc(CursorLock);
  If Cursorlock = 1 then SetState(sfCursorVis, false);
End;

procedure TGView.HideDrawClipped(Clip: PVis; VisOwner: PGView);
var
  SaveState: Word;
  SaveOpt: Word;
  P: TPoint;
  R: TRect;

	procedure Paste; {$ifndef FPK}far;{$endif}
	begin
	  DrawScreenBuf(Back)
	end;

begin
  if GetState(sfRoot)
  then FreeVis(Clip)
    { If we are root, the only way to remove our image is to take
      the window away; so don't do anything. }
  else begin
    GetOwnerClippedBounds(R);
    ClipVis(Clip, R);
    If Back = nil
    then begin
      SaveState := State;
      State := State and not sfVisible;
      If GOwner <> nil
      then begin
        GOwner^.DrawClipped(Clip, VisOwner)
      end
      else FreeVis(Clip);
      State := SaveState;
    end
    else begin
      SubtractInvisible(Clip, VisOwner, false);
      P.X := 0; P.Y := 0;
      MakeSemiGlobal(P, P);
      SaveOpt := Options;
      Options := Options and not (ofBuffer + ofMetafile);
      DrawVisLocal(Clip, P, @Self, @Paste, CurBP);
      Options := SaveOpt;
      FreeVis(Clip)
    end
  end
end;

procedure TGView.Locate(var Bounds: TRect);
var
  R: TRect;
  P, Min, Max: TPoint;
  VisBefore: PVis;

 function Range(Val, Min, Max: Integer): Integer;
 Begin
   If Val < Min then Range := Min else
     If Val > Max then Range := Max else
       Range := Val;
 End;

Begin
  SizeLimits(Min, Max);
  Bounds.B.X := Bounds.A.X + Range(Bounds.B.X - Bounds.A.X, Min.X, Max.X);
  Bounds.B.Y := Bounds.A.Y + Range(Bounds.B.Y - Bounds.A.Y, Min.Y, Max.Y);
  GetBounds(R);
  If not Bounds.Equals(R) then Begin
    {$ifdef Windows}
    If GetState(sfRoot)
    then ChangeBounds(Bounds) else
    {$endif Windows}
    If (Options and GlobalOptions and ofBuffer = 0) and GetState(sfTransparent)
    then begin
      RestoreBackground;
      SetBounds(Bounds);
      If Options and GlobalOptions and ofStoreBack <> 0
	then StoreBack;
      SetBounds(R);
      ChangeBounds(Bounds);
    end
    else begin
      VisBefore := GetVisibility;
      ChangeBounds(Bounds);
      GetExtent(Bounds);
      P.X := 0; P.Y := 0; MakeSemiGlobal(P, P);
      Bounds.Move(P.X, P.Y);
      SubtractRegion(VisBefore, Bounds);

      GetBounds(Bounds);
      SetBounds(R);
      HideDrawClipped(VisBefore, @Self);
      SetBounds(Bounds);
      FreeBack
    end
  End;
End;

procedure TGView.MakeFirst;
Begin
  PutInFrontOf(GOwner^.First);
End;

{$ifdef NOIASM}
procedure TGView.MakeSemiGlobal(Source: TPoint; var Dest: TPoint);
var
  P: TPoint;
  V: PView;
begin
  P := Source;
  V := @Self;
  while (V^.Owner <> nil)
    {$ifdef Windows} and not V^.GetState(sfRoot) {$endif} do
  begin
    P.x := P.x + V^.Origin.x;
    P.y := P.y + V^.Origin.y;
    V := V^.Owner
  end;
  Dest := P
end;
{$else}
procedure TGView.MakeSemiGlobal(Source: TPoint; var Dest: TPoint); assembler;
asm
	LES     DI,Self
	XOR     AX,AX
	MOV     DX,AX
@@1:
{$ifdef Windows}
	test	es:[di].TView.State, sfRoot
	jnz	@@0
{$endif Windows}
	ADD     AX,ES:[DI].TView.Origin.X
	ADD     DX,ES:[DI].TView.Origin.Y
	LES     DI,ES:[DI].TView.Owner
	MOV     SI,ES
	OR      SI,DI
	JNE     @@1
@@0:
	ADD     AX,Source.X
	ADD     DX,Source.Y
	LES     DI,Dest
	CLD
	STOSW
	XCHG    AX,DX
	STOSW
end;
{$endif NOIASM}

function TGView.MouseEvent(var Event: TEvent; Mask: Word): Boolean;
Begin
  {$ifdef Windows}
  SetCapture(Main^.Wnd);
  {$endif Windows}
  Repeat
    GetEvent (Event);
  Until Event.What and (Mask or evMouseUp) <> 0;
  MouseEvent := Event.What <> evMouseUp;
  {$ifdef Windows}
  ReleaseCapture;
  {$endif Windows}
End;

procedure TGView.MoveTo(X, Y: Integer);
var R: TRect;
Begin
  R.Assign(X, Y, X + Size.X, Y + Size.Y);
  Locate(R);
End;

function TGView.NextView: PGView;
Begin
  If GOwner<>nil then If @Self=GOwner^.Last then NextView:=nil
					    else NextView:=GNext
		 else NextView:=nil;
End;

{$ifdef NOIASM}
function TGView.Prev: PGView;
var
  V: PGView;
begin
  V := @Self;
  while (V^.GNext <> @Self) do
    V := V^.GNext;
  Prev := V
end;
{$else}
function TGView.Prev: PGView; assembler;
Asm
	LES     DI,Self
	MOV     CX,DI
	MOV     BX,ES
@@1:    MOV     AX,DI
	MOV     DX,ES
	LES     DI,ES:[DI].TGView.GNext
	CMP     DI,CX
	JNE     @@1
	MOV     SI,ES
	CMP     SI,BX
	JNE     @@1
End;
{$endif NOIASM}

function TGView.PrevView: PGView;
Begin
  If GOwner<>nil
  then
    If @Self=GOwner^.First
    then PrevView:=nil
    else PrevView:=Prev
  else PrevView:=nil;
End;

procedure TGView.PutInFrontOf(Target: PGView);
var
  VisBefore, VisAfter: PVis;
  OldNext: PGView;

 procedure MoveView;
 Begin
   GOwner^.RemoveView(@Self);
   GOwner^.InsertView(@Self, Target);
 End;

 procedure SubtractTrans(Vis: PVis; TopView, BottomView: PGView);
 var
   Orig: TPoint;
   P: PGView;
   R: TRect;
 begin
   P := TopView;
   Orig.X := 0; Orig.Y := 0;
   GOwner^.MakeSemiGlobal(Orig, Orig);
   while (P <> nil) and (P <> BottomView) do
   begin
     If P^.State and (sfVisible + sfTransparent) = (sfVisible + sfTransparent)
     then begin
       P^.GetBounds(R);
       R.Move(Orig.X, Orig.Y);
       SubtractRegion(VisBefore, R);
     end;
     P := P^.NextView
   end
 end;

 function MovedUp: Boolean;
 var
   P: PGView;
 begin
   P := OldNext;
   while (P <> nil) and (P <> @Self) do
     P := P^.NextView;
   MovedUp := P = nil
 end;

Begin
  If Target = @Self then
    GOwner^.SetCurrent(@Self, NormalSelect)
  else
  if (GOwner <> nil) and (Target <> NextView) and
    ((Target = nil) or (Target^.GOwner = GOwner))
  then
    if State and (sfVisible + sfExposed) <> (sfVisible + sfExposed)
    then MoveView
    {$ifdef Windows}
    else if Wnd <> 0
    then begin
      MoveView;
      UpdateWinOrder;
      if Options and ofSelectable <> 0
      then GOwner^.SetCurrent(@Self, NormalSelect);
    end
    {$endif Windows}
    else begin
      VisBefore := GetVisibility;
      OldNext := GNext;
      State := State and not sfVisible;
      MoveView;
      State := State or sfVisible;
      VisAfter := GetVisibility;

      If MovedUp
      then begin  { Moved up }
        SubtractTrans(VisBefore, NextView, OldNext);
        SubtractVis(VisAfter, VisBefore);
        FreeVis(VisBefore);
        ShowDrawClipped(VisAfter, @Self);
      end
      else begin  { Moved down }
        SubtractTrans(VisAfter, OldNext, @Self);
        SubtractVis(VisBefore, VisAfter);
        FreeVis(VisAfter);
        GOwner^.DrawClipped(VisBefore, GOwner)
      end;

{      VisCopy := CopyVis(VisAfter);
      SubtractVis(VisAfter, VisBefore);
      SubtractVis(VisBefore, VisCopy);
      FreeVis(VisCopy);
      DrawClipped(VisAfter, @Self);
      HideDrawClipped(VisBefore, GOwner); }

      if Options and ofSelectable <> 0 then
      begin
	{GOwner^.ResetCurrent;}
	GOwner^.SetCurrent(@Self, NormalSelect) { ****** sth to be dun }
      end;
    end;
end;

procedure TGView.PutPeerViewPtr(var S: TStream; P: PGView);
var
  Index: Integer;
begin
  if (P = nil) or (OwnerGroup = nil) then Index := 0
  else Index := OwnerGroup^.IndexOf(P);
  S.Write(Index, SizeOf(Word));
end;

procedure TGView.RestoreBackground;
var
  AVis: PVis;
Begin
  AVis := GetVisibility;
  If AVis <> nil then HideDrawClipped(AVis, @Self);
  FreeBack
End;

procedure TGView.RestoreViewPort;
var R: TRect;
Begin
  If ViewLock < 1 then Exit;
  If ViewLock = 1 then Begin
    If not EndDraw(vis, @Self)			{ will zero ViewLock }
    then {error};
    FreeVis(vis);
    vis := nil;
  End
  else Begin
    Dec(ViewLock);
    GetExtent(R);
    SetSubRect(R)
  end
End;

Procedure TGView.Select;
begin
  if Options and ofSelectable <> 0 then
    if Options and ofTopSelect <> 0 then MakeFirst else
      if GOwner <> nil then GOwner^.SetCurrent(@Self, NormalSelect);
end;

procedure TGView.SetCursor(X, Y: Integer);
Begin
  HideCursor;
  Cursor.X:=X;
  Cursor.Y:=Y;
  ShowCursor;
End;

Procedure TGView.SetState(AState: Word; Enable: Boolean);
var
  Command: Word;
  ToShow, ToHide: Boolean;
Begin
  If AState and sfCursorVis <> 0 then
    If Enable then Begin
      If (State and sfCursorVis=0) and
	 GetState(sfExposed + sfVisible + sfSelected) then Begin
	If CursorFlag then DrawCursor;
      End
    End
    Else Begin
      If (State and sfCursorVis <>0) and
	 GetState(sfExposed + sfVisible + sfSelected) then Begin
	If CursorFlag then DrawCursor;
      End;
    End;

  If AState = sfVisible then Begin
    ToShow := Enable and (State and sfVisible = 0);
    ToHide := not Enable and (State and sfVisible <> 0);
    If ToHide then RestoreBackground;
  End;

{$ifdef Windows}
  If (AState = sfRoot) or (AState = sfExposed) then
    If Enable and ((State or AState) and (sfRoot + sfExposed) = (sfRoot + sfExposed))
    then begin
      State := State or AState;
      CreateWin
    end else
    if not Enable and ((State and not AState) and (sfRoot + sfExposed) <> (sfRoot + sfExposed))
    then DestroyWin;
{$endif Windows}

  If Enable
    Then State:=State or AState
    Else State:=State and not AState;

  if GOwner <> nil then
    case AState of
      sfVisible:
	Begin
	  If ToShow
	  then begin
	    If Options and GlobalOptions and ofStoreBack <> 0
	    then StoreBack;
	    If Options and ofSelectable <> 0
	    then
	      {GOwner^.ResetCurrent;}
	      GOwner^.SetCurrent(@Self, NormalSelect);
	    if GOwner^.State and sfExposed <> 0 then
	      SetState(sfExposed, Enable);
	    {$ifdef Windows} if not GetState(sfRoot) then {$endif Windows} DrawVisible
	  end
	  else begin
	    if GOwner^.State and sfExposed <> 0 then
	      SetState(sfExposed, Enable);
	    if Options and ofSelectable <> 0
	    then GOwner^.ResetCurrent
	  end
	End;
      sfFocused:
	Begin
	  If Enable then ShowCursor
		    else HideCursor;
	  if Enable then
	    Command := cmReceivedFocus else
	    Command := cmReleasedFocus;
	  Message(GOwner, evBroadcast, Command, @Self);
	End;
      sfExposed:
	CalcFirstPass;
      sfTransparent:
	DrawView;
    End;
End;

function TGView.SetSubRect(var SubRect: TRect): Boolean;
var
  R: TRect;
Begin
  with SubRect do Begin
    R.Copy(SubRect);
    R.Move(DrawOrigin.x, DrawOrigin.y);
    R.Intersect(MetaClipRect);
    SetSubRect := not R.Empty;
    SetClipRectR(R)
  End
End;

{$ifdef Windows}
procedure TGView.SetTitle(ATitle: string);
begin
  If Wnd <> 0
  then begin
    ATitle[Length(ATitle)+1] := #0;
    if not AnsiCode
    then OemToAnsi(@ATitle[1], @ATitle[1]);
    SetWindowText(Wnd, @ATitle[1]);
  end
end;
{$else !Windows}
procedure TGView.SetTitle(ATitle: string);
begin
end;
{$endif Windows}

procedure TGView.SetViewPort;
var
  P: TPoint;
  R: TRect;
Begin
  If ViewLock = 0 then Begin
    vis := GetVisibility;
    P.X := 0; P.Y := 0; MakeSemiGlobal(P, P);
    If not BeginDraw(vis, P, @Self, true)		{ will inc ViewLock }
    then begin
      {error}
      FreeVis(vis);
      vis := nil
    end
  End
  Else Begin
    Inc(ViewLock);
    GetExtent(R);
    SetSubRect(R);
  end
End;

procedure TGView.ShowCursor;
Begin
  If CursorLock > 0 then Dec(CursorLock);
  If CursorLock = 0 then SetState(sfCursorVis, true);
End;

procedure TGView.ShowDrawClipped(Clip: PVis; VisOwner: PGView);
begin
  If not GetState(sfTransparent)
  then DrawClipped(Clip, VisOwner)
  else If GOwner <> nil then
    GOwner^.DrawClipped(Clip, GOwner);
end;

procedure TGView.Store(var S: TStream);
Begin
  TView.Store (S);
  S.Write (CursorSize, SizeOf (CursorSize));
  S.Write (CursorLock, SizeOf (CursorLock));
End;

procedure TGView.StoreBack;
var
  R: TRect;
Begin
  FreeBack;
  if HasInvisiblePart then Exit;
  R.A.X := 0; R.A.Y := 0; MakeSemiGlobal(R.A, R.A); MakeSemiGlobal(Size, R.B);
{$ifndef Windows}
  SetCriticalAreaR(R);
{$endif}
  HideMouse;
  With R do Back := StoreScreen(A.X, A.Y, B.X, B.Y);
  ShowMouse;
End;

procedure TGView.SubtractInvisible(var AVis: PVis; VisOwner: PGView;
  SubTrans: Boolean);
var
  Orig: TPoint;

{$ifdef FPK}
 procedure Subtract(Orig: TPoint; Q: PGView);
 var
   R: TRect;
   P: PGView;
   G: PGGroup;
   Cur: PGView;
 Begin
   while (Q <> nil) and (Q <> VisOwner)
   {$ifdef Windows} and (Q^.State and sfRoot = 0) {$endif Windows} do
   Begin
     Dec(Orig.x, Q^.Origin.x);
     Dec(Orig.y, Q^.Origin.y);
     G := Q^.GOwner;
     Cur := Q;

   If G <> nil
     then begin
       P := G^.First;
       while P <> Cur do Begin
	 If P^.State and (sfVisible + sfRoot) = sfVisible then Begin
	   P^.GetBounds(R);
	   R.Move(Orig.x, Orig.y);
	   If P^.Options and ofStoreBack <> 0
	     then begin
	       IndirectRegion(aVis, R, P);
	       If not ((P^.State and sfTransparent <> 0) and not SubTrans)
	       then SubtractRegion(aVis, R)
	     end else
	       If (P^.State and sfTransparent <> 0) and not SubTrans
		 then IndirectRegion(aVis, R, P)
	       else SubtractRegion(aVis, R)
	 End;
	 P := P^.GNext
       End
     end;


     Q := Q^.GOwner;
     IndirectRegion(aVis, R, nil)
   End
 End;


{$else}
procedure SubtractPeers(Orig: TPoint; G: PGGroup; Cur: PGView);
 var
   P: PGView;
   R: TRect;
 Begin
   If G = nil then Exit;
   P := G^.First;
   while P <> Cur do Begin
     If P^.State and (sfVisible + sfRoot) = sfVisible then Begin
       P^.GetBounds(R);
       R.Move(Orig.x, Orig.y);
       If P^.Options and ofStoreBack <> 0
       then begin
	 IndirectRegion(aVis, R, P);
	 If not ((P^.State and sfTransparent <> 0) and not SubTrans)
	 then SubtractRegion(aVis, R)
       end else
       If (P^.State and sfTransparent <> 0) and not SubTrans
       then IndirectRegion(aVis, R, P)
       else SubtractRegion(aVis, R)
     End;
     P := P^.GNext
   End
 End;

 procedure Subtract(Orig: TPoint; P: PGView);
 var
   R: TRect;
 Begin
   while (P <> nil) and (P <> VisOwner)
   {$ifdef Windows} and (P^.State and sfRoot = 0) {$endif Windows} do
   Begin
     Dec(Orig.x, P^.Origin.x);
     Dec(Orig.y, P^.Origin.y);
     SubtractPeers(Orig, P^.GOwner, P);
     P := P^.GOwner;
     IndirectRegion(aVis, R, nil)
   End
 End;

{$endif}

Begin
  If (VisOwner <> nil) and (VisOwner^.GOwner = @Self)
  then begin
    { down-clip, one step supported }
    Unindirect(aVis, @Self)
  end
  else begin
    { up-clip }
    Orig.X := 0; Orig.Y := 0;
    MakeSemiGlobal(Orig, Orig);
    Subtract(Orig, @Self);
  end
End;

function TGView.TopView: PGView;
var P: PGView;
Begin
  If TheTopView = nil then Begin
    P := @Self;
    While (P <> nil) and (P^.State and sfModal = 0) do P := P^.GOwner;
    TopView := P;
  End
  Else TopView := TheTopView;
End;

{**************************** TFrame object *********************************}

Constructor TFrame.Init(var Bounds: TRect);
Begin
  TGView.Init (Bounds);
  EventMask:=evMouse+evKeyBoard+evCommand;
  Options:=Options or (ofPostProcess + ofFirstPass);
  GrowMode:=gfGrowHiX+gfGrowHiY;
  TitleSize:= 19;
  OldC:=mcStd;
End;

constructor TFrame.Load(var S: TStream);
Begin
  TGView.Load (S);
  S.Read (TitleSize, SizeOf (TitleSize));
  OldC:=mcStd;
End;

{$ifdef CALC_BOOL_PARAM_PROBLEM}
procedure TFrame.CalcFirstPass;
var
  Enable: Boolean;
begin
  Enable := (State and sfTransparent = 0) and
    (Options and GlobalOptions and ofFirstPass <> 0);
  SetState(sfFirstPass, Enable)
end;
{$else}
procedure TFrame.CalcFirstPass;
begin
  SetState(sfFirstPass,
    (State and sfTransparent = 0) and
    (Options and GlobalOptions and ofFirstPass <> 0))
end;
{$endif}

procedure TFrame.ChMCursor;
var P: TPoint;
Begin
  If (GOwner=nil) {or (GOwner^.State and sfFocused = 0)} then Exit;
  {$ifdef Windows}
  if PWindow(GOwner)^.Flags and wfRootFrame <> 0 then Exit;
  {$endif Windows}
  If (PWindow (GOwner)^.Flags and wfGrow <>0) and Exposed then Begin
    MakeLocal(MouseWhere, P);
    { check the hori borders }
    If Ranges(P.Y, 0, 3) then
      If Ranges(P.X, 0, 22) then NewNum := mcResizeDnRi
      Else If Ranges(P.X, Size.X - 23, Size.X - 1) then NewNum := mcResizeDnLe
	   Else NewNum := mcResizeVert
    Else If Ranges(P.Y, Size.Y - 4, Size.Y - 1) then
           If Ranges(P.X, 0, 22) then NewNum := mcResizeDnLe
           Else If Ranges(P.X, Size.X - 23, Size.X - 1) then NewNum := mcResizeDnRi
		Else NewNum := mcResizeVert;

    { check the vert. borders }
    If NewNum = MCurStandard then
      If Ranges(P.X, 0, 3) then
        If Ranges(P.Y, 0, 22) then NewNum := mcResizeDnRi
        Else If Ranges(P.Y, Size.Y - 23, Size.Y - 1) then NewNum := mcResizeDnLe
             Else NewNum := mcResizeHori
      Else If Ranges(P.X, Size.X - 4, Size.X - 1) then
             If Ranges(P.Y, 0, 22) then NewNum := mcResizeDnLe
             Else If Ranges(P.Y, Size.Y - 23, Size.Y - 1) then NewNum := mcResizeDnRi
                  Else NewNum := mcResizeHori;

  End;
End;

Procedure TFrame.Draw;
Var
  R, Client: TRect;
  S: String;
  C: Byte;
  Flags: Word;

 procedure PaintFrame(Modal: Boolean);
 var I: Integer;
 Begin
   If Flags and wf3DFrame <> 0
   then begin
     SetColor(LightGray);
     Rectangle(1, 1, Size.X - 2, Size.Y - 2);
     Rectangle(2, 2, Size.X - 3, Size.Y - 3);
     Rectangle(3, 3, Size.X - 4, Size.Y - 4);
     EdgeR(0, 0, Size.X - 1, Size.Y - 1, false, LightGray);
     EdgeR(4, 4, Size.X - 5, Size.Y - 5, true, LightGray);
   end
   else begin
     SetColor (Black);
     RectAngle (0, 0, Size.X-1, Size.Y-1);
     If Modal then Begin
       If State and sfActive <> 0 then SetColor(GetColor(4))
				  else SetColor(GetColor(3));
       For I:=1 to 4 do RectAngle (I, I, Size.X-I-1, Size.Y-I-1);
       SetColor(White);
       Rectangle(5, 4, Size.X - 6, Size.Y - 6);
     End else Begin
       If State and sfActive <> 0 then SetColor(GetColor(2))
                                  else SetColor(GetColor(3));
       For I:=1 to 2 do RectAngle (I, I, Size.X-I-1, Size.Y-I-1);
       SetColor(Black);
       Rectangle(3, 3, Size.X - 4, Size.Y - 4);
       If Flags and wfGrow <> 0 then Begin
         SetColor (GetColor (5));
         HoriLine (Size.X-3,Size.Y-23,Size.X-1);
         VertLine (Size.X-24,Size.Y-3,Size.Y-1);
         HoriLine (0, 22, 2);
         VertLine (22, 0, 2);
         HoriLine (0, Size.Y-23, 2);
         VertLine (22, Size.Y-3, Size.Y-1);
         HoriLine (Size.X-3, 22, Size.X-1);
         VertLine (Size.X-23, 0, 2);
       End;
     End
   end
 End;

Begin
  GetExtent (R);
  Flags:=PWindow (GOwner)^.Flags;
  {$ifdef Windows}
  If Flags and wfRootFrame <> 0
  then begin
    { Background }
    If Flags and wfBackground <> 0 then Begin
      SetFillStyle(SolidFill, GetColor(1));
      With R do Bar(A.X, A.Y, B.X - 1, B.Y - 1);
    End;
    Exit;
  end;
  {$endif Windows}

  { Frame }
{$ifdef CALC_BOOL_PARAM_PROBLEM}
  if Flags and wfModal <> 0
  then PaintFrame(true)
  else PaintFrame(false);
{$else}
  PaintFrame(Flags and wfModal <> 0);
{$endif}
  PWindow(GOwner)^.GetClientRect(Client);

  { Title bar }
  {* Background colors *}
  If Flags and wfShowTitle <> 0 then Begin
    GetTitleRect(R);
    If GetState (sfActive) then C:=7 else C:=6;
    SetFillStyle (SolidFill, GetColor (C));
    Bar (R.A.X, R.A.Y, R.B.X - 1, R.B.Y - 2);
    {* Line under the title bar *}
    SetColor (Black);
    HoriLine (R.A.X, R.B.Y - 1, R.B.X - 1);
    {* Text *}
    If Flags and wfClose <>0 then Inc (R.A.X, IconSize);
    If Flags and wfZoom <>0 then Dec (R.B.X, IconSize);
    If GetState (sfActive) then C:=9 else C:=8;
    SetSubRect(R);
    SetTextJustify(CenterText, CenterText);
    SetTextParams(ftSystem, 0, GetColor(C), false);
    OutTextXY((R.A.X + R.B.X) div 2, (R.A.Y + R.B.Y) div 2, PWindow(GOwner)^.GetTitle($FF));

    GetExtent(R);
    SetSubRect(R);

    {* Close icon *}
    If Flags and wfClose <>0 then Begin
      GetTitleRect(R);
      { Vertical line just right of the close icon }
      SetColor(Black);
      VertLine (R.A.X + IconSize - 2, R.A.Y, R.B.Y - 1);
      { Close icon background }
      R.B.X := R.A.X + IconSize - 2;
      Dec(R.B.Y);
      SetFillStyle (SolidFill, GetColor (10));
      Bar (R.A.X, R.A.Y, R.B.X - 1, R.B.Y - 1);
      Inc (R.A.X, 2); Dec (R.B.X, 4);
      R.A.Y:=R.A.Y+(R.B.Y-R.A.Y) div 2-1;
      R.B.Y:=R.A.Y+2;
      SetColor (GetColor (11)); RectAngle (R.A.X, R.A.Y, R.B.X, R.B.Y);
      SetColor (GetColor (13));
      HoriLine (R.A.X+1, R.B.Y+1, R.B.X + 1);
      VertLine (R.B.X+1, R.A.Y+1, R.A.Y + 2);
      SetColor (GetColor (12));
      HoriLine (R.A.X+1, R.A.Y+1, R.B.X-1);
    End;

    {* Window number *}
    If (Flags and wfShowNumber <> 0) and (PWindow(GOwner)^.Number <> wnNoNumber) and
       (PWindow(GOwner)^.Number < 10) then Begin
      GetTitleRect(R); Dec(R.A.Y);
      R.A.X := R.B.X - IconSize + 1; R.B.Y := R.A.Y + TitleSize;
      If Flags and wfZoom <> 0 then R.Move(-IconSize + 1, 0);
      With R do Begin
        SetFillStyle(SolidFill, LightGray);
	Bar(A.X, A.Y, B.X, B.Y);
	SetColor (DarkGray);
        VertLine (A.X + 3, A.Y + 3, B.Y - 2);
        HoriLine (A.X + 3, A.Y + 3, B.X - 3);
	SetColor (White);
        HoriLine (A.X + 1, A.Y + 1, B.X - 1);
        VertLine (B.X - 3, A.Y + 3, B.Y - 2);
        HoriLine (A.X + 3, B.Y - 2, B.X - 3);
	SetColor (Black);
        RectAngle (A.X, A.Y, B.X, B.Y);
        Str(PWindow(GOwner)^.Number, S);
        SetTextJustify(CenterText, CenterText);
        SetTextParams(ftSansSerif, 0, GetColor(8), false);
        OutTextXY((A.X + B.X) div 2, (A.Y + B.Y) div 2 + 2, S);
      End;
    End;
    DrawZoomField (False);
  End;
  { Background }
  If Flags and wfBackground <> 0 then Begin
    R.Copy(Client);
    SetFillStyle(SolidFill, GetColor(1));
    With R do Bar(A.X, A.Y, B.X - 1, B.Y - 1);
    if Flags and wf3DFrame <> 0
    then begin
      EdgeR(R.A.x, R.A.y, R.B.x - 1, R.B.y - 1, false, GetColor(1));
    end
  End;
End;

procedure TFrame.DrawZoomField(Down: Boolean);
var R: TRect;
Begin
  If (PWindow (GOwner)^.Flags and wfZoom <>0)
  then Begin
    SetViewPort;
    { Calculate the bounds. }
    GetTitleRect(R);
    Inc(R.B.X); {black frame is just outside the title rect }
    Dec(R.A.Y); {black frame is just outside the title rect }
    R.A.X := R.B.X - IconSize;
    DrawButton (R, Down, True, White, Black, LightGray, DarkGray);
    If not Zoomed then DrawColIcon(R.A.X, R.A.Y, 4, 0)
		  else DrawColIcon(R.A.X, R.A.Y, 5, 0);
    RestoreViewPort;
  End;
End;

procedure TFrame.GetClientRect(var R: TRect);
begin
  {$ifdef Windows}
  if PWindow(GOwner)^.Flags and wfRootFrame <> 0
  then begin
    GetExtent(R);
    Exit
  end;
  {$endif Windows}
  If PWindow(GOwner)^.Flags and wf3DFrame <> 0
  then R.Assign(5, 5, Size.X - 5, Size.Y - 5) else
  If PWindow(GOwner)^.Flags and wfModal = 0
  then R.Assign(4, 4, Size.X - 4, Size.Y - 4)
  else R.Assign(6, 5, Size.X - 6, Size.Y - 6);
  If PWindow(GOwner)^.Flags and wfShowTitle <> 0
  then Inc(R.A.Y, TitleSize);
end;

Function TFrame.GetPalette: PPalette;
Const
  P:String [Length (CFrame)]=CFrame;
Begin
  GetPalette:=@P;
End;

procedure TFrame.GetTitleRect(var R: TRect);
begin
  if PWindow(GOwner)^.Flags
     and ({$ifdef Windows} wfRootFrame + {$endif Windows} wfShowTitle) <> wfShowTitle
  then begin
    R.Assign(0, 0, 0, 0);
    Exit
  end;
  If PWindow(GOwner)^.Flags and wf3DFrame <> 0
  then R.Assign(5, 5, Size.X - 5, 5 + TitleSize) else
  If PWindow(GOwner)^.Flags and wfModal = 0
  then R.Assign(4, 4, Size.X - 4, 4 + TitleSize)
  else R.Assign(6, 5, Size.X - 6, 5 + TitleSize);
end;

procedure TFrame.HandleEvent(var Event: TEvent);
var P, Min, Max: TPoint;
    R: TRect;
    ResMode: Byte;
    Flags: Word;
    Zoomfield: Boolean;
Begin
  TGView.HandleEvent (Event);
  Flags:=PWindow (GOwner)^.Flags;
  {$ifdef Windows}
  if Flags and wfRootFrame <> 0 then Exit;
  {$endif Windows}
  MakeLocal (Event.Where, P);
  If (Event.What=evMouseDown) and MouseInView (Event.Where) then Begin
    If Flags and wfShowTitle <> 0 then Begin
      GetTitleRect(R);
      R.B.X := R.A.X + IconSize;
      If R.Contains (P) and (Flags and wfClose <> 0) then Begin
	 Repeat Until not MouseEvent (Event, evNothing);
	 MakeLocal (Event.Where, P);
	 If R.Contains(P) then Begin
	   Event.What:=evCommand;
	   Event.Command:=cmClose;
	   Event.InfoPtr:=nil;
	   PutEvent (Event);
         End;
         ClearEvent (Event);
         Exit
      End;

      GetTitleRect(R);
      R.A.X := R.B.X - IconSize;
      If R.Contains (P) and (Flags and wfZoom <> 0) then Begin
        Zoomfield:=False;
	Repeat
          MakeLocal (Event.Where, P);
	  If (R.Contains (P) and not Zoomfield) or
             (not R.Contains (P) and Zoomfield) then Begin
            Zoomfield:=not Zoomfield;
            DrawZoomfield (Zoomfield);
	  End;
	Until not MouseEvent (Event, evMouseAuto);
        DrawZoomField (False);
        If R.Contains (P) then Begin
          Event.What:=evCommand;
          Event.Command:=cmZoom;
          Event.InfoPtr:=nil;
          PutEvent (Event);
	  ClearEvent (Event);
        End;
        Exit
      End;
      If Flags and wfMove <>0 then Begin
        GetTitleRect(R);
        If Flags and wfClose <> 0 then Inc(R.A.X, IconSize);
        If Flags and wfZoom <> 0 then Dec(R.B.X, IconSize);
        If R.Contains (P) then Begin
	  If not Event.Double then
	  With PWindow (GOwner)^ do Begin
	    GOwner^.GetExtent (R);
	    SizeLimits (Min, Max);
	    DragView (Event,DragMode or dmDragMove, R, Min, Max, 0);
	    ClearEvent (Event);
	  End
	  Else If Flags and wfZoom <> 0 then PWindow (GOwner)^.Zoom;
	  Exit
	End
      End
    End;
    If Flags and wfGrow<>0 then Begin
      ResMode:=0;
      MakeLocal (Event.Where, P);
      GetExtent(R);
      If R.Contains(P) then Begin
	If Ranges(P.Y, 0, 3) then
	  If Ranges(P.X, 0, 22) then ResMode := rmUpLe
	  Else If Ranges(P.X, Size.X - 23, Size.X - 1) then ResMode := rmUpRi
	       Else ResMode := rmUp
	Else If Ranges(P.Y, Size.Y - 4, Size.Y - 1) then
	       If Ranges(P.X, 0, 22) then ResMode := rmDnLe
	       Else If Ranges(P.X, Size.X - 23, Size.X - 1) then ResMode := rmDnRi
		    Else ResMode := rmDown;
	If ResMode = 0 then
	  If Ranges(P.X, 0, 3) then
	    If Ranges(P.Y, 0, 22) then ResMode := rmUpLe
	    Else If Ranges(P.Y, Size.Y - 23, Size.Y - 1) then ResMode := rmDnLe
		 Else ResMode := rmLeft
	  Else If Ranges(P.X, Size.X - 4, Size.X - 1) then
		 If Ranges(P.Y, 0, 22) then ResMode := rmUpRi
		 Else If Ranges(P.Y, Size.Y - 23, Size.Y - 1) then ResMode := rmDnRi
		      Else ResMode := rmRight

      End;
      If ResMode <>0 then
      With PWindow (GOwner)^ do Begin
	GOwner^.GetExtent (R);
	SizeLimits (Min, Max);
	DragView (Event,DragMode or dmDragGrow, R, Min, Max, ResMode);
	ClearEvent (Event)
      End
    End
  End
End;

procedure TFrame.SetState(AState: Word; Enable: Boolean);
Begin
  TGView.SetState (AState, Enable);
  If AState and sfActive <> 0 then DrawView;
End;

procedure TFrame.Store(var S: TStream);
Begin
  TGView.Store (S);
  S.Write (TitleSize, SizeOf (TitleSize));
End;

function TFrame.Valid(Command: Word): Boolean;
Begin
  {Valid:=TGView.Valid (Command);}
  Valid:=TView.Valid (Command);
End;

function TFrame.Zoomed: Boolean;
var Min, Max: TPoint;
Begin
  If GOwner <> nil then With GOwner^ do Begin
    SizeLimits(Min, Max);
    Zoomed := (Origin.x = 0) and (Origin.y = 0)
    and (Size.x = Max.x) and (Size.y = Max.y);
  End
  Else Zoomed := false;
End;

{**************************** TBackGround object ****************************}

Constructor TBackGround.Init(var Bounds: TRect);
Begin
  TGView.Init (Bounds);
  EventMask:=0;
  GrowMode:=gfGrowHiX+gfGrowHiY;
End;

Procedure TBackGround.Draw;
Begin
  SetFillStyle (SolidFill, GetColor (1));
  Bar (0, 0, Size.X-1, Size.Y-1);
End;

{**************************** TScrollbarBack object *************************}

function TScrollbarBack.GetPalette: PPalette;
const
  P: string[Length(CScrollbarBack)] = CScrollbarBack;
begin
  GetPalette := @P
end;

{****************************** TScrollBar object ***************************}

constructor TScrollBar.Init(var Bounds: TRect);
Begin
  TGView.Init (Bounds);
  {Options:=Options or ofPostProcess;}
  Value:=0;
  Min:=0;
  Max:=0;
  PgStep:=1;
  ArStep:=1;
  ButtonSize:=18;
  If Size.X<=ButtonSize then Begin
    Flags:=sbVertical;
    GrowMode:=gfGrowLoX + gfGrowHiX + gfGrowHiY;
  End
  Else Begin
    Flags:=sbHorizontal;
    GrowMode:=gfGrowLoY + gfGrowHiX + gfGrowHiY;
  End;
End;

constructor TScrollBar.Load(var S: TStream);
Begin
  TGView.Load (S);
  S.Read (Value, SizeOf (Value));
  S.Read (Min, SizeOf (Min));
  S.Read (Max,SizeOf (Max));
  S.Read (ArStep, SizeOf (ArStep));
  S.Read (PgStep, SizeOf (PgStep));
  S.Read (Flags, SizeOf (Flags));
  ButtonSize:=18;
End;

procedure TScrollBar.Draw;
var R: TRect;
Begin
  DrawUpButton (false);
  DrawDnButton (false);

  GetExtent (R);
  SetColor (GetColor (2));
  RectAngle (0, 0, Size.X-1, Size.Y-1);

  If Flags and sbVertical <>0 then
    R.Assign (1, ButtonSize, Size.X-2, Size.Y-ButtonSize-1)
  Else R.Assign (ButtonSize, 1, Size.X-1-ButtonSize, Size.Y-2);
  SetFillStyle (SolidFill, GetColor (5));
  Bar (R.A.X, R.A.Y, R.B.X, R.B.Y);

  If State and sfDisabled = 0 then DrawIndicator (Value, false);
End;

procedure TScrollBar.DrawUpButton(Down: Boolean);
var R: TRect;
Begin
  SetViewPort;
  GetExtent (R);
  If Flags and sbVertical <> 0 then R.B.Y:=ButtonSize
			       else R.B.X:=ButtonSize;
  DrawButton (R, Down, True, GetColor (1), GetColor (2), GetColor (3),
	      GetColor (4));
  If Flags and sbVertical <>0 then
    If GetState (sfDisabled) then Begin
      {DrawIcon (R.A.X, R.A.Y, 13, $10F);
      DrawIcon (R.A.X, R.A.Y, 9, $07);}
      DrawColIcon (R.A.X, R.A.Y, 13, 15);
      DrawColIcon (R.A.X, R.A.Y, 9, 8)
    End
    Else {DrawIcon (R.A.X, R.A.Y, 9, $0F)}
      DrawColIcon(R.A.X, R.A.Y, 9, 0)
  Else If GetState (sfDisabled) then Begin
	 {DrawIcon (R.A.X, R.A.Y, 12, $10F);
	 DrawIcon (R.A.X, R.A.Y, 8, $07);}
	 DrawColIcon(R.A.X, R.A.Y, 12, 15);
	 DrawColIcon(R.A.X, R.A.Y, 8, 8);
       End
       Else {DrawIcon (R.A.X, R.A.Y, 8, $0F);}
	 DrawColIcon(R.A.X, R.A.Y, 8, 0);
  RestoreViewPort;
End;

procedure TScrollBar.DrawDnButton(Down: Boolean);
var R: TRect;
Begin
  SetViewPort;
  GetExtent (R);
  If Flags and sbVertical <> 0 then R.A.Y:=R.B.Y-ButtonSize
			       else R.A.X:=R.B.X-ButtonSize;
  DrawButton (R, Down, True, GetColor (1), GetColor (2), GetColor (3),
	      GetColor (4));
  If Flags and sbVertical <>0 then
    If GetState (sfDisabled) then Begin
      DrawColIcon (R.A.X, R.A.Y, 14, 15);
      DrawColIcon (R.A.X, R.A.Y, 10, 8)
    End
    Else DrawColIcon(R.A.X, R.A.Y, 10, 0)
  Else If GetState (sfDisabled) then Begin
	 DrawColIcon(R.A.X, R.A.Y, 11, 15);
	 DrawColIcon(R.A.X, R.A.Y, 7, 8);
       End
       Else DrawColIcon(R.A.X, R.A.Y, 7, 0);
  RestoreViewPort;
End;

procedure TScrollBar.DrawIndicator(V: Integer; Down: Boolean);
var R: TRect;
    Pos: Integer;
Begin
  SetViewPort;
  Pos:=GetPosition (V);
  If Flags and sbVertical <>0 then
    R.Assign (0, ButtonSize-1+Pos, Size.X, ButtonSize-1+Pos+ButtonSize)
  Else R.Assign (ButtonSize-1+Pos, 0, ButtonSize-1+Pos+ButtonSize, Size.Y);
  DrawButton (R, Down, True, GetColor (1), GetColor (2), GetColor (3),
	      GetColor (4));
  RestoreViewPort;
End;

function TScrollBar.GetPalette: PPalette;
const P: String [Length (CScrollBar)] = CScrollBar;
Begin
  GetPalette:=@P;
End;

function TScrollBar.GetPosition(V: Integer): Integer;
var R: TRect;
Begin
  If Max>Min then Begin
    GetExtent (R);
    If Flags and sbVertical <>0 then Begin
      R.Grow (-1,-ButtonSize);
      Dec (R.B.Y,ButtonSize-2);
      GetPosition:=Round ((R.B.Y-R.A.Y) * 1.0 / (Max-Min) * (V-Min));
    End
    Else Begin
      R.Grow (-ButtonSize,-1);
      Dec (R.B.X,ButtonSize-2);
      GetPosition:=Round ((R.B.X-R.A.X) * 1.0 / (Max-Min) * (V-Min));
    End;
  End
  Else GetPosition:=0;
End;

procedure TScrollBar.HandleEvent(var Event: TEvent);
var MIV, Down: Boolean;
    Delta, A: Integer;
    R, H: TRect;
    EvWhere, P: TPoint;

 function ValueOf (Pos: TPoint): Integer;
 var R: TRect;
     V: Integer;
 Begin
   GetExtent (R);
   If Flags and sbVertical <>0 then Begin
     R.Grow (0,-ButtonSize);
     Dec (R.B.Y,ButtonSize-2);
     V:=Round (((Max-Min)*1.0/(R.B.Y-R.A.Y)*(Pos.Y-R.A.Y))+Min);
   End
   Else Begin
     R.Grow (-ButtonSize,0);
     Dec (R.B.X,ButtonSize-2);
     V:=Round (((Max-Min)*1.0/(R.B.X-R.A.X)*(Pos.X-R.A.X))+Min);
   End;
   If V>Max then V:=Max;
   If V<Min then V:=Min;
   ValueOf:=V;
 End;

Begin
  TGView.HandleEvent (Event);
  MIV:=MouseInView (Event.Where);
  If (Event.What=evKeyBoard) {and ((Flags and sbHandleKeyBoard <>0) or
     GetState (sfSelected)) } then Begin
    Delta:=0;
    If Flags and sbVertical <>0 then
      Case Event.KeyCode Of
	kbUp       : Delta:=ScrollStep (sbUpArrow);
	kbDown     : Delta:=ScrollStep (sbDownArrow);
	kbPgDn     : Delta:=ScrollStep (sbPageDown);
	kbPgUp     : Delta:=ScrollStep (sbPageUp);
	kbCtrlPgDn : Delta:=Max-Value;
	kbCtrlPgUp : Delta:=Min-Value;
       else Exit;
      End
    Else
      Case Event.KeyCode Of
	kbRight : Delta:=ScrollStep (sbRightArrow);
        kbLeft  : Delta:=ScrollStep (sbLeftArrow);
        kbHome  : Delta:=Min-Value;
	kbEnd   : Delta:=Max-Value;
       else Exit;
      End;
    ClearEvent (Event);
    SetValue (Value+Delta);
  End;
  If (Event.What=evMouseDown) and MIV then Begin
    GetExtent (R);
    MakeLocal (Event.Where, EvWhere);
    If Flags and sbVertical <>0 then R.B.Y:=ButtonSize
				else R.B.X:=ButtonSize;
    If R.Contains (EvWhere) then Begin
      Down:=True;
      DrawUpButton (True);
      Repeat
        MakeLocal (Event.Where, EvWhere);
        If (R.Contains (EvWhere) and not Down) or
           (not R.Contains (EvWhere) and Down) then Begin
          Down:=not Down;
          DrawUpButton (Down);
	End;
        If Down then
          If Flags and sbVertical <>0 then SetValue (Value+ScrollStep (sbUpArrow))
                                      else SetValue (Value+ScrollStep (sbLeftArrow));

      Until not MouseEvent (Event, evMouseAuto);
      DrawUpButton (False);
      ClearEvent (Event);
    End;
    GetExtent (R);
    If Flags and sbVertical <>0 then R.A.Y:=R.B.Y-ButtonSize
                                else R.A.X:=R.B.X-ButtonSize;
    If R.Contains (EvWhere) then Begin
      Down:=True;
      DrawDnButton (True);
      Repeat
        MakeLocal (Event.Where, EvWhere);
        If (R.Contains (EvWhere) and not Down) or
           (not R.Contains (EvWhere) and Down) then Begin
          Down:=not Down;
	  DrawDnButton (Down);
	End;
        If Down then
          If Flags and sbVertical <>0 then SetValue (Value+ScrollStep (sbDownArrow))
				      else SetValue (Value+ScrollStep (sbRightArrow));

      Until not MouseEvent (Event, evMouseAuto);
      DrawDnButton (False);
      ClearEvent (Event);
    End;
    GetExtent (R);
    If Flags and sbVertical <>0 then R.Grow (0,-ButtonSize)
                                else R.Grow (-ButtonSize,0);
    H:=R;
    If Flags and sbVertical <>0 then Begin
      R.B.Y:=R.A.Y+ButtonSize;
      R.Move (0,GetPosition (Value)-1);
    End
    Else Begin
      R.B.X:=R.A.X+ButtonSize;
      R.Move (GetPosition (Value)-1,0);
    End;
    If R.Contains (EvWhere) then Begin
      P.X:=EvWhere.X-R.A.X;
      P.Y:=EvWhere.Y-R.A.Y;
      Down:=False;
      Repeat
        MakeLocal (Event.Where, EvWhere);
        If (MouseInView (Event.Where) and not Down) or
           (not MouseInView (Event.Where) and Down) then Begin
          Down:=not Down;
          DrawIndicator (Value, Down);
        End;
        If Down then Begin
	  If Flags and sbVertical <>0 then Dec (EvWhere.Y, P.Y)
                                      else Dec (EvWhere.X, P.X);
          Delta:=ValueOf (EvWhere);
	  If (Min<=Delta) and (Max>=Delta) and (Value<>Delta) then Begin
            RestoreIndicator (Value);
            Value:=Delta;
	    DrawIndicator (Value, True);
            If Flags and sbDoScrolling <> 0 then ScrollDraw;
          End;
        End;
      Until not MouseEvent (Event, evMouseAuto);
      DrawIndicator (Value, false);
      ScrollDraw;
      ClearEvent (Event);
    End
    Else If H.Contains (EvWhere) then Begin
           If Flags and sbVertical<>0 then A:=EvWhere.Y-ButtonSize
                                      else A:=EvWhere.X-ButtonSize;
           Down:=GetPosition (Value)<A;
           Repeat
             MakeLocal (Event.Where, EvWhere);
             If Flags and sbVertical<>0 then A:=EvWhere.Y-ButtonSize
                                        else A:=EvWhere.X-ButtonSize;
             If Down then SetValue (Value+ScrollStep (sbPageDown))
		     else SetValue (Value+ScrollStep (sbPageUp));
	   Until not MouseEvent (Event, evMouseAuto) or
		 (Down and (GetPosition (Value)>A)) or
		 (not Down and (GetPosition (Value)<A));
           ClearEvent (Event);
         End;
  End;
End;

procedure TScrollBar.RestoreIndicator(OldV: Integer);
var R: TRect;
    POldV: Integer;
Begin
  SetViewPort;
  HideMouse;
  GetExtent (R);
  If Flags and sbVertical <>0 then
    R.Assign (1, ButtonSize, Size.X-1, Size.Y-ButtonSize)
  Else R.Assign (ButtonSize, 1, Size.X-ButtonSize, Size.Y-1);
  POldV:=GetPosition (OldV);
  If Flags and sbVertical <>0 then Begin
    Inc (R.A.Y,POldV);
    If POldV>GetPosition (Min) then Dec (R.A.Y);
    R.B.Y:=R.A.Y+ButtonSize;
    If POldV>=GetPosition (Max) then Dec (R.B.Y);
  End
  Else Begin
    Inc (R.A.X, POldV);
    If POldV>GetPosition (Min) then Dec (R.A.X);
    R.B.X:=R.A.X+ButtonSize;
    If POldV>=GetPosition (Max) then Dec (R.B.X);
  End;
  SetFillStyle (SolidFill,GetColor (5));
  Bar (R.A.X, R.A.Y, R.B.X-1, R.B.Y-1);
  ShowMouse;
  RestoreViewPort;
End;

procedure TScrollBar.ScrollDraw;
Begin
  If GetState(sfExposed)
  then Message(GOwner, evBroadCast, cmScrollBarChanged, @Self);
End;

function TScrollBar.ScrollStep(Part: Integer): Integer;
Begin
  Case Part Of
    sbLeftArrow, sbUpArrow    : ScrollStep:=-ArStep;
    sbRightArrow, sbDownArrow : ScrollStep:=ArStep;
    sbPageLeft, sbPageUp      : ScrollStep:=-PgStep;
    sbPageRight, sbPageDown   : ScrollStep:=PgStep;
   else ScrollStep:=0;
  End;
End;

procedure TScrollBar.SetParams(AValue, AMin, AMax, APgStep, AArStep: Integer);
var OldV: Integer;
Begin
  If AMax < AMin then AMax := AMin;
  If AValue < AMin then AValue := AMin;
  If AValue > AMax then AValue := AMax;
  OldV := Value;
  If (OldV <> AValue) or (Min <> AMin) or (Max <> AMax) then Begin
    Value := AValue;
    If Exposed and not GetState (sfDragging) then Begin
      RestoreIndicator (OldV);
      Min := AMin;
      Max := AMax;
      If State and sfDisabled = 0 then DrawIndicator (Value, false);
    End
    Else Begin
      Min := AMin;
      Max := AMax;
    End;

    If OldV <> AValue then ScrollDraw;
  End;
  PgStep := APgStep;
  ArStep := AArStep;

  If Min = Max then Begin
    If State and sfDisabled = 0 then SetState (sfDisabled, True)
  End
  Else If State and sfDisabled <> 0 then SetState (sfDisabled, False);
End;

procedure TScrollBar.SetRange(AMin, AMax: Integer);
Begin
  SetParams (Value,AMin,AMax,PgStep,ArStep);
End;

procedure TScrollBar.SetState(AState: Word; Enable: Boolean);
Begin
  TGView.SetState (AState, Enable);
  If (AState and sfDisabled <>0) and Exposed and
     (State and sfDragging = 0) then Begin
    If not Enable then DrawIndicator(Value, False)
		  else RestoreIndicator(Value);
    DrawUpButton (False);
    DrawDnButton (False);
  End;
End;

procedure TScrollBar.SetStep(APgStep, AArStep: Integer);
Begin
  SetParams (Value,Min,Max,APgStep,AArStep);
End;

procedure TScrollBar.SetValue(AValue: Integer);
Begin
  SetParams (AValue,Min,Max,PgStep,ArStep);
End;

procedure TScrollBar.Store(var S: TStream);
Begin
  TGView.Store (S);
  S.Write (Value, SizeOf (Value));
  S.Write (Min, SizeOf (Min));
  S.Write (Max,SizeOf (Max));
  S.Write (ArStep, SizeOf (ArStep));
  S.Write (PgStep, SizeOf (PgStep));
  S.Write (Flags, SizeOf (Flags));
End;

{****************************** TScroller object ****************************}

constructor TScroller.Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar);
Begin
  TGView.Init (Bounds);
  Options := Options or ofSelectable;
  EventMask := EventMask or evBroadcast;
  HScrollBar := AHScrollBar;
  VScrollBar := AVScrollBar;
  TextSize.X := 8;
  TextSize.Y := 18;
End;

constructor TScroller.Load(var S: TStream);
Begin
  TGView.Load (S);
  GetPeerViewPtr (S, HScrollBar);
  GetPeerViewPtr (S, VScrollBar);
  S.Read (Delta, SizeOf (TPoint)*3);
End;

procedure TScroller.ChangeBounds(var Bounds: TRect);

 procedure SetScrollBars(Enable: Boolean);
 Begin
   If HScrollBar <> nil then HScrollBar^.SetState(sfDragging, Enable);
   If VScrollBar <> nil then VScrollBar^.SetState(sfDragging, Enable);
 End;

Begin
  SetBounds (Bounds);
  Inc (ScrollLock);
  SetScrollBars(true);
  SetLimit (Limit.X, Limit.Y);
  SetScrollBars(false);
  Dec (ScrollLock);
End;

function TScroller.GetPalette: PPalette;
const
  P: String [Length (CScroller)] = CScroller;
Begin
  GetPalette := @P;
End;

procedure TScroller.HandleEvent(var Event: TEvent);
Begin
  TGView.HandleEvent(Event);
  If (Event.What = evBroadcast) and (Event.Command = cmScrollBarChanged) and
     ((Event.InfoPtr = HScrollBar) or (Event.InfoPtr = VScrollBar)) then
      ScrollDraw;
End;

procedure TScroller.ScrollDraw;
var
  D, O: TPoint;
begin
  {If ScrollLock > 0 then Exit;}
  If HScrollBar <> nil then D.X := HScrollBar^.Value
		       else D.X := 0;
  If VScrollBar <> nil then D.Y := VScrollBar^.Value
		       else D.Y := 0;
  If (D.X <> Delta.X) or (D.Y <> Delta.Y) then Begin
    HideCursor;
    O.X := (Delta.X - D.X) * TextSize.X;
    O.Y := (Delta.Y - D.Y) * TextSize.Y;
    SetCursor(Cursor.X + O.X, Cursor.Y + O.Y);
    If ScrollLock = 0
    then begin
{---
      Delta := D;
      ScrollView(@Self, O, nil);
 ---}
      BeginScroll(O, nil);
      Delta := D;
      EndScroll
    end;
    ShowCursor
  End
end;

procedure TScroller.ScrollTo(X, Y: Integer);
Begin
  Inc(ScrollLock);
  If HScrollBar <> nil then HScrollBar^.SetValue (X);
  If VScrollBar <> nil then VScrollBar^.SetValue (Y);
  Dec(ScrollLock);
  ScrollDraw;
End;

procedure TScroller.SetLimit(X, Y: Integer);
Begin
  Limit.X := X;
  Limit.Y := Y;
  Inc(ScrollLock);
  If HScrollBar <> nil then With HScrollBar^ do
    SetParams (Value, 0, X - (Self.Size.X div TextSize.X),
      (Self.Size.X - 1) div TextSize.X, ArStep);
  If VScrollBar <> nil then With VScrollBar^ do
    SetParams(Value, 0, Y - (Self.Size.Y div TextSize.Y),
      (Self.Size.Y - 1) div TextSize.Y, ArStep);
  Dec(ScrollLock);
  ScrollDraw;
End;

function TScroller.ShownRows: Integer;
begin
  ShownRows := (Size.Y + TextSize.Y - 1) div TextSize.Y
end;

function TScroller.ShownCols: Integer;
begin
  ShownCols := (Size.X + TextSize.X - 1) div TextSize.X
end;

procedure TScroller.Store(var S: TStream);
Begin
  TGView.Store (S);
  PutPeerViewPtr (S, HScrollBar);
  PutPeerViewPtr (S, VScrollBar);
  S.Write (Delta, SizeOf (TPoint)*3);
End;

{***************************** TListViewer object ***************************}

constructor TListViewer.Init(var Bounds: TRect; AScrollBar: PScrollBar);
Begin
  TGView.Init (Bounds);
  Options:=Options or (ofFirstClick+ofSelectable);
  EventMask:=evMouse+evKeyBoard+evBroadcast+evCommand;
  ScrollBar:=AScrollBar;
  SetNumCols(1);
End;

constructor TListViewer.Load(var S: TStream);
Begin
  TGView.Load(S);
  GetPeerViewPtr(S,ScrollBar);
  S.Read(Focused,SizeOf (Focused));
  S.Read(Range, SizeOf (Range));
  S.Read(TopItem,SizeOf (TopItem));
  S.Read(Flags, SizeOf(Flags));
  S.Read(NumCols, SizeOf(NumCols))
End;

procedure TListViewer.ChangeBounds(var Bounds: TRect);
Begin
  TGView.ChangeBounds (Bounds);
  If ScrollBar<>nil then
    if NumCols = 1
      then ScrollBar^.SetStep(GetPageSize+1, 1)
    else ScrollBar^.SetStep(GetPageSize+1, GetNumRows);
End;

procedure TListViewer.Draw;
var I: Integer;
Begin
  SetFillStyle (SolidFill,GetColor (1));
  Bar (0, 0, Size.X-1, Size.Y-1);
  SetColor (GetColor (2));
  RectAngle (0, 0, Size.X-1, Size.Y-1);
  For I := TopItem to TopItem + GetPageSize
    + (Flags and lfPartialLines) * NumCols do
    If I<Range then DrawItem (I);
End;

procedure TListViewer.DrawItem(Item: Integer);
var R, Sub: TRect;
    C, Tc: Word;
Begin
  If Item>=Range then Exit;
  SetViewPort;
  GetItemRect(Item, R);
  If GetState (sfSelected) then If Item=Focused then Begin C:=6; Tc:=4 End
						else Begin C:=5; Tc:=3 End
			   else Begin C:=5; Tc:=3 End;
  SetFillStyle (SolidFill,GetColor (C));
  If (Item >= TopItem) and
     (Item <= TopItem + GetPageSize + (Flags and lfPartialLines) * NumCols) then Begin
    GetItemSubRect(Sub);
    Sub.Intersect(R);
    SetSubRect(Sub);
    Bar (R.A.X, R.A.Y, R.B.X-1, R.B.Y-1);
    If IsSelected (Item) and
       ((Item <> Focused) or (State and sfSelected = 0)) then Begin
      SetColor (GetColor (2));
      SetLineStyle (4,$BBBB,1);
      RectAngle (R.A.X, R.A.Y, R.B.X-1, R.B.Y-1);
      SetLineStyle (SolidLn,0,1);
    End;
    SetTextJustify(LeftText, CenterText);
    SetTextParams(ftSansSerif, 0, GetColor(Tc), false);
    DrawItemText(Item, R);
  End;
  RestoreViewPort;
End;

procedure TListViewer.DrawItemText(Item: Integer; R: TRect);
var
  S: String;
  Sub: TRect;
Begin
  S := GetText(Item, $FF);
  While TextWidth(S) > R.B.X do
    Dec(S[0]);
  R.Grow(-5, 0);
  GetItemSubRect(Sub);
  Sub.Intersect(R);
  SetSubRect(Sub);
  Dec(R.B.Y, R.A.Y);
  OutTextXY(R.A.X, R.A.Y + R.B.Y div 2 + 1, S);
End;

procedure TListViewer.FocusItem(Item: Integer);
var
  P: TPoint;
  R, ItemR, H: TRect;
  OldF, OldTop, NewTop: Integer;
  NumRows: Integer;
Begin
  If (Item<>Focused) and (Item<Range) and (Item>=0) then Begin
    OldF := Focused;
    OldTop := TopItem;
    Focused:=Item;
    If ScrollBar<>nil then ScrollBar^.SetValue(Item);
    P.X := 0;
    GetItemSubRect(R);

    If (Item=TopItem-1) and (NumCols = 1) then Begin
      DrawItem(OldF);
      If Flags and lfPartialLines <> 0
	then DrawItem(Focused);
      Dec(TopItem);
      GetItemRect(GetPageSize + TopItem, ItemR);
      If Flags and lfPartialLines = 0
      then R.B.Y := ItemR.B.Y;
      GetItemRect(Item, ItemR);
      P.Y := ItemR.B.Y - ItemR.A.Y;
      {ScrollView(@Self, P, @R);}
      NewTop := TopItem; TopItem := OldTop;
      BeginScroll(P, @R);
      TopItem := NewTop;
      EndScroll
    End else
    If (Item = GetPageSize + 1 + TopItem) and (NumCols = 1) then Begin
      DrawItem(OldF);
      If Flags and lfPartialLines <> 0
	then DrawItem(Focused);
      P.Y := 0;
      Repeat
	GetItemRect(TopItem, H);
	Dec(P.Y, H.B.Y - H.A.Y);
	Inc (TopItem);
	GetItemRect(Item, ItemR)
      Until (ItemR.B.Y <= R.B.Y) and (ItemR.B.X <= R.B.X);
      If Flags and lfPartialLines = 0
      then R.B.Y := ItemR.B.Y;
      {ScrollView(@Self, P, @R);}
      NewTop := TopItem; TopItem := OldTop;
      BeginScroll(P, @R);
      TopItem := NewTop;
      EndScroll
    End else
    If (Item>GetPageSize+TopItem) then Begin
      NumRows := GetNumRows;
      if NumCols <> 1
      then begin
	Repeat
	  Inc(TopItem, NumRows);
	  GetItemRect(Item, ItemR)
	Until ItemR.B.X <= R.B.X
      end
      else begin
        TopItem := Item - NumRows + 1;
	{Repeat
	  Inc(TopItem);
	  GetItemRect(Item, ItemR)
	Until ItemR.B.Y <= R.B.Y;}
      end;
      If TopItem<0 then TopItem:=0;
      DrawView;
    End else
    If Item<TopItem then Begin
      if NumCols <> 1
      then begin
	NumRows := GetNumRows;
	TopItem := Item - (Item mod NumRows);
      end
      else TopItem:=Item;
      DrawView;
    End else Begin
      DrawItem(OldF);
      DrawItem(Focused);
    End
  End
End;

function TListViewer.GetItemHeight: Integer;
begin
  GetItemHeight := 15
end;

procedure TListViewer.GetItemRect(Item: Integer; var R: TRect);
var
  Row, Col, NumRows, ItemHeight, ItemWidth: Integer;
  SubRect: TRect;
Begin
  { This will work only with one-column list viewers: }
  {R.Assign (3, 3+(Item-TopItem)*15, Size.X-3, 18+(Item-TopItem)*15);}

  GetItemSubRect(SubRect);
  ItemHeight := GetItemHeight;
  ItemWidth := (SubRect.B.X - SubRect.A.X) div NumCols;
  if NumCols = 1
  then begin
    Row := Item - TopItem;
    Col := 0
  end
  else begin
    if Flags and (lfVerticalFirst+lfHorizontalFirst) = lfVerticalFirst
    then begin
      NumRows := (SubRect.B.Y - SubRect.A.Y) div ItemHeight;
      if NumRows = 0 then NumRows := 1;
      Row := (Item - TopItem) mod NumRows;
      Col := (Item - TopItem) div NumRows
    end
    else begin { Note: lfHorizontalFirst not yet properly implemented. }
      Row := (Item - TopItem) div NumCols;
      Col := (Item - TopItem) mod NumCols
    end;
  end;
  R.Assign(SubRect.A.X + Col * ItemWidth,
    SubRect.A.Y + Row * ItemHeight,
    SubRect.A.X + (Col+1) * ItemWidth,
    SubRect.A.Y + (Row+1) * ItemHeight);

End;

procedure TListViewer.GetItemSubRect(var R: TRect);
Begin
  R.Assign(3, 3, Size.X - 3, Size.Y - 3);
End;

function TListViewer.GetNumRows: Integer;
var
  SubRect: TRect;
begin
  GetItemSubRect(SubRect);
  GetNumRows := (SubRect.B.Y - SubRect.A.Y) div GetItemHeight;
end;

function TListViewer.GetPageSize: Integer;
var I, C: Integer;
    R, Sub: TRect;
Begin
  I := TopItem;
  GetItemSubRect(Sub);
  C := 0;
  Repeat
    GetItemRect(I, R);
    Inc(I); Inc(C);
  Until (R.B.Y > Sub.B.Y) or (R.B.X > Sub.B.X);
  Dec(C, 2);
  GetPageSize := C;
End;

function TListViewer.GetPalette: PPalette;
Const P: String [Length (CListViewer)] = CListViewer;
Begin
  GetPalette:=@P;
End;

function TListViewer.GetText(Item: Integer; MaxLen: Integer): String;
Begin Abstract End;

procedure TListViewer.HandleEvent(var Event: TEvent);
var R: TRect;
    EvWhere: TPoint;
    I: Integer;

  procedure CheckedFocus(NewFocused: Integer);
  begin
    if NewFocused < 0 then FocusItem(0) else
    if NewFocused >= Range then FocusItem(Range - 1) else
    FocusItem(NewFocused)
  end;

Begin
  TGView.HandleEvent (Event);
  If Event.What=evKeyDown then Begin
    I := GetPageSize;
    Case Event.KeyCode Of
      kbUp       : If Focused>0 then FocusItem (Focused-1);
      kbDown     : If Focused<Range-1 then FocusItem (Focused+1);
      kbLeft:
	if NumCols <> 1
	then CheckedFocus(Focused - GetNumRows);
      kbRight:
	if NumCols <> 1
	then CheckedFocus(Focused + GetNumRows);
      kbHome:
	FocusItem (TopItem);
      kbEnd:
	CheckedFocus(TopItem+I);
      kbPgUp:
	CheckedFocus(Focused - I - 1);
      kbPgDn:
	CheckedFocus(Focused + I+1);
      kbCtrlPgUp:
	FocusItem(0);
      kbCtrlPgDn:
	FocusItem(Range-1);
     else If Event.CharCode=' ' then SelectItem(Focused)
				else Exit;
    end;
    ClearEvent (Event);
  End;
  If (Event.What=evMouseDown) and MouseInView (Event.Where) then Begin
    Repeat
      MakeLocal (Event.Where,EvWhere);
      For I := TopItem to TopItem + GetPageSize + (Flags and lfPartialLines) * NumCols do
      begin
	GetItemRect(I, R);
	If R.Contains (EvWhere) then Begin
	  FocusItem(Min(I, Range - 1));
	  If Event.Double and (I < Range) then
	    SelectItem(Focused);
	End;
      End;
      {R.Assign (0,0,Size.X-1,Size.Y-1);}
      GetItemSubRect(R);
      if NumCols = 1
      then begin
	If EvWhere.Y<R.A.Y then CheckedFocus(Focused-1) else
	if EvWhere.Y>=R.B.Y then CheckedFocus(Focused+1)
      end
      else begin
	if EvWhere.X<R.A.X then CheckedFocus(Focused - GetNumRows) else
	if EvWhere.X>=R.B.X then CheckedFocus(Focused + GetNumRows)
      end
    Until not MouseEvent (Event, evMouseMove+evMouseAuto);
    ClearEvent (Event);
  End;
  If (Event.What=evBroadCast) and (Event.InfoPtr=ScrollBar) and
     (Event.Command=cmScrollBarChanged) and
     (Focused <> ScrollBar^.Value) then Begin
    If not GetState (sfSelected) then Select;
    FocusItem (ScrollBar^.Value);
  End;
End;

function TListViewer.IsSelected(Item: Integer): Boolean;
Begin
  IsSelected:=Item=Focused
End;

procedure TListViewer.SelectItem(Item: Integer);
Begin
  Message(GOwner, evBroadCast, cmListItemSelected, @Self);
End;

procedure TListViewer.SetNumCols(Num: Integer);
begin
  NumCols := Num;
  if ScrollBar <> nil
  then if NumCols = 1
    then ScrollBar^.SetStep(GetPageSize+1, 1)
    else ScrollBar^.SetStep(GetPageSize+1, GetNumRows);
end;

procedure TListViewer.SetRange(ARange: Integer);
Begin
  Range:=ARange;
  If ScrollBar<>nil then ScrollBar^.SetRange (0,Range-1);
  If Focused>=Range then FocusItem (0);
End;

procedure TListViewer.SetState(AState: Word; Enable: Boolean);
Begin
  TGView.SetState (AState,Enable);
  If AState and sfSelected <>0 then If Exposed then DrawItem (Focused);
End;

procedure TListViewer.Store(var S: TStream);
Begin
  TGView.Store (S);
  PutPeerViewPtr (S,PGView (ScrollBar));
  S.Write (Focused, SizeOf (Focused));
  S.Write (Range, SizeOf (Range));
  S.Write (TopItem, SizeOf (TopItem));
  S.Write (Flags, SizeOf (Flags));
  S.Write(NumCols, SizeOf(NumCols))
End;

{*************************** TGGroup object *********************************}

constructor TGGroup.Init(var Bounds: TRect);
Begin
  TGView.Init (Bounds);
  Last:=nil;
  Current:=nil;
  Options := (Options or (ofSelectable + ofBufferOne)) and not ofFirstPass;
  EventMask:=$FFFF;
  StandardFont:=ftSansSerif;
End;

{$ifdef NOIASM}
constructor TGGroup.Load(var S: TStream);
begin
  {% not yet implemented}
end;
{$else}
constructor TGGroup.Load(var S: TStream);
var FixupSave: PFixupList;
    Count, I: Integer;
    P, Q: ^Pointer;
    V: PGView;
    OwnerSave: PGGroup;
Begin
  SetCurrentCursor (mcHourGlass);

  TGView.Load(S);
  S.Read (StandardFont, SizeOf (StandardFont));
  OwnerSave := OwnerGroup;
  OwnerGroup := @Self;
  FixupSave := FixupList;
  S.Read(Count, SizeOf(Word));
  asm
	MOV     CX,Count
	SHL     CX,1
	SHL     CX,1
	SUB     SP,CX
	MOV     FixupList.Word[0],SP
	MOV     FixupList.Word[2],SS
	MOV     DI,SP
	PUSH    SS
	POP     ES
	XOR     AL,AL
	CLD
	REP     STOSB
  end;
  for I := 1 to Count do
  begin
    V := PGView(S.Get);
    if V <> nil then InsertView(V, nil);
  end;
  V := Last;
  for I := 1 to Count do
  begin
    V := V^.GNext;
    P := FixupList^[I];
    while P <> nil do
    begin
      Q := P;
      P := P^;
      Q^ := V;
    end;
  end;
  OwnerGroup := OwnerSave;
  FixupList := FixupSave;
  GetSubViewPtr(S, V);
  SetCurrent(V, NormalSelect);
  if OwnerGroup = nil then Awaken;

  SetCurrentCursor (mcNoCursor);
End;
{$endif NOIASM}

destructor TGGroup.Done;
var P, T: PGView;
Begin
  Hide;
  P := Last;
  if P <> nil then
  begin
    repeat
      P^.Hide;
      P := P^.Prev;
    until P = Last;
    repeat
      T := P^.Prev;
      Dispose(P, Done);
      P := T;
    until Last = nil;
  end;
  TGView.Done;
End;

{$ifdef NOIASM}
function TGGroup.At(Index: Integer): PGView;
var
  V: PGView;
  i: Integer;
begin
  V := Last;
  for i := 1 to Index do
    V := V^.GNext;
  At := V
end;
{$else}
function TGGroup.At(Index: Integer): PGView; assembler;
asm
	LES     DI,Self
	LES     DI,ES:[DI].TGGroup.Last
	MOV     CX,Index
@@1:    LES     DI,ES:[DI].TGView.GNext
	LOOP    @@1
	MOV     AX,DI
	MOV     DX,ES
end;
{$endif}

procedure TGGroup.Awaken;

  procedure DoAwaken(P: PGView); {$ifndef FPK}far;{$endif}
  begin
    P^.Awaken;
  end;

begin
  ForEachInMethod(@DoAwaken, @Self);
end;

procedure TGGroup.CalcFirstPass;
begin
  SetState(sfFirstPass, false)
end;

procedure TGGroup.ChangeBounds(var Bounds: TRect);
var D: TPoint;
    VisBefore: PVis;
    OCB: TRect;

 procedure DoCalcChange(P: PGView); {$ifndef FPK}far;{$endif}
 var R: TRect;
 begin
   P^.CalcBounds(R, D);
   P^.ChangeBounds(R);
 end;

begin
  D.X := Bounds.B.X - Bounds.A.X - Size.X;
  D.Y := Bounds.B.Y - Bounds.A.Y - Size.Y;
  if (D.X = 0) and (D.Y = 0) then
  begin
    if State and (sfVisible + sfExposed) <> (sfVisible + sfExposed)
    then SetBounds(Bounds)
    {$ifdef Windows}
    else if Wnd <> 0
    then begin
      SetBounds(Bounds);
      UpdateWinPos
    end
    {$endif Windows}
    else begin
      {VisBefore := GetVisibility;}
      If GetOwnerClippedBounds(OCB)
      then begin
	VisBefore := NewVis(OCB);
	SubtractInvisible(VisBefore, nil, true { Trans sub });
      end
      else VisBefore := nil;
      D.X := Bounds.A.X - Origin.X;
      D.Y := Bounds.A.Y - Origin.Y;
      CopyDrawView(VisBefore, Bounds, D)
    end
  end else
  begin
    SetBounds(Bounds);
    Lock;
    ForEachInMethod(@DoCalcChange, @Self);
    {$ifdef Windows}
    If Wnd <> 0 then UpdateWinPos;
    {$endif Windows}
    Unlock;
  end;
end;

procedure TGGroup.ChMCursor;
var
  P: PGView;
  Where: TPoint;
  R: TRect;
begin
  MakeLocal(MouseWhere, Where);
  If (Where.x >= 0) and (Where.x < Size.x) and
     (Where.y >= 0) and (Where.y < Size.y)
  then begin
    NewNum := MCurStandard;
    P := First;
    while P <> nil do
    begin
      If P^.State and sfVisible <> 0
      then Begin
	P^.GetBounds(R);
	If R.Contains(Where) then begin
	  P^.ChMCursor;
	  Exit
	end
      end;
      P := P^.NextView
    end
  end
End;

function TGGroup.DataSize: Sw_Word;
var
  T: Word;

  procedure AddSubviewDataSize(P: PView); {$ifndef FPK}far;{$endif}
  begin
    Inc(T, P^.DataSize);
  end;

begin
  T := 0;
  ForEachInMethod(@AddSubviewDataSize, @Self);
  DataSize := T;
end;

procedure TGGroup.Delete(P: PGView);
var SaveState: Word;
Begin
  SaveState := P^.State;
  P^.Hide;
  RemoveView(P);
  P^.Owner := nil;
  P^.GOwner := nil;
  P^.GNext := nil;
  if SaveState and sfVisible <> 0 then P^.Show;
End;

procedure TGGroup.Draw;
{ This method will only be called by a FirstPassDraw action,
  mainly for transparent overlapping.
}
var
  P: PGView;
Begin
  {do a kind of ReDraw}
  P := Last;
  while P <> nil do
  begin
    If P^.State and (sfVisible + sfRoot) = sfVisible
    then DrawFirstPass(P, nil);
    P := P^.PrevView
  end;
End;

procedure TGGroup.DrawClipped(Clip: PVis; VisOwner: PGView);
var
  Orig: TPoint;
  StateMask: Word;
  R: TRect;

	function FirstPassView(P: PGView): Boolean; {$ifndef FPK}far;{$endif}
	begin
	  FirstPassView := P^.State and sfFirstPass <> 0
	end;

	function TransparentView(P: PGView): Boolean; {$ifndef FPK}far;{$endif}
	begin
	  TransparentView := P^.State and (sfTransparent + sfRoot) = sfTransparent
	end;

	procedure DoDrawFirstPass; {$ifndef FPK}far;{$endif}
	var
	  P: PGView;
	begin
	  P := Last;
	  while P <> nil do
	    with P^ do
	    begin
	      If State and (sfVisible + sfFirstPass + sfRoot) = sfVisible + sfFirstPass
	      then DrawFirstPass(P, @Self);
	      P := P^.PrevView
	    end;
	end;

	procedure DoDrawClipped(P: PGView); {$ifndef FPK}far;{$endif}
	var
	  R: TRect;
	{$ifdef Windows}
          PropOrig: TPoint;
	  v: PVis;
	{$endif}
	begin
	  with P^ do
	    If State and sfVisible <> 0
	    then begin
	      {$ifdef Windows}
	      if State and sfRoot <> 0
	      then begin
		{The target view P into which we propagate the vis is rooted.
		 That means, the vis must be moved into the target's co-ordinate
		 system (the target is P->MakeSemiGlobal()-invariant.) }
		P^.GOwner^.MakeSemiGlobal(P^.Origin, PropOrig);
		v := CopyVis(Clip);
		PropOrig.x := -PropOrig.x;
		PropOrig.y := -PropOrig.y;
		MoveVis(v, PropOrig);
		P^.DrawClipped(v, P);
	      end
	      else
	      {$endif Windows}
		   begin
		If State and StateMask = 0
		then DrawClipped(CopyVis(Clip), P);
		GetBounds(R);
		R.Move(Orig.x, Orig.y);
		If State and sfTransparent = 0
		then SubtractRegion(Clip, R)
		else IndirectRegion(Clip, R, P)
	      end
	    end
	end;

begin
  If Exposed
  then begin
    Orig.X := 0; Orig.Y := 0;
    MakeSemiGlobal(Orig, Orig);
    GetOwnerClippedBounds(R);
    ClipVis(Clip, R);
    SubtractInvisible(Clip, VisOwner, false);
    if not EmptyVis(Clip)
    then begin
      StateMask := sfFirstPass + sfTransparent;
      { First pass
      }
      If FirstThatInMethod(@FirstPassView, @Self) <> nil
      then begin
        If FirstThatInMethod(@TransparentView, @Self) <> nil
        then StateMask := sfTransparent
        else DrawVisLocal(Clip, Orig, @Self, @DoDrawFirstPass, CurBP);
      end;
      { Second pass
      }
      ForEachInMethod(@DoDrawClipped, @Self);
    end;
  end;
  FreeVis(Clip)
end;

procedure TGGroup.DrawVisible;
Begin
  ReDraw
End;

procedure TGGroup.EndModal(Command: Word);
Begin
  If State and sfModal <> 0 then EndState := Command
			    else TGView.EndModal(Command);
End;

procedure TGGroup.EventError(var Event: TEvent);
Begin
  If GOwner<>nil then GOwner^.EventError (Event);
End;

function TGGroup.Execute: Word;
var Event: TEvent;
Begin
  Repeat
    EndState:=0;
    Repeat
      GetEvent (Event);
      HandleEvent (Event);
      If Event.What<>evNothing then EventError (Event);
    Until EndState<>0;
  Until Valid (EndState);
  Execute:=EndState;
End;

function TGGroup.ExecView(P: PGView): Word;
var SaveOptions: Word;
    SaveOwner: PGGroup;
    SaveTopView: PGView;
    SaveCurrent: PGView;
    SaveCommands: TCommandSet;
Begin
  if P <> nil then
  begin
    SaveOptions := P^.Options;
    SaveOwner := P^.GOwner;
    SaveTopView := TheTopView;
    SaveCurrent := Current;
    GetCommands(SaveCommands);
    TheTopView := P;
    P^.Options := P^.Options and not ofSelectable;
    P^.SetState(sfModal, True);
    SetCurrent(P, EnterSelect);
    if SaveOwner = nil then Insert(P);
    ExecView := P^.Execute;
    if SaveOwner = nil then Delete(P);
    SetCurrent(SaveCurrent, LeaveSelect);
    P^.SetState(sfModal, False);
    P^.Options := SaveOptions;
    TheTopView := SaveTopView;
    SetCommands(SaveCommands);
  end else ExecView := cmCancel;
End;

function TGGroup.First: PGView;
Begin
  If Last=nil
    Then First:=nil
    Else First:=Last^.GNext;
End;

function TGGroup.FirstMatch(AState: Word; AOptions: Word): PGView;

function Matches(P: PGView): Boolean; {$ifndef FPK}far;{$endif}
begin
  Matches := (P^.State and AState = AState) and
    (P^.Options and AOptions = AOptions);
end;

begin
  FirstMatch := FirstThatInMethod(@Matches, @Self);
end;

{$ifdef FPK}
function TGGroup.FirstThat(Test: Pointer): PGView;
var
  P, ALast: PGView;
begin
  if Last <> nil
  then begin
    ALast := Last;
    P := First;
    while (P <> ALast) do
    begin
      if Boolean(Chr(LongInt(CallPointerLocal(Test,
		 PreviousFramePointer, P))))
      then begin
	FirstThat := P;
	Exit
      end;
      P := P^.GNext
    end;
    if Boolean(Chr(LongInt(CallPointerLocal(Test,
		 PreviousFramePointer, P))))
    then begin
      FirstThat := P;
      Exit
    end
  end;
  FirstThat := nil
end;


function TGGroup.FirstThatInMethod(Test: Pointer; Obj: pointer): PGView;
var
  P, ALast: PGView;
begin
  if Last <> nil
  then begin
    ALast := Last;
    P := First;
    while (P <> ALast) do
    begin
      if Boolean(Chr(LongInt(CallPointerMethodLocal(Test,
		 PreviousFramePointer, Obj, P))))
      then begin
	FirstThatInMethod := P;
	Exit
      end;
      P := P^.GNext
    end;
    if Boolean(Chr(LongInt(CallPointerMethodLocal(Test,
		 PreviousFramePointer, Obj, P))))
    then begin
      FirstThatInMethod := P;
      Exit
    end
  end;
  FirstThatInMethod := nil
end;

{$else !FPK}

function TGGroup.FirstThat(Test: Pointer): PGView; Assembler;
var ALast: Pointer;
Asm
	LES     DI,Self
	LES     DI,ES:[DI].TGGroup.Last
        MOV     AX,ES
	OR      AX,DI
        JE      @@3
	MOV     WORD PTR ALast[2],ES
        MOV     WORD PTR ALast[0],DI
@@1:    LES     DI,ES:[DI].TGView.GNext
	PUSH    ES
	PUSH    DI
	PUSH    ES
	PUSH    DI
{$IFDEF Windows}
	MOV	AX,[BP]
	AND	AL,0FEH
	PUSH	AX
{$ELSE}
	PUSH    WORD PTR [BP]
{$ENDIF}
	CALL    Test
	POP     DI
	POP     ES
	OR      AL,AL
	JNE     @@2
	CMP     DI,WORD PTR ALast[0]
	JNE     @@1
	MOV     AX,ES
	CMP     AX,WORD PTR ALast[2]
	JNE     @@1
	XOR     DI,DI
	MOV     ES,DI
@@2:    MOV     SP,BP
@@3:    MOV     AX,DI
	MOV     DX,ES
End;

function TGGroup.FirstThatInMethod(Test: Pointer; Obj: pointer): PGView; Assembler;
var ALast: Pointer;
Asm
	LES     DI,Self
	LES     DI,ES:[DI].TGGroup.Last
        MOV     AX,ES
	OR      AX,DI
        JE      @@3
	MOV     WORD PTR ALast[2],ES
        MOV     WORD PTR ALast[0],DI
@@1:    LES     DI,ES:[DI].TGView.GNext
	PUSH    ES
	PUSH    DI
	PUSH    ES
	PUSH    DI
{$IFDEF Windows}
	MOV	AX,[BP]
	AND	AL,0FEH
	PUSH	AX
{$ELSE}
	PUSH    WORD PTR [BP]
{$ENDIF}
	CALL    Test
	POP     DI
	POP     ES
	OR      AL,AL
	JNE     @@2
	CMP     DI,WORD PTR ALast[0]
	JNE     @@1
	MOV     AX,ES
	CMP     AX,WORD PTR ALast[2]
	JNE     @@1
	XOR     DI,DI
	MOV     ES,DI
@@2:    MOV     SP,BP
@@3:    MOV     AX,DI
	MOV     DX,ES
End;

{$endif FPK}

function TGGroup.FindNext(Forwards: Boolean): PGView;
var P: PGView;
begin
  FindNext := nil;
  if Current <> nil then
  begin
    P := Current;
    repeat
      if Forwards then P := P^.GNext else P := P^.Prev;
    until ((P^.State and (sfVisible + sfDisabled) = sfVisible) and
      (P^.Options and ofSelectable <> 0)) or (P = Current);
    if P <> Current then FindNext := P;
  end;
end;

function TGGroup.FocusNext(Forwards: Boolean): Boolean;
var P: PGView;
begin
  P := FindNext(Forwards);
  FocusNext := True;
  if P <> nil then FocusNext := P^.Focus;
end;

{$ifdef FPK}
procedure TGGroup.ForEach(Action: Pointer);
var
  P, ALast: PGView;
begin
  if Last <> nil
  then begin
    ALast := Last;
    P := First;
    while (P <> ALast) do
    begin
      CallPointerLocal(Action, PreviousFramePointer, P);
      P := P^.GNext
    end;
    CallPointerLocal(Action, PreviousFramePointer, P);
  end
end;

procedure TGGroup.ForEachInMethod(Action: Pointer; Obj: pointer);
var
  P, ALast: PGView;
begin
  if Last <> nil
  then begin
    ALast := Last;
    P := First;
    while (P <> ALast) do
    begin
      CallPointerMethodLocal(Action, PreviousFramePointer, Obj, P);
      P := P^.GNext
    end;
    CallPointerMethodLocal(Action, PreviousFramePointer, Obj, P);
  end
end;

{$else !FPK}

procedure TGGroup.ForEach(Action: Pointer); assembler;
var ALast: Pointer;
Asm
	LES     DI,Self
	LES     DI,ES:[DI].TGGroup.Last
	MOV     AX,ES
	OR      AX,DI
	JE      @@4
	MOV     WORD PTR ALast[2],ES
	MOV     WORD PTR ALast[0],DI
	LES     DI,ES:[DI].TGView.GNext
@@1:    CMP     DI,WORD PTR ALast[0]
	JNE     @@2
	MOV     AX,ES
	CMP     AX,WORD PTR ALast[2]
	JE      @@3
@@2:    PUSH    WORD PTR ES:[DI].TGView.GNext[2]
	PUSH    WORD PTR ES:[DI].TGView.GNext[0]
	PUSH    ES
	PUSH    DI
{$IFDEF Windows}
	MOV	AX,[BP]
	AND	AL,0FEH
	PUSH	AX
{$ELSE}
	PUSH    WORD PTR [BP]
{$ENDIF}
	CALL    Action
	POP     DI
	POP     ES
	JMP     @@1
@@3:
{$IFDEF Windows}
	MOV	AX,[BP]
	AND	AL,0FEH
	PUSH	AX
{$ELSE}
	PUSH    WORD PTR [BP]
{$ENDIF}
	CALL    Action
@@4:
End;

procedure TGGroup.ForEachInMethod(Action: Pointer; Obj: pointer); assembler;
var ALast: Pointer;
Asm
	LES     DI,Self
	LES     DI,ES:[DI].TGGroup.Last
	MOV     AX,ES
	OR      AX,DI
	JE      @@4
	MOV     WORD PTR ALast[2],ES
	MOV     WORD PTR ALast[0],DI
	LES     DI,ES:[DI].TGView.GNext
@@1:    CMP     DI,WORD PTR ALast[0]
	JNE     @@2
	MOV     AX,ES
	CMP     AX,WORD PTR ALast[2]
	JE      @@3
@@2:    PUSH    WORD PTR ES:[DI].TGView.GNext[2]
	PUSH    WORD PTR ES:[DI].TGView.GNext[0]
	PUSH    ES
	PUSH    DI
{$IFDEF Windows}
	MOV	AX,[BP]
	AND	AL,0FEH
	PUSH	AX
{$ELSE}
	PUSH    WORD PTR [BP]
{$ENDIF}
	CALL    Action
	POP     DI
	POP     ES
	JMP     @@1
@@3:
{$IFDEF Windows}
	MOV	AX,[BP]
	AND	AL,0FEH
	PUSH	AX
{$ELSE}
	PUSH    WORD PTR [BP]
{$ENDIF}
	CALL    Action
@@4:
End;
{$endif FPK}

procedure TGGroup.GetData(var Rec);
type Bytes = array[0..65534] of Byte;

var I: Word;
    V: PGView;
Begin
  I := 0;
  if Last <> nil then
  begin
    V := Last;
    repeat
      V^.GetData(Bytes(Rec)[I]);
      Inc(I, V^.DataSize);
      V := V^.Prev;
    until V = Last;
  end;
End;

function TGGroup.GetHelpCtx: Word;
var Help: Word;
Begin
  Help := hcNoContext;
  If Current <> nil then Help := Current^.GetHelpCtx;
  If Help = hcNoContext then Help := TGView.GetHelpCtx;
  GetHelpCtx := Help;
End;

procedure TGGroup.GetSubViewPtr(var S: TStream; var P);
var Index: Word;
Begin
  S.Read(Index, SizeOf(Word));
  if Index > 0 then
    Pointer(P) := At(Index)
  else
    Pointer(P) := nil;
End;

procedure TGGroup.HandleEvent(var Event: TEvent);

 procedure DoHandleEvent(P: PGView); {$ifndef FPK}far;{$endif}
 begin
   if (P = nil) or ((P^.State and sfDisabled <> 0)
     and (Event.What and (PositionalEvents or FocusedEvents) <> 0)) then Exit;
   case Phase of
     phPreProcess: if P^.Options and ofPreProcess = 0 then Exit;
     phPostProcess: if P^.Options and ofPostProcess = 0 then Exit;
   end;
   if Event.What and P^.EventMask <> 0 then P^.HandleEvent(Event);
 end;

 function ContainsMouse(P: PGView): Boolean; {$ifndef FPK}far;{$endif}
 begin
   ContainsMouse := (P^.State and sfVisible <> 0) and
     (P^.DragMode and dmClickThru = 0) and
     P^.MouseInView(Event.Where);
 end;

{$ifdef Windows}
  function CheckWnd: Boolean;
  var
    P, W: PGView;
  begin
    W := GetView(Event.Wnd);
    P := W;
    while (P <> nil) and (P <> @Self) do
      P := P^.GOwner;
    If P <> nil		{ Target Wnd is a child }
    then begin
      Event.Wnd := 0;
      W^.HandleEvent(Event);
      CheckWnd := false;
    end
    else begin
      P := @Self;
      while (P <> nil) and (P <> W) do
	P := P^.GOwner;
      If P <> nil	{ Target Wnd is a parent }
      then begin
	Event.Wnd := 0;
	{HandleEvent(Event)}
        CheckWnd := true
      end;
    end;
  end;
{$endif Windows}

Begin
  {$ifdef Windows}
  If (Event.What and WindowedEvents <> 0) and (Event.Wnd <> 0)
  then {CheckWnd else} if not CheckWnd then Exit;
  {$endif Windows}
  If Event.What = evPositionalCtx
  then begin
    Phase := phFocused;
    DoHandleEvent(FirstThatInMethod(@ContainsMouse, @Self));
  end
  else begin
    TGView.HandleEvent(Event);
    If Event.What and FocusedEvents <> 0 then
    begin
      Phase := phPreProcess;
      ForEachInMethod(@DoHandleEvent, @Self);
      Phase := phFocused;
      DoHandleEvent(Current);
      Phase := phPostProcess;
      ForEachInMethod(@DoHandleEvent, @Self);
    end else
    begin
      Phase := phFocused;
      if (Event.What and PositionalEvents <> 0) then
	DoHandleEvent(FirstThatInMethod(@ContainsMouse, @Self)) else
	ForEachInMethod(@DoHandleEvent, @Self);
    end;
  end
End;

{$ifdef NOIASM}
function TGGroup.IndexOf(P: PGView): Integer;
var
  i: Integer;
  V: PGView;
begin
  V := First;
  i := 1;
  while (V <> P) and (V <> Last) do
  begin
    V := V^.GNext;
    Inc(i)
  end;
  if V = P
  then IndexOf := i
  else IndexOf := 0
end;
{$else}
function TGGroup.IndexOf(P: PGView): Integer; assembler;
asm
	LES     DI,Self
	LES     DI,ES:[DI].TGGroup.Last
	MOV     AX,ES
	OR      AX,DI
	JE      @@3
	MOV     CX,DI
	MOV     BX,ES
	XOR     AX,AX
@@1:    INC     AX
	LES     DI,ES:[DI].TGView.GNext
	MOV     DX,ES
	CMP     DI,P.Word[0]
	JNE     @@2
	CMP     DX,P.Word[2]
	JE      @@3
@@2:    CMP     DI,CX
	JNE     @@1
	CMP     DX,BX
	JNE     @@1
	XOR     AX,AX
@@3:
end;
{$endif FPK}

function TGGroup.Insert(P: PGView): pointer;
Begin
  Insert := InsertBefore(P, First);
End;

function TGGroup.InsertBefore(P, Target: PGView): pointer;
var
  SaveState: Word;
Begin
  if (P <> nil) and (P^.GOwner = nil) and
    ((Target = nil) or (Target^.GOwner = @Self)) then
  begin
    if P^.Options and ofCenterX <> 0 then
      P^.Origin.X := (Size.X - P^.Size.X) div 2;
    if P^.Options and ofCenterY <> 0 then
      P^.Origin.Y := (Size.Y - P^.Size.Y) div 2;
    SaveState := P^.State;
    P^.Hide;
    InsertView(P, Target);
    {$ifdef Windows}
    If Options and ofRooting <> 0
    then P^.SetState(sfRoot, true);
    {$endif Windows}
    if SaveState and sfVisible <> 0 then P^.Show;
    if State and sfActive <> 0 then
      P^.SetState(sfActive, True);
  end;
  InsertBefore := P
End;

procedure TGGroup.InsertView(P, Target: PGView);
Begin
  { for to remain foreground }
  If P^.Options and ofHoldFirst = 0 then
  If (Target <> nil) and (Target^.Options and ofHoldFirst <> 0) {and (Target^.GOwner <> nil) and
     (Target^.GOwner^.GOwner = nil)} then
    While (Target <> nil) and (Target^.Options and ofHoldFirst <>0) do
      Target := Target^.NextView;
  { -- end -- }
  P^.GOwner := @Self;
  P^.Owner := PGroup(@Self);
  if Target <> nil then
  begin
    Target := Target^.Prev;
    P^.GNext := Target^.GNext;
    Target^.GNext := P;
  end else
  begin
    if Last = nil then P^.GNext := P else
    begin
      P^.GNext := Last^.GNext;
      Last^.GNext := P;
    end;
    Last := P;
  end;
End;

procedure TGGroup.Lock;
Begin
  Inc (LockFlag);
End;

procedure TGGroup.PutSubViewPtr(var S: TStream; P: PGView);
var Index: Word;
Begin
  If P = nil then Index := 0
  else Index := IndexOf(P);
  S.Write(Index, SizeOf(Word));
End;

procedure TGGroup.ReDraw;
var
  AVis: PVis;
Begin
  AVis := GetVisibility;
  If AVis <> nil
  then DrawClipped(AVis, @Self);
End;

{$ifdef NOIASM}
procedure TGGroup.RemoveView(P: PGView);
var
  V, VP: PGView;
begin
  if Last <> nil
  then begin
    VP := Last;
    V := Last^.GNext;
    while (V <> P) and (V <> Last) do
    begin
      VP := V;
      V := V^.GNext;
    end;
    if V = P
    then begin
      VP^.GNext := V^.GNext;
      if V = Last
      then begin
	if V = VP
	then Last := nil
	else Last := VP
      end
    end
  end
end;
{$else}
procedure TGGroup.RemoveView(P: PGView); assembler;
asm
	PUSH    DS
	LDS     SI,Self
	LES     DI,P
	LDS     SI,DS:[SI].TGGroup.Last
	PUSH    BP
	MOV     AX,DS
	OR      AX,SI
	JE      @@7
	MOV     AX,SI
	MOV     DX,DS
	MOV     BP,ES
@@1:    MOV     BX,WORD PTR DS:[SI].TGView.GNext[0]
	MOV     CX,WORD PTR DS:[SI].TGView.GNext[2]
	CMP     CX,BP
	JE      @@5
@@2:    CMP     CX,DX
	JE      @@4
@@3:    MOV     SI,BX
	MOV     DS,CX
	JMP     @@1
@@4:    CMP     BX,AX
	JNE     @@3
	JMP     @@7
@@5:    CMP     BX,DI
	JNE     @@2
	MOV     BX,WORD PTR ES:[DI].TGView.GNext[0]
	MOV     CX,WORD PTR ES:[DI].TGView.GNext[2]
	MOV     DS:WORD PTR [SI].TGView.GNext[0],BX
	MOV     DS:WORD PTR [SI].TGView.GNext[2],CX
	CMP     DX,BP
	JNE     @@7
	CMP     AX,DI
	JNE     @@7
	CMP     CX,BP
	JNE     @@6
	CMP     BX,DI
	JNE     @@6
	XOR     SI,SI
	MOV     DS,SI
@@6:    POP     BP
	PUSH    BP
	LES     DI,Self
	MOV     WORD PTR ES:[DI].TGView.Last[0],SI
	MOV     WORD PTR ES:[DI].TGView.Last[2],DS
@@7:    POP     BP
	POP     DS
end;
{$endif}

procedure TGGroup.ResetCurrent;
begin
  SetCurrent(FirstMatch(sfVisible, ofSelectable), NormalSelect);
end;

procedure TGGroup.SelectNext(Forwards: Boolean);
var P: PGView;
Begin
  P := FindNext(Forwards);
  if P <> nil then P^.Select;
End;

procedure TGGroup.SetCurrent(P: PGView; Mode: SelectMode);

 procedure SelectView(P: PGView; Enable: Boolean);
 begin
   if P <> nil then P^.SetState(sfSelected, Enable);
 end;

 procedure FocusView(P: PGView; Enable: Boolean);
 begin
   if (State and sfFocused <> 0) and (P <> nil) then
     P^.SetState(sfFocused, Enable);
 end;

var OldCur: PGView;

Begin
  if Current <> P then
  begin
    {Lock;}
    OldCur := Current;
    FocusView(Current, False);
    if Mode <> EnterSelect then SelectView(Current, False);
    if Mode <> LeaveSelect then SelectView(P, True);
    FocusView(P, True);
    Current := P;
    {Unlock;}
  end;
End;

procedure TGGroup.SetData(var Rec);
type Bytes = array[0..65534] of Byte;

var I: Word;
    V: PGView;
Begin
  I := 0;
  if Last <> nil then
  begin
    V := Last;
    repeat
      V^.SetData(Bytes(Rec)[I]);
      Inc(I, V^.DataSize);
      V := V^.Prev;
    until V = Last;
  end;
End;

procedure TGGroup.SetState(AState: Word; Enable: Boolean);

 procedure DoSetState(P: PGView); {$ifndef FPK}far;{$endif}
 begin
   P^.SetState(AState, Enable);
 end;

 procedure DoExpose(P: PGView); {$ifndef FPK}far;{$endif}
 begin
   if P^.State and sfVisible <> 0 then P^.SetState(sfExposed, Enable);
 end;

begin
  TGView.SetState(AState, Enable);
  case AState of
    sfActive, sfDragging:
      begin
{        Lock;}
	ForEachInMethod(@DoSetState, @Self);
{	Unlock;}
      end;
    sfFocused:
      if Current <> nil then Current^.SetState(sfFocused, Enable);
    sfExposed:
      begin
	ForEachInMethod(@DoExpose, @Self);
           {if not Enable then FreeBuffer;}
      end;
  end;
End;

procedure TGGroup.Store(var S: TStream);
var Count: Integer;
    OwnerSave: PGGroup;

 procedure DoPut(P: PGView); {$ifndef FPK}far;{$endif}
 begin
{$ifdef FPK}
   S.Put(pobject(P));
{$else}
   S.Put(P);
{$endif}
 end;

Begin
  TGView.Store(S);
  S.Write (StandardFont, SizeOf (StandardFont));
  OwnerSave := OwnerGroup;
  OwnerGroup := @Self;
  Count := IndexOf(Last);
  S.Write(Count, SizeOf(Word));
  ForEachInMethod(@DoPut, @Self);
  PutSubViewPtr(S, Current);
  OwnerGroup := OwnerSave;
End;

procedure TGGroup.Unlock;
Begin
  If Lockflag <> 0 then Begin
    Dec (LockFlag);
    If LockFlag = 0 then DrawView;
  End;
End;

function TGGroup.Valid(Command: Word): Boolean;

 function IsInValid(P: PGView): Boolean; {$ifndef FPK}far;{$endif}
 Begin
   IsInValid := not P^.Valid(Command);
 End;

Begin
  Valid := True;
  If Command = cmReleasedFocus then Begin
    If (Current <> nil) and (Current^.Options and ofValidate <> 0) then
      Valid := Current^.Valid(Command);
  End
  Else if Command <> cmHeyYou
    then Valid := FirstThatInMethod(@IsInvalid, @Self) = nil;
End;

{**************************** TDragRect object ******************************}

constructor TDragRect.Init(var Bounds: TRect);
begin
  TGView.Init(Bounds);
  Options := Options and not ofBuffer;
  State := State or sfTransparent
end;

procedure TDragRect.Draw;
var I: Byte;
Begin
  SetColor(White);
  SetWriteMode(XORPut);
  For I := 0 to 1 do
    Rectangle(I, I, Size.X - I - 1, Size.Y - I - 1);
  SetWriteMode(NormalPut);
End;

procedure TDragRect.HideDrawClipped(Clip: PVis; VisOwner: PGView);
begin
  DrawClipped(Unraw(Clip), VisOwner)
  {inherited HideDrawClipped(Clip, VisOwner)}
end;

procedure TDragRect.Locate(var Bounds: TRect);
var
  R: TRect;
begin
  If GetState(sfTransparent)
  then inherited Locate(Bounds)
  else begin
    GetBounds(R);
    If not Bounds.Equals(R)
    then begin
      Hide;
      ChangeBounds(Bounds);
      Show
    end
  end
end;

{**************************** TWindow object *******************************}

Constructor TWindow.Init(var Bounds: TRect; ATitle: TTitleStr; ANumber: Integer);
Begin
  TGGroup.Init(Bounds);
  Title := NewStr(ATitle);
  Number := ANumber;
  Flags := wfMove + wfGrow + wfClose + wfZoom +
    wfShowNumber + wfShowTitle + wfBackground;
  Options := Options or (ofSelectable+ofTopSelect+ofTileable+ofFirstClick);
  GrowMode := gfGrowAll + gfGrowRel;
  GetBounds(ZoomRect);
  Palette := wpWhiteWindow;
  InitFrame;
  If Frame<>nil Then Insert (Frame);
End;

constructor TWindow.Load(var S: TStream);
Begin
  TGGroup.Load (S);
  S.Read(Flags, SizeOf(Word) + SizeOf(TRect) + 2 * SizeOf(Integer));
  GetSubViewPtr (S, Frame);
  Title:=S.ReadStr;
  S.Read (ScrBBck, SizeOf (ScrBBck));
  If ScrBBck<>nil then GetSubViewPtr (S, ScrBBck);
End;

destructor TWindow.Done;
Begin
  DisposeStr (Title);
  TGGroup.Done;
End;

function TWindow.AmLastWindow: Boolean;
var G: PGGroup;

 function Test(P: PGView): Boolean; {$ifndef FPK}far;{$endif}
 Begin
   Test := Message(P, evBroadCast, cmHeyYou, @Self)<>nil;
 End;

Begin
  G := GOwner;
  If G <> nil then Begin
    While G^.GOwner <> nil do G := G^.GOwner;
    AmLastWindow := G^.FirstThatInMethod(@Test, @Self)=nil;
  End
  Else AmLastWindow := true;
End;

procedure TWindow.Close;
Begin
  If Flags and wfHideClose <> 0
  then Hide
  else if Valid(cmClose) then Free
End;

{$ifdef Windows}
function TWindow.CreateRootWindow: Integer;
var
  st: LongInt;
begin
  st := ws_OverlappedWindow or ws_ClipSiblings or ws_ClipChildren;
  If Flags and wfShowTitle = 0
  then st := (st and not ws_SysMenu) or gvs_NoFrame
  else {Flags := Flags or wfRootFrame;} SetFlags(Flags or wfRootFrame);
  Wnd := CreateWindow(WindowClass, nil, st,
    0, 0, 100, 100, Main^.Wnd{0}, 0, hInstance, @Self);
  CreateRootWindow := sw_Show
end;
{$endif Windows}

procedure TWindow.GetClientRect(var R: TRect);
Begin
  If Frame = nil
  then GetExtent(R)
  else Frame^.GetClientRect(R)
End;

function TWindow.GetPalette: PPalette;
Const P: Array [wpWhiteWindow..wpCyanWindow] Of String [Length (CWhiteWindow)] =
	   (CWhiteWindow,CGrayWindow,CCyanWindow);
Begin
  GetPalette:=@P[Palette];
End;

function TWindow.GetTitle(MaxSize: Integer): TTitleStr;
var S: TTitleStr;
Begin
  If Title <> nil then Begin
    S:=Title^;
    If Length (S)>MaxSize then S:=Copy (S,0,MaxSize);
  End
  Else S := '';
  GetTitle := S;
End;

procedure TWindow.HandleEvent(var Event: TEvent);
var Limits: TRect;
    Min, Max: TPoint;
Begin
  {$ifdef Windows}
  if Event.What = evSystem then
    case PMSG(Event.InfoPtr)^.message of
      wm_Close:
        begin
	  If (Flags and wfClose <> 0)
          then Begin
	    If State and sfModal = 0
            then Close
	    else Begin
	      Event.What := evCommand;
	      Event.Command := cmCancel;
	      PutEvent(Event);
	      ClearEvent(Event);
	    End;
	  End;
	  ClearEvent(Event);
        end;
    end;
  {$endif Windows}
  TGGroup.HandleEvent(Event);
  if (Event.What = evCommand) then
    case Event.Command of
      cmResize:
	If Flags and (wfMove + wfGrow) <> 0 then Begin
          GOwner^.GetExtent(Limits);
          SizeLimits(Min, Max);
	  DragView(Event, DragMode or (Flags and (wfMove + wfGrow)),
            Limits, Min, Max, rmDnRi);
          ClearEvent(Event);
	End;
      cmClose:
	If (Flags and wfClose <> 0) and
	  ((Event.InfoPtr = nil) or (Event.InfoPtr = @Self)) then
	Begin
	  ClearEvent(Event);
	  If State and sfModal = 0 then Close
          Else Begin
            Event.What := evCommand;
            Event.Command := cmCancel;
            PutEvent(Event);
            ClearEvent(Event);
	  End;
	End;
      cmZoom:
	If (Flags and wfZoom <> 0) and
	  ((Event.InfoPtr = nil) or (Event.InfoPtr = @Self)) then
	begin
          Zoom;
          ClearEvent(Event);
        end;
    end
  else if Event.What = evKeyDown then
    case Event.KeyCode of
      kbTab:
	begin
	  FocusNext(False);
          ClearEvent(Event);
	end;
      kbShiftTab:
        begin
          FocusNext(True);
          ClearEvent(Event);
	end;
    end
  else if (Event.What = evBroadcast) and (Event.Command = cmSelectWindowNum)
	 and (Event.InfoInt = Number) and (Options and ofSelectable <> 0) then
  begin
    Select;
    ClearEvent(Event);
  end
  else If (Event.What = evBroadCast) and (Event.Command = cmHeyYou) and
          (Event.InfoPtr <> @Self) and GetState(sfVisible) then ClearEvent(Event);
End;

Procedure TWindow.InitFrame;
Var R:TRect;
Begin
  GetExtent (R);
  Frame:=New (PFrame, Init (R));
End;

procedure TWindow.SetFlags(AFlags: Word);
var
  OldClientRect, ClientRect, Bounds: TRect;
  Delta: TPoint;

  procedure DoMove(P: PGView); {$ifndef FPK}far;{$endif}
  begin
    if P <> PGView(Frame)
    then P^.MoveTo(P^.Origin.x + Delta.x, P^.Origin.y + Delta.y);
  end;

begin
  GetClientRect(OldClientRect);
  Flags := AFlags;
  GetClientRect(ClientRect);
  if not ClientRect.Equals(OldClientRect)
  then begin
    { Change the bounds and the position of the subviews
      such that the subview bounds and the client rect remain
      MakeGlobal()-invariant.
      Note that this operation is a hack, used for
      compatibility reasons only. }
    Delta.x := ClientRect.A.x - OldClientRect.A.x;
    Delta.y := ClientRect.A.y - OldClientRect.A.y;
    ForEachInMethod(@DoMove, @Self);
    GetBounds(Bounds);
    Inc(Bounds.A.x, OldClientRect.A.x - ClientRect.A.x);
    Inc(Bounds.A.y, OldClientRect.A.y - ClientRect.A.y);
    Inc(Bounds.B.x, OldClientRect.B.x - ClientRect.B.x);
    Inc(Bounds.B.y, OldClientRect.B.y - ClientRect.B.y);
    SetBounds(Bounds);
    GetExtent(Bounds);
    if Frame <> nil then Frame^.Locate(Bounds);
  end;
  If Frame <> nil then Frame^.DrawView;
end;

procedure TWindow.SetState(AState: Word; Enable: Boolean);
var WindowCommands: TCommandSet;
Begin
  If AState = sfVisible then Begin
    If Enable then Number := GetWindowNumber(Number)
	      else FreeWindowNumber(Number);
  End;

  TGGroup.SetState(AState, Enable);
  If AState = sfSelected then
    SetState(sfActive, Enable);
  If ((AState = sfSelected) or ((AState = sfExposed) and
    (State and sfSelected <> 0))){ and AmLastWindow} then { ge„ndert Mi. !!!}
  Begin
    WindowCommands := WindowCmds;
    if Flags and wfGrow + wfMove <> 0 then
      WindowCommands := WindowCommands + [Byte(cmResize)];
    if Flags and wfClose <> 0 then
      WindowCommands := WindowCommands + [Byte(cmClose)];
    if Flags and wfZoom <> 0 then
      WindowCommands := WindowCommands + [Byte(cmZoom)];
    if Enable then EnableCommands(WindowCommands)
    else DisableCommands(WindowCommands);
  end;
End;

procedure TWindow.SetTitle(ATitle: string);
begin
  if Title <> nil
  then DisposeStr(Title);
  Title := NewStr(ATitle);
  if Frame <> nil then Frame^.DrawView;
  {$ifdef Windows}
  If Wnd <> 0 then UpdateWinTitle;
  {$endif Windows}
end;

procedure TWindow.SizeLimits(var Min, Max: TPoint);
Begin
  {TGView.SizeLimits (Min,Max);}
  TView.SizeLimits (Min,Max);
  Min := GMinWinSize;
End;

function TWindow.StandardScrollBar(AOptions: Word): PScrollBar;
var R: TRect;
    S: PScrollBar;
Begin
  If ScrBBck=nil then Begin
    R.Assign (Size.X-20, Size.Y-20, Size.X-4, Size.Y-4);
    ScrBBck:=New (PScrollbarBack, Init (R));
    Insert (ScrBBck);
    ScrBBck^.GrowMode:=gfGrowAll;
  End;
  GetExtent(R);
  If AOptions and sbVertical = 0 then
    R.Assign (R.A.X+3, R.B.Y-21, R.B.X-20, R.B.Y-3)
  else R.Assign (R.B.X-21, R.A.Y+22, R.B.X-3, R.B.Y-20);
  S := New (PScrollBar, Init(R));
  Insert (S);
  S^.Flags := AOptions;
  If AOptions and sbHandleKeyboard <> 0
  then S^.Options := S^.Options or ofPostProcess;
  StandardScrollBar := S;
End;

procedure TWindow.Store(var S: TStream);
Begin
  TGGroup.Store (S);
  S.Write(Flags, SizeOf(Word) + SizeOf(TRect) + 2 * SizeOf(Integer));
  PutSubViewPtr (S, Frame);
  S.WriteStr (Title);
  S.Write (ScrBBck, SizeOf (ScrBBck));
  If ScrBBck<>nil then PutSubViewPtr (S, ScrBBck);
End;

{$ifdef Windows}
procedure TWindow.UpdateWinTitle;
var
  S: string;
begin
  S := GetTitle(254);
  S[Length(S)+1] := #0;
  Inc(S[0]);
  if not AnsiCode
  then OemToAnsi(@S[1], @S[1]);
  SetWindowText(Wnd, @S[1]);
end;
{$endif Windows}

function TWindow.Valid(Command: Word): Boolean;
begin
  Valid := (Command <> cmHeyYou) and inherited Valid(Command)
end;

procedure TWindow.Zoom;
var R: TRect;
    Max, Min: TPoint;
Begin
  SizeLimits(Min, Max);
  If (Size.X <> Max.X) or (Size.Y <> Max.Y) then Begin
    GetBounds(ZoomRect);
    R.A.X := 0; R.A.Y := 0;
    R.B := Max;
    Locate(R);
  End
  Else Locate(ZoomRect);
End;

{**** Visibility detection and drawing support (formerly known as GVisible)
}

{ Primitives ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

function Intersect(var Dest, Source: Objects.TRect): Boolean;
{ Returns true iff Dest and Source intersect.
  Dest is the intersection rectangle iff there is one.
  & faster implementation &
}
begin
  Dest.Intersect(Source);
  Intersect := not Dest.Empty
end;

function DoesIntersect(Dest: Objects.TRect; var Source: Objects.TRect): Boolean;
{ Returns true iff Dest and Source intersect.
  & faster implementation &
}
begin
  DoesIntersect := Intersect(Dest, Source)
end;

{ TViewCollection object ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

type
  PViewCollection = ^TViewCollection;
  TViewCollection = object(TCollection)
    constructor CopyOf(C: PCollection);
    constructor Join(C1, C2: PCollection);
    procedure FreeItem(Item: pointer); virtual;
  end;

constructor TViewCollection.CopyOf(C: PCollection);
begin
  TObject.Init;
  {If C = nil
  then begin
    Fail;
    Exit
  end; } {This is now separately handled.}
  Count := C^.Count;
  Limit := C^.Limit;
  Delta := C^.Delta;
  GetMem(Items, Limit * SizeOf(pointer));
  Move(C^.Items^, Items^, Limit * SizeOf(pointer))
end;

constructor TViewCollection.Join(C1, C2: PCollection);

	procedure Ins(C2Item: pointer); {$ifndef FPK}far;{$endif}

		function Matches(C1Item: pointer): Boolean; {$ifndef FPK}far;{$endif}
		begin
		  Matches := C1Item = C2Item
		end;

	begin
{$ifdef _FPK}	  
	  If C1^.FirstThatInMethod(@Matches, @Self) = nil
	  then Insert(C2Item)
{$else}	  
	  If C1^.FirstThat(@Matches) = nil
	  then Insert(C2Item)
{$endif}	   
	end;

begin
{$ifndef _FPK}
  if C1 = nil then CopyOf(C2) else
  if C2 = nil then CopyOf(C1)
  else begin
    CopyOf(C1);
    {$ifdef _FPK}
    C2^.ForEachInMethod(@Ins, @Self)
    {$else}
    C2^.ForEach(@Ins)
    {$endif}
  end
{$endif _FPK}
end;

procedure TViewCollection.FreeItem(Item: pointer);
begin
end;

{ Data structure
}
{$ifdef Windows}
type
  PRectVis = ^TRectVis;
  TRectVis = record
    Region: HRgn;
    CurTrans: Integer;
    Trans: PViewCollection;
    DrawMode: Word;
  end;
{$else}
const
  maxRect = 16;

type
  PVRect = ^TVRect;
  TVRect = record
    Count: Integer;
    Next: PVRect;
    R: array[0..maxRect-1] of TRect;
  end;
  PPVRect = ^PVRect;

type
  PRectVis = ^TRectVis;
  TRectVis = record
    TheBounds: TRect;
    List: PVRect;
    CurTrans: Integer;
    Trans: PViewCollection;
    case DrawMode: Word of
      1: (VgaBuf: TVgaBuf);
      2: (MetaBuf: pointer);
      3: ();
  end;
{$endif}

function CopyViewCollection(P: PViewCollection): PViewCollection;
begin
  if P = nil
  then CopyViewCollection := nil
  else CopyViewCollection := New(PViewCollection, CopyOf(P));
end;

function JoinViewCollections(C1, C2: PViewCollection): PViewCollection;
begin
  If (C1 = nil) and (C2 = nil)
  then JoinViewCollections := nil
  else JoinViewCollections := New(PViewCollection, Join(C1, C2))
end;

function RawCopyVis(Vis: PVis): PVis; forward;

function CopyVis(Vis: PVis): PVis;
var
  Copy: PRectVis;
begin
  CopyVis := nil;
  If Vis = nil then Exit;
  Copy := RawCopyVis(Vis);
  Copy^.CurTrans := PRectVis(Vis)^.CurTrans;
  Copy^.Trans := CopyViewCollection((PRectVis(Vis)^.Trans));
  CopyVis := Copy
end;

{ Transparency support ----------------------------------------------------
}

procedure IndirectRegion(Vis: PVis; var Bounds: Objects.TRect; Transparent: PGView);
begin
  If (Vis = nil) {$ifndef Windows}
     or not DoesIntersect(PRectVis(Vis)^.TheBounds, Bounds) {$endif}
  then Exit;
  with PRectVis(Vis)^ do
  begin
    If CurTrans < 0 then Exit;          { Raw mode }
    If Transparent = nil
    then begin
      If Trans <> nil
      then CurTrans := Trans^.Count
    end
    else begin
      If Trans = nil
      then begin
	Trans := New(PViewCollection, Init(2, 8));
	CurTrans := 0
      end;
      Trans^.AtInsert(CurTrans, Transparent)
    end
  end
end;

procedure Unindirect(Vis: PVis; GOwner: PGView);
var
  i: Integer;
begin
  If Vis <> nil then
  with PRectVis(Vis)^ do
  begin
    If Trans <> nil then
    with Trans^ do
    begin
      For i := Trans^.Count - 1 downto 0 do
	if PGView(PGView(Trans^.At(i))^.GOwner) = GOwner
	then Trans^.AtFree(i);
      If CurTrans > Trans^.Count
      then CurTrans := Trans^.Count;
    end
  end
end;

function Unraw(Vis: PVis): PVis;
begin
  If Vis <> nil then
  with PRectVis(Vis)^ do
  begin
    DrawMode := $FFFF;
  end;
  Unraw := Vis
end;

function HasTrans(Vis: PVis): Boolean;

	function IsTrans(View: PGView): Boolean; {$ifndef FPK}far;{$endif}
	begin
	  IsTrans := View^.State and sfTransparent <> 0
	end;

begin
{$ifdef _FPK}
  HasTrans := (Vis <> nil) and (PRectVis(Vis)^.Trans <> nil);
{$else}
  HasTrans := (Vis <> nil) and (PRectVis(Vis)^.Trans <> nil) and
    (PRectVis(Vis)^.Trans^.FirstThat(@IsTrans) <> nil)
{$endif}
end;

function CondRawCopyVis(Vis: PVis): PVis;
var
  Copy: PVis;
begin
  Copy := RawCopyVis(Vis);
  If PRectVis(Vis)^.DrawMode = $FFFF  { indicates UnRaw }
  then PRectVis(Copy)^.CurTrans := 0;
  CondRawCopyVis := Copy
end;

procedure HideTrans(Vis: PVis; This: PGView);
var
  i: Integer;
  P: PGView;
begin
  If Vis = nil then Exit;
  with PRectVis(Vis)^ do
  begin
    If Trans <> nil then
      with Trans^ do
	For i := Trans^.Count - 1 downto 0 do
	begin
	  P := PGView(Trans^.At(i));
	  {with P^ do}
	    If P^.State and sfTransparent <> 0
	    then P^.HideDrawClipped(CondRawCopyVis(Vis), This)
	end;
  end;
end;

procedure ShowTrans(Vis: PVis; This: PGView);
var
  i: Integer;
  P: PGView;
begin
  If Vis = nil then Exit;
  with PRectVis(Vis)^ do
  begin
    If Trans <> nil then
      with Trans^ do
	For i := 0 to Trans^.Count - 1 do
	begin
	  P := PGView(Trans^.At(i));
	  {with P^ do
	  begin}
	    If P^.State and sfTransparent <> 0
	    then P^.DrawClipped(RawCopyVis(Vis), P);
	    P^.FreeBack
	  {end}
	end
  end;
end;

procedure FreeBacks(Vis: PVis; This: PGView);
var
  i: Integer;
  P: PGView;
begin
  If Vis = nil then Exit;
  with PRectVis(Vis)^ do
  begin
    If Trans <> nil then
      with Trans^ do
	For i := 0 to Trans^.Count - 1 do
	begin
	  P := PGView(Trans^.At(i));
	  {with P^ do}
	    P^.FreeBack;
	end
  end;
end;

procedure DrawTransBack(Vis: PVis; This: PGView);
{ Draw Trans' background
}
	procedure SetVis(Trans: PCollection; Enable: Boolean);
	var
	  i: Integer;
	  P: PGView;
	begin
	  with Trans^ do
	    For i := 0 to Count - 1 do
	    begin
	      P := PGView(At(i));
	      with P^ do
		If P^.State and sfTransparent <> 0 then
		if Enable
		then P^.State := P^.State or sfVisible
		else P^.State := P^.State and not sfVisible
	    end
	end;

begin
  If Vis = nil then Exit;
  with PRectVis(Vis)^ do
    If Trans <> nil
    then begin
      SetVis(Trans, false);
      if This^.GOwner <> nil then {&}
	DrawFirstPass(This^.GOwner, This);
      SetVis(Trans, true)
    end
end;

procedure DrawTrans(Vis: PVis; This: PGView);
{ Draw Trans by first-pass
}
var
  i: Integer;
  P: PGView;
begin
  If Vis = nil then Exit;
  with PRectVis(Vis)^ do
  begin
    If Trans <> nil then
      with Trans^ do
	For i := 0 to Trans^.Count - 1 do
	begin
	  P := PGView(Trans^.At(i));
	  with P^ do
	  begin
	    If P^.State and sfTransparent <> 0
	    then DrawFirstPass(P, This);
	    P^.FreeBack
	  end
	end
  end;
end;

{$ifdef Windows}
{$i gviswin.pas}
{$else}
{$i gvisrect.pas}
{$endif !Windows}

{ ------------------------------------------------------------------------
}

procedure DrawVisLocal(Vis: PVis; Origin: Objects.TPoint; This: PGView;
  Local: pointer; Frame: FramePointer);
var
  P: TPoint;
  Modi: Boolean;

 procedure CallDraw; {$ifndef FPK}near;{$endif}
 Begin
   If Local = nil
     then This^.Draw
{$ifdef NOIASM}
     else CallVoidMethodLocal(Local, Frame, This)
{$else}
     else asm
	push	bp
	mov	bp, [bp+4]		{ DrawVisLocal's frame }
{$IFDEF Windows}
	MOV	AX,[Frame]
	AND	AL,0FEH
	PUSH	AX
{$ELSE}
	push    [Frame]
{$ENDIF}
	push	cs			{ push return address }
	push	OFFSET @@1
	push	WORD PTR Local[2]	{ push local proc address }
	push	WORD PTR Local[0]
	retf				{ jump to local proc }
@@1:	pop	bp
    end
{$endif NOIASM}
 End;

 procedure SimpleDraw(var Rect: Objects.TRect); {$ifndef FPK}near;{$endif}
 Begin
   SetClipRectR(Rect);
   MetaClipRect := ClipRect;
   with Rect do SetCriticalArea(A.x, A.y, B.x, B.y);
   CallDraw
 End;

Begin
  If Vis = nil then Exit;
  This^.ViewLock := 0;
  Modi := Local <> nil;
  If BeginDraw(Vis, Origin, This, Modi)
  then Begin
    CallDraw;
    This^.ViewLock := 1;
    If EndDraw(Vis, This) then Exit
  End;
{$ifndef Windows}
  SetDrawOriginP(Origin);
  This^.ViewLock := 1;
  HideMouse;
  ForEachRect(PRectVis(Vis)^.List, @SimpleDraw);
  ShowMouse;
  ShowTrans(Vis, This);
  This^.ViewLock := 0
{$endif}
End;

procedure DrawVis(Vis: PVis; Origin: Objects.TPoint; This: PGView);
Begin
  DrawVisLocal(Vis, Origin, This, nil, FramePointer(0))
End;

{ Following variables are actually DrawFirstPass-local }
const
  theSelf: PGView = nil;
var
  GroupDrawOrigin: Objects.TPoint;
  GroupMetaClipRect: Objects.TRect;

procedure DrawFirstPass(P: PGView; This: PGView);
var
  SaveDrawOrigin: Objects.TPoint;
  SaveClipRect: Objects.TRect;
  SaveMetaClipRect: Objects.TRect;
  ViewBounds: Objects.TRect;
  Delta: Objects.TPoint;
begin
  SaveDrawOrigin := DrawOrigin;
  SaveMetaClipRect := MetaClipRect;
  SaveClipRect := ClipRect;
  If This = nil
  then begin
    This := theSelf;
    DrawOrigin := GroupDrawOrigin;
    MetaClipRect := GroupMetaClipRect
  end
  else begin
    theSelf := This;
    GroupDrawOrigin := DrawOrigin;
    GroupMetaClipRect := MetaClipRect;
  end;
  P^.GetBounds(ViewBounds);
  If PGView(P^.GOwner) <> This
  then begin
    Delta.X := 0; Delta.Y := 0;
    This^.MakeSemiGlobal(Delta, Delta);
    Delta.X := -Delta.X;
    Delta.Y := -Delta.Y;
    If P^.GOwner <> nil
    then P^.GOwner^.MakeSemiGlobal(Delta, Delta);
    ViewBounds.Move(Delta.X, Delta.Y)
  end;
  If This^.SetSubRect(ViewBounds)
  then begin
    ViewBounds.Move(GroupDrawOrigin.X, GroupDrawOrigin.Y);
    SetMetaClipRectR(ClipRect {ViewBounds});
    SetDrawOriginP(ViewBounds.A);
    with P^ do
    begin
      Inc(ViewLock); { disable old-style SetViewPort calls }
      Draw;
      Dec(ViewLock);
    end;
    SetDrawOriginP(SaveDrawOrigin);
    SetMetaClipRectR(SaveMetaClipRect);
    SetClipRectR(SaveClipRect)
  end
end;

procedure CritUnionDelta(Delta: Objects.TPoint);
var
  R: Objects.TRect;
Begin
  R := CriticalArea;
  If Delta.x > 0 then Dec(R.A.x, Delta.x) else Dec(R.B.x, Delta.x);
  If Delta.y > 0 then Dec(R.A.y, Delta.y) else Dec(R.B.y, Delta.y);
  SetCriticalArea(R.A.x, R.A.y, R.B.x, R.B.y);
End;

procedure ScrollView(This: PGView; Delta: Objects.TPoint; SubRect: PRect);
begin
  This^.BeginScroll(Delta, SubRect);
  This^.EndScroll
end;

procedure ScrollGroup(This: PGGroup; Delta: Objects.TPoint);
var
  P: Objects.TPoint;
  avis, ivis: PVis;
  Opt: Word;

{$ifdef FPK}
 procedure DoCopy(ObjFake: pointer);
 { DrawVisLocal expects a method-local function.}
{$else}
 procedure DoCopy; {$ifndef FPK}far;{$endif}
{$endif}
 Begin
   CritUnionDelta(Delta);
   CopyScreen(DrawOrigin.X, DrawOrigin.Y,
	      This^.Size.X + DrawOrigin.X, This^.Size.Y + DrawOrigin.Y,
	      Delta.X + DrawOrigin.X, Delta.Y + DrawOrigin.Y)
 End;

Begin
  P.X := 0; P.Y := 0;
  This^.MakeSemiGlobal(P, P);
  avis := This^.GetVisibility;
  If GetVgaMemCaps and vmcCopy <> 0
  then begin
    ivis := IntersectDelta(avis, Delta);
    SubtractHiddenSources(ivis, Delta, This);
    Opt := This^.Options;
    This^.Options := Opt and not (ofBuffer + ofMetafile); { CopyScreen is not bufferable }
    DrawVisLocal(ivis, P, This, @DoCopy, CurBP);	{ copy region }
    This^.Options := Opt;
    SubtractVis(avis, ivis);
    FreeVis(ivis);
  end;
  This^.DrawClipped(avis, This); { draw region }
End;

{*********************** GVViews registration procedure *********************}

procedure RegisterGVViews;
Begin
  {RegisterType (RView);}
  RegisterType (RGView);
  RegisterType (RFrame);
  RegisterType (RBackground);
  RegisterType (RScrollBar);
  RegisterType (RScroller);
  RegisterType (RListViewer);
  RegisterType (RGGroup);
  RegisterType (RWindow);
End;

Begin
  PositionalEvents := PositionalEvents or evPositionalCtx;
{$ifndef FPK}
{$ifndef Windows}
  WinRes.SetHelpCtxDelta(hc)
{$endif Windows}
{$endif FPK}
End.

