unit GvClip;

{ Graphics Vision Clipboard,
  Copr. 1997 Matthias K"oppe.

  * TClipEditor is a simple TFileEditor descendant which talks to the
    MS Windows clipboard if running in a DOS box or as Windows program.
  * TClipWindow is the same to TClipEditor as
    TEditWindow is to TFileEditor.
}

interface

{$ifdef Windows}
uses WinTypes, Objects, Drivers, GvViews, GvEdit;
{$else !Windows}
uses Objects, Drivers, GvViews, GvEdit;
{$endif Windows}

type
  PClipEditor = ^TClipEditor;
  TClipEditor = object(TFileEditor)
    constructor Init(var Bounds: TRect;
      AHScrollBar, AVScrollBar: PScrollBar;
      AIndicator: PIndicator; AFileName: FNameStr);
    destructor Done; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    function InsertFrom(Editor: PEditor): Boolean; virtual;
  private
    {$ifdef Windows}
    NextWnd, ViewerWnd: HWND;
    Locked: Boolean;
    {$endif Windows}
    procedure ReadClipboard;
  end;

  PClipWindow = ^TClipWindow;
  TClipWindow = object(TEditWindow)
    constructor Init(var Bounds: TRect; ANumber: Integer);
  end;

implementation

{$ifdef Windows}
uses WinProcs, Memory, Strings, WinRes, WinGr, GvApp;
{$else !Windows}
uses Memory, Strings, WinRes, Clipbrd, GvApp;
{$endif}

const
  CF: array[Boolean] of Word = (cf_OEMText, cf_Text);

{$ifndef Windows}

{ We use the following hack to check if we've already seen the clipboard:
  A cookie (format cf_GV) is put to the clipboard. A small (<=32 byte)
  or no cookie at all means we haven't seen it yet. A large (>=33 byte)
  cookie means we have seen the clipboard. -- Note that this approach
  will not work if more than one copy is active.
}
const
  cf_GV = 44444;

procedure ClipboardSeen(yeah: Boolean);
var
  size: Word;
  Buf: pointer;
begin
  if yeah
  then size := 33
  else size := 1;
  GetMem(Buf, size);
  if not SetClipboardData(cf_GV, Buf^, size)
  then FreeMem(Buf, size);
end;

{$endif Windows}

constructor TClipEditor.Init(var Bounds: TRect;
  AHScrollBar, AVScrollBar: PScrollBar;
  AIndicator: PIndicator; AFileName: FNameStr);
begin
  inherited Init(Bounds, AHScrollbar, AVScrollbar, AIndicator, AFileName);
  CanUndo := false;
  Clipboard := @Self;
  EventMask := EventMask or (evBroadcast {$ifdef Windows} + evSystem {$endif Windows});
  {$ifndef Windows}
  ReadClipboard;
  {$endif Windows}
end;

destructor TClipEditor.Done;
begin
  {$ifdef Windows}
  if ViewerWnd <> 0
  then ChangeClipboardChain(ViewerWnd, NextWnd);
  {$else !Windows}
  If WinOldAp
  then begin
    OpenClipboard;
    ClipboardSeen(false);
    CloseClipboard;
  end;
  {$endif Windows}
  inherited Done;
end;

procedure TClipEditor.HandleEvent(var Event: TEvent);
var
  P: PGView;
begin
  inherited HandleEvent(Event);
  {$ifdef Windows}
  If (Event.What = evBroadcast) and (Event.Command = cmIdle)
  then begin
    if ViewerWnd = 0
    then begin
      P := FindWin;
      if P <> nil
      then begin
        ViewerWnd := P^.Wnd;
        NextWnd := SetClipboardViewer(ViewerWnd)
      end
    end;
  end else
  if (Event.What = evSystem) then
  with PMSG(Event.InfoPtr)^ do
    case message of
      WM_DRAWCLIPBOARD:
        begin
          if not Locked then ReadClipboard;
          SendMessage(NextWnd, message, wparam, lparam);
          ClearEvent(Event);
        end;
      WM_CHANGECBCHAIN:
        begin
          if wparam = ViewerWnd
          then ViewerWnd := LoWord(lParam)
          else SendMessage(NextWnd, message, wparam, lparam);
          ClearEvent(Event);
        end;
    end;
  {$else !Windows}
  if (Event.What = evBroadcast) and (Event.Command = cmIdle) and
     (GetShiftState and kbAltShift = 0) and
        {the line above is a hack, "prevents" open clipboard at Windows task switch...}
     (PeekClipboardDataSize(cf_GV) < 33)
  then ReadClipboard
  {$endif Windows}
