unit GvTv;

{ Graphics Vision 2.10, Turbo Vision Gate,
  Copr. 1995,1996 Matthias K”ppe

  Portions of this code are from views.pas, which is
  copr. 1992 Borland Int., Inc.
}

{$G+,X+}

interface

uses Objects, Drivers, MyMouse, Views, GvViews, App;

const
  evDown = $8000;

type
{ pointer types
}
  PTGate = ^TTGate;
  PGGate = ^TGGate;

{ object types
}
  TTGate = object(TGroup)
    GGate: PGGate;
    constructor Init(var Bounds: {text}TRect; view: PView);
    procedure ChangeBounds(var Bounds: TRect); virtual;
    function Execute: Word; virtual;
    procedure GetEvent(var Event: TEvent); virtual;
    function GetPalette: PPalette; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure PutEvent(var Event: TEvent); virtual;
  end;

  TGGate = object(TGView)
    TGate: PTGate;
    SavedWhere: TPoint;
    constructor Init(var Bounds: {grfx}TRect; ATGate: PTGate);
    destructor Done; virtual;
    procedure ChangeBounds(var Bounds: TRect); virtual;
    procedure Draw; virtual;
    procedure DrawBuf(var Clip: TRect; var Buf);
    function Execute: Word; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure SetState(AState: Word; Enable: Boolean); virtual;
    procedure TransEvent(var Event: TEvent; Down: Boolean);
    function Valid(Command: Word): Boolean; virtual;
  end;

{ Up-link objects
}
  PUpProgram = ^TUpProgram;
  TUpProgram = object(App.TProgram)
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure InitDesktop; virtual;
    procedure Idle; virtual;
  end;

  PUpDesktop = ^TUpDesktop;
  TUpDesktop = object(App.TDesktop)
    procedure Draw; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
  end;

  PUpScrollbar = ^TUpScrollbar;
  TUpScrollbar = object(Views.TScrollbar)
    Scrollbar: PScrollbar;
    constructor Init(AScrollbar: PScrollbar);
    procedure HandleEvent(var Event: TEvent); virtual;
  end;

{ service routines
}
function NewGGate(View: PView): PGGate;
function FreeGGate(GGate: PGGate): PView;

const
  CharSize: TPoint = (X: 8; Y: 14);

procedure SetGateScreenSize(X, Y: Integer);
procedure UseGvPal;
procedure UpdatePal;
procedure UpdateSizes;

var
  AppPalette: TPalette;