end;

{$ifdef Windows}
function TClipEditor.InsertFrom(Editor: PEditor): Boolean;
var
  siz: Word;
  buf: PChar;
  Handle: THandle;
begin
  InsertFrom := inherited InsertFrom(Editor);
  if ViewerWnd = 0 then Exit;
  siz := Editor^.SelEnd - Editor^.SelStart + 1;
  Handle := GlobalAlloc(GMEM_DDESHARE, siz);
  buf := GlobalLock(Handle);
  If buf <> nil
  then begin
    Move(Editor^.Buffer^[Editor^.BufPtr(Editor^.SelStart)], buf^, siz-1);
    buf[siz-1] := #0;
    GlobalUnlock(Handle);
    Locked := true;
    if OpenClipboard(ViewerWnd)
    then begin
      EmptyClipboard;
      SetClipboardData(CF[AnsiCode], Handle);
      CloseClipboard
    end;
    Locked := false
  end
end;
{$else !Windows}
function TClipEditor.InsertFrom(Editor: PEditor): Boolean;
var
  siz: Word;
  buf: PChar;
begin
  InsertFrom := inherited InsertFrom(Editor);
  if WinOldAp
  then begin
    siz := Editor^.SelEnd - Editor^.SelStart + 1;
    buf := MemAlloc(siz);
    If buf <> nil
    then begin
      Move(Editor^.Buffer^[Editor^.BufPtr(Editor^.SelStart)], buf^, siz-1);
      buf[siz-1] := #0;
      if OpenClipboard
      then begin
	EmptyClipboard;
	if SetClipboardData(CF[AnsiCode], buf^, siz)
	then buf := nil;
	ClipboardSeen(true);
	CloseClipboard
      end;
      if buf <> nil
      then FreeMem(buf, siz);
    end
  end
end;
{$endif Windows}

{$ifdef Windows}
procedure TClipEditor.ReadClipboard;
var
  P: PGView;
  Handle: THandle;
  Buf: PChar;
begin
  if ViewerWnd = 0 then Exit;
  if OpenClipboard(ViewerWnd)
  then begin
    if IsClipboardFormatAvailable(CF[AnsiCode])
    then begin
      Handle := GetClipboardData(CF[AnsiCode]);
      Buf := GlobalLock(Handle);
      if Buf <> nil
      then begin
	SetSelect(BufLen, BufLen, false);
	NewLine;
	InsertText(Buf, StrLen(Buf), true);
      end;
      GlobalUnlock(Handle);
      {dont free this handle}
    end;
    CloseClipboard;
  end
end;
{$else !Windows}
procedure TClipEditor.ReadClipboard;
var
  B: pointer;
  siz: LongInt;
begin
  if WinOldAp and OpenClipboard
  then begin
    siz := GetClipboardDataSize(CF[AnsiCode]);
    if siz > 0
    then begin
      B := MemAlloc(siz);
      if B <> nil
      then begin
	GetClipboardData(CF[AnsiCode], B^);
	SetSelect(BufLen, BufLen, false);
	NewLine;
	InsertText(B, StrLen(B), true);
	FreeMem(B, siz);
      end
    end;
    ClipboardSeen(true);
    CloseClipboard;
  end
end;
{$endif Windows}

constructor TClipWindow.Init(var Bounds: TRect; ANumber: Integer);
var
  HScrollBar, VScrollBar: PScrollBar;
  Indicator: PIndicator;
  Back: PBackground;
  R: TRect;
begin
  TWindow.Init(Bounds, '', ANumber);
  Options := Options or ofTileable;
  Flags := Flags and not wfBackground;

  R.Assign (100, Size.Y - 21, Size.X - 20, Size.Y - 3);
  HScrollBar := New(PScrollBar, Init(R));
  Insert(HScrollBar);
  R.Assign (Size.X - 21, 22, Size.X - 3, Size.Y - 20);
  VScrollBar := New(PScrollBar, Init(R));
  Insert(VScrollBar);
  R.Assign(3, Size.Y - 21, 101, Size.Y - 3);
  Indicator := New(PIndicator, Init(R));
  Insert(Indicator);
  R.Assign (4, 23, Size.X - 21, Size.Y - 21);
  Editor := New(PClipEditor, Init(
    R, HScrollBar, VScrollBar, Indicator, ''));
  Insert(Editor);
  R.Assign (Size.X-20, Size.Y-20, Size.X-4, Size.Y-4);
  Back := New(PScrollbarBack, Init(R));
  With Back^ do GrowMode := gfGrowAll;
  Insert(Back);
end;

end.