implementation	{ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

uses
  MsgBox,
{$ifdef DPMI}
  WinApi,
{$endif}
  Gr, ExtGraph, MyFonts, MetaGr,
  GvApp, GvMsgBox;

{ WriteView patch ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

function FindPos(Start, Stop: PChar; str: string): PChar;
var
  obj: string;
  i: Byte;
begin
  obj[0] := chr(Stop-Start);
  Move(Start^, obj[1], Length(obj));
  i := Pos(str, obj);
  If i = 0
  then Findpos := nil
  else FindPos := Start + (i-1)
end;

var
  gwClip: TRect;
  gwDraw: Boolean;
  gwPtr: pointer;

function GGateOf(P: PView): PGGate;
begin
  while (P <> nil) and (TypeOf(P^) <> TypeOf(TTGate)) do
    P := P^.Owner;
  If P <> nil
  then GGateOf := PTGate(P)^.GGate
  else GGateOf := nil
end;

procedure WriteGraph(P: PView); near;
var
  GGate: PGGate;
begin
  GGate := GGateOf(P);
  If GGate <> nil then
    GGate^.DrawBuf(gwClip, gwPtr^)
end;

{ Write to view                                         }
{ In    AX    = Y coordinate                            }
{       BX    = X coordinate                            }
{       CX    = Count                                   }
{       ES:DI = Buffer Pointer                          }

procedure WriteView; near; assembler;
const
  Self   =   6;
  Target =  -4;
  Buffer =  -8;
  BufOfs = -10;
asm
	mov	gwDraw, 0
	MOV     [BP].BufOfs,BX
	MOV     [BP].Buffer[0],DI
	MOV     [BP].Buffer[2],ES
	ADD     CX,BX
	XOR     DX,DX
	LES     DI,[BP].Self
	OR      AX,AX
	JL      @@3
	CMP     AX,ES:[DI].TView.Size.Y
	JGE     @@3
	OR      BX,BX
	JGE     @@1
	XOR     BX,BX
@@1:    CMP     CX,ES:[DI].TView.Size.X
	JLE     @@2
	MOV     CX,ES:[DI].TView.Size.X
@@2:    CMP     BX,CX
	JL      @@10
@@3:    RET
@@10:   TEST    ES:[DI].TView.State,sfVisible
	JE      @@3
	CMP     ES:[DI].TView.Owner.Word[2],0
	JE      @@3
	MOV     [BP].Target[0],DI
	MOV     [BP].Target[2],ES
	ADD     AX,ES:[DI].TView.Origin.Y
	MOV     SI,ES:[DI].TView.Origin.X
	ADD     BX,SI
	ADD     CX,SI
	ADD     [BP].BufOfs,SI
	LES     DI,ES:[DI].TView.Owner
	CMP     AX,ES:[DI].TGroup.EndState.2.TRect.A.Y	{ Clip is EndState.2 }
	JL      @@3
	CMP     AX,ES:[DI].TGroup.EndState.2.TRect.B.Y
	JGE     @@3
	CMP     BX,ES:[DI].TGroup.EndState.2.TRect.A.X
	JGE     @@11
	MOV     BX,ES:[DI].TGroup.EndState.2.TRect.A.X
@@11:   CMP     CX,ES:[DI].TGroup.EndState.2.TRect.B.X
	JLE     @@12
	MOV     CX,ES:[DI].TGroup.EndState.2.TRect.B.X
@@12:   CMP     BX,CX
	JGE     @@3
	LES     DI,ES:[DI].TGroup.Last
@@20:   LES     DI,ES:[DI].TView.Next
	CMP     DI,[BP].Target[0]
	JNE     @@21
	MOV     SI,ES
	CMP     SI,[BP].Target[2]
	JNE     @@21
	JMP     @@40
@@21:   TEST    ES:[DI].TView.State,sfVisible
	JE      @@20
	MOV     SI,ES:[DI].TView.Origin.Y
	CMP     AX,SI
	JL      @@20
	ADD     SI,ES:[DI].TView.Size.Y
	CMP     AX,SI
	JL      @@23
	TEST    ES:[DI].TView.State,sfShadow
	JE      @@20
	ADD     SI,ShadowSize.Y
	CMP     AX,SI
	JGE     @@20
	MOV     SI,ES:[DI].TView.Origin.X
	ADD     SI,ShadowSize.X
	CMP     BX,SI
	JGE     @@22
	CMP     CX,SI
        JLE     @@20
	CALL    @@30
@@22:   ADD     SI,ES:[DI].TView.Size.X
	JMP     @@26
@@23:   MOV     SI,ES:[DI].TView.Origin.X
        CMP     BX,SI
	JGE     @@24
	CMP     CX,SI
	JLE     @@20
	CALL    @@30
@@24:   ADD     SI,ES:[DI].TView.Size.X
	CMP     BX,SI
	JGE     @@25
	CMP     CX,SI
	JLE     @@31
	MOV     BX,SI
@@25:   TEST    ES:[DI].TView.State,sfShadow
	JE      @@20
	PUSH    SI
	MOV     SI,ES:[DI].TView.Origin.Y
        ADD     SI,ShadowSize.Y
	CMP     AX,SI
	POP     SI
	JL      @@27
	ADD     SI,ShadowSize.X
@@26:   CMP     BX,SI
	JGE     @@27
	INC     DX
	CMP     CX,SI
	JLE     @@27
	CALL    @@30
	DEC     DX
@@27:   JMP     @@20
@@30:   PUSH    [BP].Target.Word[2]
	PUSH    [BP].Target.Word[0]
	PUSH    [BP].BufOfs.Word[0]
	PUSH    ES
	PUSH    DI
	PUSH    SI
	PUSH    DX
        PUSH    CX
	PUSH    AX
	MOV     CX,SI
	CALL    @@20
	POP     AX
	POP     CX
	POP     DX
	POP     SI
	POP     DI
	POP     ES
	POP     [BP].BufOfs.Word[0]
	POP     [BP].Target.Word[0]
	POP     [BP].Target.Word[2]
	MOV     BX,SI
@@31:   RET
@@40:   LES     DI,ES:[DI].TView.Owner
	MOV     SI,ES:[DI].TGroup.Buffer.Word[2]
	OR      SI,SI
	JE      @@44
	CMP     SI,ScreenBuffer.Word[2]
	JE      @@41
	CALL    @@50
	JMP     @@44
@@41:
	{ write (one line) to ScreenBuffer >> }

	mov	gwClip.A.X, bx
	mov     gwClip.A.Y, ax
	mov	gwClip.B.X, cx
	mov	gwClip.B.Y, ax
	inc	gwClip.B.Y
	mov	gwDraw, 1
	CALL    @@50

	{ << write to ScreenBuffer }
@@44:
	CMP     ES:[DI].TGroup.EndState.10.Byte,0
					{ LockFlag is EndState.10.Byte }
	JNE     @@31
	JMP     @@10

@@50:   PUSH    ES
	PUSH    DS
	PUSH    DI
	PUSH    CX
	PUSH    AX
	MUL     ES:[DI].TView.Size.X.Byte[0]
	ADD     AX,BX
	SHL     AX,1
	ADD     AX,ES:[DI].TGroup.Buffer.Word[0]
	MOV     DI,AX
	MOV     ES,SI
	mov	gwPtr.Word[0], di
	mov	gwPtr.Word[2], es

	MOV     AH,ShadowAttr
	SUB     CX,BX
	MOV     SI,BX
	SUB     SI,[BP].BufOfs
	SHL     SI,1
	ADD     SI,[BP].Buffer.Word[0]
	MOV     DS,[BP].Buffer.Word[2]
	CLD
	OR      DX,DX
	JNE     @@52
	REP     MOVSW
	JMP     @@70

@@52:   LODSB
	INC     SI
	STOSW
	LOOP    @@52

@@70:
	push	bx
	push	dx
	push	es

	mov	ax, SEG gwPtr
	mov	ds, ax
	cmp	gwDraw, 0
	jz	@@80
	push	[bp].Self.Word[2]
	push	[bp].Self.Word[0]
	call    WriteGraph
	mov	gwDraw, 0
@@80:
	pop	si
	pop	dx
	pop	bx
	POP     AX
	POP     CX
	POP     DI
	POP     DS
	POP     ES
	RET
end;

procedure LockGraph(P: PView); near;
var
  GGate: PGGate;
begin
  GGate := GGateOf(P);
  If GGate <> nil then
    GGate^.SetViewport
end;

procedure UnlockGraph(P: PView); near;
var
  GGate: PGGate;
begin
  GGate := GGateOf(P);
  If GGate <> nil then
    GGate^.RestoreViewport
end;

procedure GWriteView; far; assembler;
asm
	call	WriteView
end;

procedure PatchWriteView;
type
  PWord = ^Word;
var
  p: PChar;
  wv: pointer;
  Sel: Word;
begin
  p := FindPos(@TView.WriteBuf, @TView.WriteLine, #$C4#$7E#$0A#$E8);
  Inc(p, PWord(p + 4)^ + 6);
{$ifdef DPMI}
  Sel := AllocSelector(PtrRec(p).Seg);
  PtrRec(p).Seg := PrestoChangoSelector(PtrRec(p).Seg, Sel);
{$endif}
  wv := @GWriteView;
  P[0] := #$9A;		{ call far direct }
  Move(wv, P[1], 4);
  P[5] := #$C3;		{ retn }
{$ifdef DPMI}
  FreeSelector(Sel)
{$endif}
end;

{ Method-replacing patches ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

function VMTMethod(ObjType: pointer; Offs: Word): pointer; assembler;
asm
	les	di, ObjType
	add	di, Offs
	mov	ax, es:[di]
	mov	dx, es:[di+2]
end;

{$ifdef DPMI}
procedure ESCode2Data; assembler;
{ preserves di }
asm
	push	di
	push	es
	push	es
	call	AllocSelector
	push	ax
	call    PrestoChangoSelector
	mov	es, ax
	pop	di
end;

procedure ESFreeData; assembler;
asm
	push	es
	xor	ax, ax
	mov	es, ax
	call	FreeSelector
end;
{$endif}

procedure Patch(OldMethod, NewMethod: pointer); assembler;
asm
	les	di, OldMethod
{$ifdef DPMI}
	call    ESCode2Data
{$endif}
	cld
	mov	al, $EA			{ jmp far direct }
	stosb
	mov	ax, WORD PTR NewMethod[0]
	stosw
	mov	ax, WORD PTR NewMethod[2]
	stosw
{$ifdef DPMI}
	call    ESFreeData
{$endif}
end;

{ These patches speed up writing since there appear multiple WriteView calls
}
procedure TView_WriteBuf(X, Y, W, H: Integer; var Buf;
  var Self: TView); far; assembler;
var
  Target: Pointer; {Variables used by WriteView}
  Buffer: Pointer;
  Offset: Word;
asm
	CMP     H,0
	JLE     @@2
	push	Self.Word.2
	push	Self.Word
	call    LockGraph
@@1:	MOV     AX,Y
	MOV     BX,X
	MOV     CX,W
	LES     DI,Buf
	CALL    GWriteView
	MOV     AX,W
	SHL     AX,1
	ADD     WORD PTR Buf[0],AX
	INC     Y
	DEC     H
	JNE     @@1
	push	Self.Word.2
	push	Self.Word
	call	UnlockGraph
@@2:
end;

procedure TView_WriteLine(X, Y, W, H: Integer; var Buf;
  var Self: TView); far; assembler;
var
  Target: Pointer; {Variables used by WriteView}
  Buffer: Pointer;
  Offset: Word;
asm
	CMP     H,0
	JLE     @@2
	push	Self.Word.2
	push	Self.Word
	call    LockGraph
@@1:    MOV     AX,Y
	MOV     BX,X
	MOV     CX,W
	LES     DI,Buf
	CALL    GWriteView
	INC     Y
	DEC     H
	JNE     @@1
	push	Self.Word.2
	push	Self.Word
	call	UnlockGraph
@@2:
end;

{ Other functions
}

procedure SetState(AState: Word; Enable: Boolean; var Self: TGView);
begin
  Self.SetState(AState, Enable)
end;

procedure TView_ResetCursor(var Self: TView); far;
var
  gg: pointer;
begin
  gg := GGateOf(@Self);
  If gg = nil then Exit;
  asm
	LES     DI,Self
	MOV     AX,ES:[DI].TView.State
	NOT     AX
	TEST    AX,sfVisible+sfCursorVis+sfFocused
	JNE     @@4
	MOV     AX,ES:[DI].TView.Cursor.Y
	MOV     DX,ES:[DI].TView.Cursor.X
@@1:    OR      AX,AX
	JL      @@4
	CMP     AX,ES:[DI].TView.Size.Y
	JGE     @@4
	OR      DX,DX
	JL      @@4
	CMP     DX,ES:[DI].TView.Size.X
	JGE     @@4
	ADD     AX,ES:[DI].TView.Origin.Y
	ADD     DX,ES:[DI].TView.Origin.X
	MOV     CX,DI
	MOV     BX,ES
	LES     DI,ES:[DI].TView.Owner
	MOV     SI,ES
	OR      SI,DI
	JE      @@5
	TEST    ES:[DI].TView.State,sfVisible
	JE      @@4
	LES     DI,ES:[DI].TGroup.Last
@@2:    LES     DI,ES:[DI].TView.Next
	CMP     CX,DI
	JNE     @@3
	MOV     SI,ES
	CMP     BX,SI
	JNE     @@3
	LES     DI,ES:[DI].TView.Owner
	JMP     @@1
@@3:    TEST    ES:[DI].TView.State,sfVisible
	JE      @@2
	MOV     SI,ES:[DI].TView.Origin.Y
	CMP     AX,SI
	JL      @@2
	ADD     SI,ES:[DI].TView.Size.Y
	CMP     AX,SI
	JGE     @@2
	MOV     SI,ES:[DI].TView.Origin.X
	CMP     DX,SI
	JL      @@2
	ADD     SI,ES:[DI].TView.Size.X
	CMP     DX,SI
	JGE     @@2
@@4:
	{ Hide cursor }

	push	sfCursorVis
	push	0
	les	di, gg
	mov	es:[di].TGView.CursorLock, 1
	push	es
	push	di
	call	SetState
	jmp     @@0

@@5:
	{ Show cursor at ax,dx }

	push	dx
	push	ax
	les	di, gg
	mov	es:[di].TGView.CursorLock, 1
	mov	ax, es:[di].TGView.State
	and	es:[di].TGView.State, not sfCursorVis
	not	ax
	and	ax, sfCursorVis+sfExposed+sfVisible+sfSelected
	jnz	@@7

	test	es:[di].TGView.CursorFlag, 1
	jz	@@7

	push	es
	push	di
	call	TGView.DrawCursor
@@7:
	LES     DI,Self
	TEST    ES:[DI].TView.State,sfCursorIns
	mov	ax, 0
	JE      @@6
	mov	ax, 1
@@6:
	push	sfCursorIns
	push	ax
	les	di, gg
	push	es
	push	di
	call	SetState

	pop	cx
	pop	ax
	les	di, gg
	mul	CharSize.X
	push	ax
	mov	ax, cx
	inc	ax
	mul	CharSize.Y
	sub	ax, es:[di].TGView.CursorSize.y
	push	ax

	push	es
	push	di
	call	TGView.SetCursor

	push	sfCursorVis
	push	1
	les	di, gg
	mov	es:[di].TGView.CursorLock, 0
	push	es
	push	di
	call	SetState
@@0:
  end;
end;

procedure TView_DrawView(var Self: TView); far;
var
  gg: PGView;
begin
  if Self.Exposed then
  begin
    gg := GGateOf(@Self);
    If (gg <> nil) and gg^.Exposed
    then begin
      LockGraph(@Self);
      Self.Draw;
      UnlockGraph(@Self);
      if Self.State and sfFocused <> 0
      then asm
	  les	di, Self
	  push	es
	  push	di
	  mov	di, [es:di]	{VMT}
	  call	DWORD PTR [di+80] {ResetCursor}
      end;
    end
  end;
end;

{ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

function NewGGate(View: PView): PGGate;
var
  textBounds: TRect;
  grfxBounds: TRect;
  GGate: PGGate;
  TGate: PTGate;
  GrowMode: Word;
begin
  View^.GetBounds(textBounds);
  with textBounds do
    with CharSize do
      grfxBounds.Assign(A.x * X, A.y * Y, B.x * X, B.y * Y);
  View^.MoveTo(0, 0);
  View^.GetBounds(textBounds);
  GrowMode := View^.GrowMode;
  View^.GrowMode := gfGrowHiX + gfGrowHiY;
  New(TGate, Init(textBounds, View));
  New(GGate, Init(grfxBounds, TGate));
  GGate^.GrowMode := GrowMode;
  NewGGate := GGate
end;

procedure EmptyGGate(GGate: PGGate);
begin
  GGate^.TGate^.GGate := nil;
  with GGate^.TGate^ do
    Delete(First);
  GGate^.Hide;
end;

function FreeGGate(GGate: PGGate): PView;
begin
  GGate^.TGate^.GGate := nil;
  with GGate^.TGate^ do
  begin
    FreeGGate := First;
    If Last <> nil
    then Delete(First)
  end;
  Dispose(GGate, Done)
end;

{ Method-hooking patches ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

{$ifdef DPMI}

{ In DPMI mode, a modern entry code (with ENTER) is used if there are
  locals in the procedure. We can't put a CALL FAR there, since this
  requires 5 out of 4 bytes.
  Instead, we install an INT C0, followed by a table index.
}

const
  HookInterrupt = $C0;
  MethodHookPtr: Byte = 0;
  MethodHookMax = 15;
type
  THook = record
    hook: pointer;
    orig: pointer;
  end;
var
  MethodHookTable: array[0..MethodHookMax] of THook;
  MethodLocalsTable: array [0..MethodHookMax] of Word;
  Origint: pointer;

type
  TReturn = Real;	{ six bytes }

procedure PatchCall(OldMethod, NewMethod: pointer);
var
  loc: Word;
begin
  asm
	les	di, OldMethod
	call    ESCode2Data
	mov	ax, es:[di+1]
	mov	loc, ax
	mov	es:[di].word, HookInterrupt * 256 + $cd		{ int }
	mov	al, MethodHookPtr
	xor	ah, ah
	mov	es:[di+2], ax
	call    ESFreeData
  end;
  with MethodHookTable[MethodHookPtr] do
  begin
    hook := NewMethod;
    orig := OldMethod;
  end;
  MethodLocalsTable[MethodHookPtr] := loc;
  Inc(MethodHookPtr)
end;

procedure StdEntry; far; assembler;
asm
	pop	di
	pop	es			{ get return address }
	pop	dx			{ get flags }

	push	bp			{ make stack frame }
	mov	bp, sp
	mov	si, es:[di]
	shl	si, 1
	sub	sp, MethodLocalsTable[si].word

	add	di, 2			{ next instruction }
	push	dx
	push	es
	push	di
	iret
end;

procedure DispatchInt; far; assembler;
asm
	push	bp
	mov	bp, sp
	push	ds
	push	es
	push	ax
	push	si
	push	di

	les	di, [bp+2]		{ return address }
	mov	ax, es:[di]		{ get hook index }
	cmp	ax, MethodHookMax
	ja	@@0			{ bad index }

	shl	ax, 3
	mov	si, OFFSET MethodHookTable
	add	si, ax

	mov	ax, SEG MethodHookTable
	mov	ds, ax

	mov	ax, [si].THook.orig.word[0] { check the hook }
	add	ax, 2
	cmp	ax, di
	jne	@@0
	mov	ax, es
	cmp	ax, [si].THook.orig.word[2]
	jne	@@0

	mov	sp, bp			{ no danger: called from within prog }
	pop	bp
	sti
	jmp	[si].THook.hook
@@0:					{ hm, some strange call }
	pop	di
	pop	si
	pop	ax
	pop	es
	pop	ds
	pop	bp
	iret
end;

function GetProtModeInt(Int: Byte): Pointer; assembler;
asm
	MOV	BL,Int
	MOV	AX,0204H
	INT	31H
	MOV	AX,DX
	MOV	DX,CX
end;

procedure SetProtModeInt(Int: Byte; Vector: Pointer); assembler;
asm
	MOV	BL,Int
	MOV	DX,Vector.Word[0]
	MOV	CX,Vector.Word[2]
	MOV	AX,0205H
	INT	31H
end;

procedure Initint;
begin
  Origint := GetProtModeInt(HookInterrupt);
  SetProtModeInt(HookInterrupt, @Dispatchint)
end;

procedure Doneint;
begin
  SetProtModeInt(HookInterrupt, Origint)
end;

{$else}

type
  TReturn = pointer;

procedure PatchCall(OldMethod, NewMethod: pointer); assembler;
asm
	les	di, OldMethod
	cld
	mov	al, $9A			{ call far direct }
	stosb
	mov	ax, WORD PTR NewMethod[0]
	stosw
	mov	ax, WORD PTR NewMethod[2]
	stosw
end;

procedure StdEntry; far; assembler;
{ suitable for procs with locals *if they don't use ENTER* }
asm
	pop	di			{ Get return address }
	pop	es
	push	bp			{ Make stack frame }
	mov	bp, sp
	mov	al, es:[di]		{ rest of "sub sp, nn" }
	xor	ah, ah
	sub	sp, ax
	inc	di
	push	es			{ Push new return address }
	push	di
end;

{$endif}

const
  TypeOfTTGate: pointer = TypeOf(TTGate);
  sReturn = SizeOf(TReturn);

function IsTopMost(var Self: TView): Boolean; near; assembler;
{ additionally returns Zero on false; on true, es:di is GGate }
asm
	les	di, Self
	les	di, es:[di].TView.Owner
	mov	ax, es
	or	ax, di
	jz	@@0

	push	es
	push	di
	les	di, es:[di].TGroup.Last
	les	di, es:[di].TView.Next
	cmp	di, Self.Word
	jnz	@@0
	mov	ax, es
	cmp	ax, Self.2.Word
	jnz	@@0
	pop	di
	pop	es

	mov	ax, es:[di]
	cmp	ax, WORD PTR TypeOfTTGate
	jnz	@@0

	les	di, es:[di].TTGate.GGate
	mov	ax, es
	or	ax, di
	jz	@@0

	mov	al, 1
	pop	bp
	retn
@@0:
	xor	al, al
end;

procedure TView_SetState(AState: Word; Enable: Boolean;
  var Self: TView; Return: TReturn); far; assembler;
asm
	push	WORD PTR Self[2]
	push	WORD PTR Self[0]
	call	IsTopMost
	jz	@@0

	push	AState
	mov	al, Enable
	or	al, 80H			{ recursion terminator }
	xor	ah, ah
	push	ax
	push	es
	push	di
	call	SetState
@@0:
	leave
	jmp     StdEntry
end;

procedure TView_PutInFrontOf(Target: PView; var Self: TView;
  Return: TReturn); far; assembler;
asm
	push	WORD PTR Self[2]
	push	WORD PTR Self[0]
	call	IsTopMost
	jz	@@0

	mov	ax, WORD PTR Target[0]
	mov	dx, WORD PTR Target[2]
	cmp	ax, WORD PTR Self[0]
	jnz	@@1
	cmp	dx, WORD PTR Self[2]
	jnz	@@1

	{ G-Target is GGate^.GOwner.First }

	push	es
	push	di
	les	di, es:[di].TGView.GOwner
	les	di, es:[di].TGGroup.Last
	les	di, es:[di].TGView.GNext
	pop	ax
	pop	dx
	push	es
	push	di
	push	dx
	push	ax
	jmp	@@2
@@1:
	{ G-Target is NIL }

	push	0
	push	0
	push	es
	push	di
@@2:
	call	TGView.PutInFrontOf
@@0:
	leave
	jmp     StdEntry
end;

procedure SizeLimits(var Min, Max: TPoint; var Self: TView);
begin
  Self.SizeLimits(Min, Max)
end;

procedure TView_DragView(Event: TEvent; Mode: Byte; var Limits: TRect;
  MinSize, MaxSize: TPoint; var Self: TView; Return: TReturn); far; assembler;
var
  Dummy: TPoint;
asm
	push	WORD PTR Self[2]
	push	WORD PTR Self[0]
	call	IsTopMost
	jz	@@0

	push	es				{ Change MaxSize }
	push	di
	les	di, es:[di].TGView.GOwner
	push	es
	push	di
	lea	ax, Dummy
	push	ss
	push	ax
	lea	ax, MaxSize
	push	ss
	push	ax
	push	es
	push	di
	call	SizeLimits

	pop	di
	pop	es
	push	WORD PTR Limits[2]		{ Store ggate.gowner-extent }
	push	WORD PTR Limits[0]
	push	es
	push	di
	call	TGView.GetExtent
	pop	di
	pop	es

	mov	cx, CharSize.x			{ Multiply coords }
	mov	bx, CharSize.y
	push	ds
	lds	si, Event
	mov	ax, MinSize.x
	mul	cx
	mov	MinSize.x, ax
	test	[si].TEvent.What, evMouse
	jz	@@1
	mov	ax, [si].TEvent.Where.x
	mul	cx
	mov	[si].TEvent.Where.x, ax
@@1:
	mov	ax, MinSize.y
	mul	bx
	mov	MinSize.y, ax
	test	[si].TEvent.What, evMouse
	jz	@@2

	mov	ax, [si].TEvent.Where.y
	mul	bx
	mov	[si].TEvent.Where.y, ax

	add	si, TEvent.Where
	push	es
	push	di
	push	ax
	push	WORD PTR [si]
	push	ds
	push	si
	push	es
	push	di
	call	TGView.MakeGlobal
	pop	di
	pop	es
@@2:
	pop	ds

	leave
	add	sp, sReturn		{ skip return to TView.DragView }
	pop	ax
	pop	dx			{ save return to caller }
	add	sp, 4			{ skip Self }
	push	rmDnRi			{ additional param }
	push	es
	push	di			{ push Self }
	push	dx
	push	ax			{ push return address }
	jmp	TGView.DragView

@@0:
	leave
	jmp     StdEntry
end;

function TransGroup(Group: PGroup): PGGroup;
begin
  If Group = PGroup(App.Application) then TransGroup := GvApp.Application else
  if Group = PGroup(App.Desktop) then TransGroup := GvApp.Desktop else
  if (TypeOf(Group^) = TypeOf(TTGate)) and (PTGate(Group)^.GGate <> nil)
  then TransGroup := PTGate(Group)^.GGate^.GOwner
  else TransGroup := nil
end;

procedure TGroup_Delete(P: PView; var Self: TGroup; Return: TReturn); far;
begin
  If (P <> nil) and (P^.Owner <> nil) and
     (TypeOf(P^.Owner^) = TypeOf(TTGate)) and
     (PTGate(P^.Owner)^.GGate <> nil) and
     (PTGate(P^.Owner)^.GGate^.TGate <> nil)
  then begin
    EmptyGGate(PTGate(P^.Owner)^.GGate);
    asm
	leave
	add	sp, sReturn
	retf	8
    end
  end
  else asm
	leave
	jmp	StdEntry
  end
end;

function TGroup_ExecView(P: PView; var Self: TGroup; Return: TReturn): Word; far;
var
  GSelf: PGGroup;
  GGate: PGGate;
begin
  GSelf := TransGroup(@Self);
  If GSelf = nil
  then asm
	leave
	jmp	StdEntry
  end;
  GGate := NewGGate(P);
  TGroup_ExecView := GSelf^.ExecView(GGate);
  asm
	push	ax
  end;
  FreeGGate(GGate);
  asm
	pop	ax
	leave
	add	sp, sReturn	{ skip return to TGroup.InsertBefore }
	retf    8		{ return to caller }
  end
end;

procedure TGroup_InsertBefore(P, Target: PView;
  var Self: TGroup; Return: TReturn); far;
var
  GSelf: PGGroup;
  GGate: PGGate;
  GTarget: PGView;
begin
  { Test for insertion into up-links or T-Gates
  }
  GSelf := TransGroup(@Self);
  If GSelf = nil
  then asm
	leave
	jmp	StdEntry
  end;
  GGate := NewGGate(P);
  If Target = nil then GTarget := nil else
  if Target = PView(App.Menubar) then GTarget := GvApp.Menubar else
  if Target = PView(App.Desktop) then GTarget := GvApp.Desktop else
  if Target = PView(App.StatusLine) then GTarget := GvApp.StatusLine else
  GTarget := GSelf^.First;
  GSelf^.InsertBefore(GGate, GTarget);
  asm
	leave
	add	sp, sReturn	{ skip return to TGroup.InsertBefore }
	retf    12		{ return to caller }
  end;
end;

procedure TScrollbar_SetParams(AValue, AMin, AMax, APgStep, AArStep: Integer;
  var Self: TUpScrollbar; Return: TReturn); far;
begin
  If TypeOf(Self) = TypeOf(TUpScrollbar) then
  with Self do
  begin
    if AMax < AMin then AMax := AMin;
    if AValue < AMin then AValue := AMin;
    if AValue > AMax then AValue := AMax;
    Value := AValue;
    Min := AMin;
    Max := AMax;
    PgStep := APgStep;
    ArStep := AArStep;
    Scrollbar^.SetParams(AValue, AMin, AMax, APgStep, AArStep);
    asm
	leave
	add	sp, sReturn
	retf	14
    end
  end
  else asm
	leave
	jmp	StdEntry
  end
end;

procedure TWindow_Zoom(var Self: views.TWindow; Return: TReturn); far;
var
  R: TRect;
  Max, Min: TPoint;
  TheGGate: PGView;
begin
  If IsTopMost(Self)
  then begin
    asm
	mov	TheGGate.Word[0], di
	mov	TheGGate.Word[2], es
    end;
    TheGGate^.SizeLimits(Min, Max);
    Max.X := Max.X div CharSize.X;
    Max.Y := Max.Y div CharSize.Y;
    if (Longint(Self.Size) <> Longint(Max)) or
       (Self.ZoomRect.A.Y and $8000 = 0)
    then begin
      TheGGate^.GetBounds(Self.ZoomRect);
      with Self.ZoomRect.A do
	Y := Y or $8000;
      R.A.X := 0; R.A.Y := 0;
      Max.X := Max.X * CharSize.X;
      Max.Y := Max.Y * CharSize.Y;
      R.B := Max;
      TheGGate^.Locate(R);
    end
    else begin
      with Self.ZoomRect.A do
	Y := Y and not $8000;
      TheGGate^.Locate(Self.ZoomRect);
      with Self.ZoomRect.A do
	Y := Y or $8000;
    end;
    asm
	leave
	add	sp, sReturn
	retf	4
    end
  end
  else asm
	leave
	jmp	StdEntry
  end
end;

{ MsgBox patches ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

procedure ScalePoint(var P: TPoint);
begin
  P.X := P.X * CharSize.X;
  P.Y := P.Y * CharSize.Y
end;

procedure ScaleRect(var R: TRect);
begin
  ScalePoint(R.A);
  ScalePoint(R.B);
end;

function _MessageBoxRect(var R: TRect; Msg: String; Params: Pointer;
  AOptions: Word): Word; far;
begin
  ScaleRect(R);
  asm
	leave
	jmp	GvMsgBox.MessageBoxRect
  end
end;

function _InputBoxRect(var Bounds: TRect; Title: String; ALabel:
String; var S: String; Limit: Byte): Word; far;
begin
  ScaleRect(Bounds);
  asm
	leave
	jmp	GvMsgBox.InputBoxRect
  end
end;

{ Palette handling ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

var
  _CBlueWindow: TPalette;
  _CGrayWindow: TPalette;
  _CCyanWindow: TPalette;
  _CDialog: TPalette;

procedure GlobPal(var s: TPalette; const Lookup: TPalette); assembler;
asm
	push	ds
	lds	si, s
	les	bx, LookUp
	mov	cl, [si]
	inc	si
	xor	ch, ch
	cld
@@1:
	mov	al, [si]
	xlat
	mov	[si], al
	inc	si
	loop	@@1
	pop	ds
end;

procedure UpdatePal;
begin
  _CBlueWindow := Views.CBlueWindow; GlobPal(_CBlueWindow, AppPalette);
  _CGrayWindow := Views.CGrayWindow; GlobPal(_CGrayWindow, AppPalette);
  _CCyanWindow := Views.CCyanWindow; GlobPal(_CCyanWindow, AppPalette);
  _CDialog := Dialogs.CDialog; GlobPal(_CDialog, AppPalette);
end;

procedure UseGvPal;
const
  gvDelta: array[wpWhiteWindow..wpCyanWindow] of Byte
    = (9, 31, 53);
  tvDelta: array[wpWhiteWindow..wpCyanWindow] of Byte
    = (7, 23, 15);
var
  Pal: PPalette;
  i: Integer;

	procedure put(tvIndex, gvFore, gvBack: Integer);
	begin
	  AppPalette[tvIndex] :=
	    Chr(Byte(Pal^[gvBack]) shl 4 or Byte(Pal^[gvFore]))
	end;

	procedure wput(pal, tvIndex, gvFore, gvBack: Integer);
	begin
	  put(tvIndex + tvDelta[pal],
	    gvFore + gvDelta[pal], gvBack + gvDelta[pal])
	end;

begin
  Pal := GvApp.Application^.GetPalette;
  For i := GvViews.wpWhiteWindow to GvViews.wpCyanWindow do
  begin
    wput(i, 6, 19, 21);
    wput(i, 7, 20, 22);
    wput(i, 8, 20, 22)
  end;
  UpdatePal
end;

{ TTGate object ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

constructor TTGate.Init(var Bounds: {text}TRect; view: PView);
begin
  TGroup.Init(Bounds);
  State := State or sfExposed;
  Options := view^.Options and
    (ofCentered + ofSelectable + ofFirstClick + ofTopSelect + ofTileable);
  DragMode := dmDragMove + dmDragGrow;
  Buffer := ScreenBuffer;
  Insert(view)
end;

procedure TTGate.ChangeBounds(var Bounds: TRect);
var
  SaveGrowMode: Word;
  SaveState: Word;
begin
  If Last <> nil then
    with First^ do
    begin
      SaveState := State;
      State := State and not sfExposed;
      SaveGrowMode := GrowMode;
      GrowMode := gfGrowHiX + gfGrowHiY;
      inherited ChangeBounds(Bounds);
      GrowMode := 0;
      Bounds.B.X := Bounds.A.X + Size.X;
      Bounds.B.Y := Bounds.A.Y + Size.Y;
      inherited ChangeBounds(Bounds);
      GrowMode := SaveGrowMode;
      State := State or (SaveState and sfExposed)
    end;
end;

function TTGate.Execute: Word;
var
  P: PView;
begin
  P := First;
  P^.State := P^.State or sfModal;
  Execute := P^.Execute;
  P^.State := P^.State and not sfModal;
end;

procedure TTGate.GetEvent(var Event: TEvent);
begin
  GGate^.GetEvent(Event);
  GGate^.TransEvent(Event, true)
end;

function TTGate.GetPalette: PPalette;
var
  G: PGView;
begin
  GetPalette := @AppPalette;
  If GGate = nil then Exit;
  G := GGate^.GOwner;
  If G = nil then Exit;
  If (G = PGView(GvApp.Desktop)) or (G = PGView(GvApp.Application))
  then GetPalette := @AppPalette else
  if G^.Valid(cmCancel) then GetPalette := @_CDialog else
  if Message(G, evBroadcast, 40 {cmHeyYou}, nil) = G then
  case PWindow(G)^.Palette of
    wpWhiteWindow:
      GetPalette := @_CBlueWindow;
    wpGrayWindow:
      GetPalette := @_CGrayWindow;
    wpCyanWindow:
      GetPalette := @_CCyanWindow;
  end;
end;

procedure TTGate.HandleEvent(var Event: TEvent);
begin
  If Event.What and evDown <> 0
  then begin
    Event.What := Event.What and not evDown;
    inherited HandleEvent(Event)
  end
  else begin
    If (GGate <> nil) and (GGate^.GOwner <> nil)
    then GGate^.GOwner^.HandleEvent(Event)
  end;
end;

procedure TTGate.PutEvent(var Event: TEvent);
begin
  If Event.What and evMouse <> 0
  then ScalePoint(Event.Where);
  GGate^.PutEvent(Event)
end;

{ TGGate object ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

constructor TGGate.Init(var Bounds: {grfx}TRect; ATGate: PTGate);
begin
  TGView.Init(Bounds);
  TGate := ATGate;
  TGate^.GGate := @Self;
  Options :=
    (TGate^.Options and
      (ofCentered + ofSelectable + ofFirstClick + ofTopSelect + ofTileable)) or
    (ofBuffer + ofMetafile);
  EventMask := $FFFF;
  CursorSize.X := CharSize.X;
  CursorSize.Y := 2
end;

destructor TGGate.Done;
var
  t: PView;
begin
  If TGate <> nil
  then begin
    t := TGate;
    TGate := nil;
    Dispose(t, Done)
  end;
  TGView.Done
end;

procedure TGGate.ChangeBounds(var Bounds: TRect);
var
  T: TRect;
begin
  T.A.X := 0; T.A.Y := 0;
  T.B.X := (Bounds.B.X - Bounds.A.X) div CharSize.x;
  T.B.Y := (Bounds.B.Y - Bounds.A.Y) div CharSize.y;
  TGate^.Locate(T);
  T.B.X := T.B.X * CharSize.x;
  T.B.Y := T.B.Y * CharSize.y;
  T.Move(Bounds.A.X, Bounds.A.Y);
  inherited ChangeBounds(T);
end;

procedure TGGate.Draw;
begin
  If TGate <> nil then TGate^.ReDraw
end;

procedure TGGate.DrawBuf(var Clip: TRect; var Buf);
var
  i, px, x, y: Integer;
  w, sw: Word;
  s: string;

	procedure Flush;
	begin
	  SetBkColor(Hi(sw) shr 4);
	  SetTextParams(ftMonoSpace, 0, Hi(sw) and 15, false);
	  outtextxy(px, y * CharSize.y, s);
	end;

begin
  If TGate <> nil
  then begin
    SetViewPort;
    If CursorFlag and GetState(sfCursorVis + sfSelected) then DrawCursor;
    SetTextJustify(LeftText, TopText);
    SetFillStyle(SolidFill, 7);
    SetBkMode(Opaque);
    i := 0;
    y := Clip.a.y;

    sw := TDrawBuffer(Buf)[i];
    px := Clip.a.x * CharSize.x;
    s := Char(Lo(sw));
    Inc(i);
    For x := Clip.a.x + 1 to Clip.b.x - 1 do
    begin
      w := TDrawBuffer(Buf)[i];
      If Lo(w) = 255 then w := w and $FF00 + 32;
      If Hi(w) <> Hi(sw)
      then begin
	Flush;
	sw := w;
	px := x * CharSize.x;
	s := Char(Lo(w));
      end
      else
	s := s + Char(Lo(w));
      Inc(i)
    end;
    Flush;
    SetBkMode(Transparent);
    If CursorFlag and GetState(sfCursorVis + sfSelected) then DrawCursor;
    RestoreViewPort;
  end
end;

function TGGate.Execute: Word;
begin
  Execute := TGate^.Execute;
end;

procedure TGGate.HandleEvent(var Event: TEvent);
var
  Copy: TEvent;
begin
  Copy := Event;
  TGView.HandleEvent(Copy);
  if TGate <> nil
  then begin
    TransEvent(Event, true);
    Event.What := Event.What or evDown;
    TGate^.HandleEvent(Event);
    TransEvent(Event, false);
    If TGate^.GGate = nil
    then Free
  end
end;

procedure TGGate.SetState(AState: Word; Enable: Boolean);
var
  Down: Boolean;
begin
  Down := Byte(Enable) and $80 = 0;
  Byte(Enable) := Byte(Enable) and 1;
  If Down or (AState and sfExposed = 0)
  then TGView.SetState(AState, Enable);
  If AState = sfCursorIns then
    if Enable
    then CursorSize := CharSize
    else begin
      CursorSize.X := CharSize.X;
      CursorSize.Y := 2
    end
  else begin
    If Down and (TGate <> nil)
    then begin
      if AState and (sfDragging + sfModal + sfVisible + sfCursorVis) = 0
      then TGate^.SetState(AState, Enable);
      if (AState and (sfSelected + sfFocused) <> 0) and
	 (TGate^.Current <> nil)
      then TGate^.Current^.SetState(AState, Enable)
    end
  end
end;

procedure TGGate.TransEvent(var Event: TEvent; Down: Boolean);
begin
  if Event.What and evMouse <> 0 then
  if Down
  then begin
    SavedWhere := Event.Where;
    MakeLocal(Event.Where, Event.Where);
    with Event.Where do
    begin
      X := X div Charsize.x;
      Y := Y div Charsize.y
    end
  end
  else
    Event.Where := SavedWhere
end;

function TGGate.Valid(Command: Word): Boolean;
begin
  If Command = cmCancel
  then Valid := false { a little helper for GetPalette }
  else Valid := inherited Valid(Command) and TGate^.Valid(Command)
end;

{ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

procedure TUpProgram.HandleEvent(var Event: TEvent);
begin
  {If GvApp.Application <> nil
  then GvApp.Application^.HandleEvent(Event);}
  inherited HandleEvent(Event)
end;

procedure TUpProgram.InitDesktop;
var
  R: TRect;
begin
  GetExtent(R);
  Inc(R.A.Y);
  Dec(R.B.Y);
  App.Desktop := New(PUpDesktop, Init(R));
end;

procedure TUpProgram.Idle;
begin
  GvApp.Application^.Idle
end;

procedure TUpDesktop.Draw;
begin
  If GvApp.Application <> nil
  then GvApp.Application^.Redraw
end;

procedure TUpDesktop.HandleEvent(var Event: TEvent);
begin
  If GvApp.Desktop <> nil
  then GvApp.Desktop^.HandleEvent(Event);
end;

constructor TUpScrollbar.Init(AScrollbar: PScrollbar);
var
  R: TRect;
begin
  R.Assign(0, 0, 0, 0);
  inherited Init(R);
  Scrollbar := AScrollbar;
  Value := MaxInt;
  EventMask := evBroadcast
end;

procedure TUpScrollbar.HandleEvent;
begin
  If (Event.What = evBroadcast) and
     ((Event.Command = cmScrollbarClicked) or
      (Event.Command = cmScrollbarChanged)) and
     (Event.InfoPtr = Scrollbar)
  then begin
    Value := Scrollbar^.Value;
    Message(Owner, evBroadcast, Event.Command, @Self);
    ClearEvent(Event)
  end;
end;

{ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
}

procedure SetGateScreenSize(X, Y: Integer);
begin
  FreeMem(ScreenBuffer, ScreenWidth * ScreenHeight * 2);
  ScreenWidth := X;
  ScreenHeight := Y;
  GetMem(ScreenBuffer, ScreenWidth * ScreenHeight * 2)
end;

procedure UpdateSizes;
var
  R: TRect;
begin
  If GvApp.Desktop <> nil
  then begin
    GvApp.Desktop^.GetExtent(R);
    R.B.X := R.B.X div CharSize.X;
    R.B.Y := R.B.Y div CharSize.Y;
    App.Desktop^.SetBounds(R);
  end
end;

var
  UpApp: TUpProgram;
  SaveExit: pointer;

{ Exit procedure
}

procedure GvTvExit; far;
begin
  UpApp.Done;
  FreeMem(ScreenBuffer, ScreenWidth * ScreenHeight * 2);
{$ifdef DPMI}
  Doneint;
{$endif}
  ExitProc := SaveExit
end;

begin
  { Start-up screen
  }
  ScreenWidth := 128;
  ScreenHeight := 60;
  GetMem(ScreenBuffer, ScreenWidth * ScreenHeight * 2);
  { Init up-links
  }
  UpApp.Init;
{$ifdef DPMI}
  Initint;
{$endif}
  { WriteView patch
  }
  PatchWriteView;
  { MsgBox patches
  }
  Patch(@MsgBox.MessageBox, @GvMsgBox.MessageBox);
  Patch(@MsgBox.InputBox, @GvMsgBox.InputBox);
  Patch(@MsgBox.MessageBoxRect, @_MessageBoxRect);
  Patch(@MsgBox.InputBoxRect, @_InputBoxRect);
  { TView patches
  }
  Patch(@TView.WriteLine, @TView_WriteLine);
  Patch(@TView.WriteBuf, @TView_WriteBuf);
  Patch(@TView.DrawView, @TView_DrawView);
  PatchCall(@TView.DragView, @TView_DragView);
  Patch(VmtMethod(TypeOf(TView), 80 {ResetCursor}), @TView_ResetCursor);
  PatchCall(@TView.SetState, @TView_SetState);
  PatchCall(@TView.PutInFrontOf, @TView_PutInFrontOf);
  { TGroup patches
  }
  PatchCall(@TGroup.Delete, @TGroup_Delete);
  PatchCall(@TGroup.ExecView, @TGroup_ExecView);
  PatchCall(@TGroup.InsertBefore, @TGroup_InsertBefore);
  { TWindow patch
  }
  PatchCall(@views.TWindow.Zoom, @TWindow_Zoom);
  { TScrollbar patch
  }
  PatchCall(@views.TScrollbar.SetParams, @TScrollbar_SetParams);
  { Start-up palette handling
  }
  AppPalette := App.CColor;
  UpdatePal;
  { Install exit proc
  }
  SaveExit := ExitProc;
  ExitProc := @GvTvExit;
end.

