chrisrolliston / CCR.Clipboard

Extended TClipboard implementation for Delphi (FMX and VCL)
60 stars 15 forks source link

CCR.Clipboard

Extended TClipboard implementation for FMX and VCL. Compiles with Delphi XE2 and up for desktop FMX and the VCL, XE6 and up for iOS. Where the platform allows, supports delayed rendering, virtual files, change notifications, and inter-process TClipboard-based drag and drop.

Platform support

Example usage

Testing for readable data

Test for whether plain text, a URL or a TBitmap can be read from the clipboard

if Clipboard.HasText then //...
if Clipboard.HasURL then //...
if Clipboard.HasFormatFor(TBitmap) then //...

Test for a specific format being available

if Clipboard.HasFormat(cfPNG) then //...

Test for whether at least one format in a group is available, returning the first one found

var
  Matched: TClipboardFormat;
begin
  if Clipboard.HasFormat([cfPNG, cfTIFF], Matched) then //...

Enumerate all formats on the clipboard

var
  ClipFormat: TClipboardFormat;
begin
  for ClipFormat in Clipboard do
    ShowMessage(ClipFormat.Name);

Copying and pasting

Copy and paste plain text

Clipboard.AsText := 'Hello world';
S := Clipboard.AsText;

Copy two files and a URL

Clipboard.Open;
try
  Clipboard.AssignFile(SomeFileName1); //file must exist!
  Clipboard.AssignFile(SomeFileName2); //ditto
  Clipboard.AssignURL('http://www.bbc.co.uk/');
finally
  Clipboard.Close;
end;

Copy two pieces of text

Clipboard.Open;
try
  Clipboard.AssignText('First item');
  Clipboard.AssignText('Second item');
finally
  Clipboard.Close;
end;

Works on Apple platforms; on Windows the first item will get overwritten by the second.

Copy and paste a TBitmap

Clipboard.Assign(Bitmap); //copy
//...
Bitmap.Assign(Clipboard); //paste

Requires either CCR.Clipboard.VCL or CCR.Clipboard.FMX (as applicable) to be used somewhere to link in framework-specific code.

Show all plain text, URLs, file names, and virtual file descriptors on the clipboard

for S in Clipboard.GetText do
  ShowMessage(S);
for S in Clipboard.GetURLs do
  ShowMessage(S);
for S in Clipboard.GetFileNames do
  ShowMessage(S);
for S in Clipboard.GetVirtualFileDescriptors do
  ShowMessage(S);

Paste the data for each virtual file into a local variable and show its size

Clipboard.EnumVirtualFiles(
  procedure (const Descriptor: string; const GetBytes: TFunc<TBytes>;
    var LookForMore: Boolean)
  var
    Data: TBytes;
  begin
    Data := GetBytes;
    ShowMessageFmt('%d bytes', [Length(Data)]);
  end);

A TStream version is also available:

Clipboard.EnumVirtualFiles(
  procedure (const Descriptor: string; const SaveToStream: TProc<TStream>;
    var LookForMore: Boolean)
  var
    Stream: TStream;
  begin
    Stream := TMemoryStream.Create;
    try
      SaveToStream(Stream);
      ShowMessageFmt('%d bytes', [Stream.Size]);
    finally
      Stream.Free;
    end;
  end);

A further variant writes virtual files to a directory of your choice:

Clipboard.SaveVirtualFiles(PathToExistingDir, memPastedFileNames.Lines);

Paste as many TBitmap instances as can be read into a TObjectList

FBitmaps.Clear;
Clipboard.GetObjects<TBitmap>(
  procedure (const AssignTo: TProc<TBitmap>; var LookForMore: Boolean)
  var
    Bitmap: TBitmap;
  begin
    Bitmap := TBitmap.Create;
    FBitmaps.Add(Bitmap);
    AssignTo(Bitmap);
  end);

Copy a delay-rendered TBitmap

Clipboard.AssignDelayed<TBitmap>(
  procedure (BitmapToRender: TBitmap)
  begin
    BitmapToRender.SetSize(100, 100);
    //...
  end);

Copy delay-rendered text

Clipboard.AssignTextDelayed(
  function : string
  begin
    Result := 'This is just an example generated at ' + DateTimeToStr(Now);
  end);

Copy the contents of a FMX TImage as a virtual file

Clipboard.AssignVirtualFile('Example.png', Image1.Bitmap);

Copy a delay-rendered virtual text file

Clipboard.AssignVirtualFileDelayed('Example.txt', 
  function : string
  begin
    Result := 'This is just an example generated at ' + DateTimeToStr(Now);
  end);

Custom formats

Register a custom format

var
  cfMyDoc: TClipboardFormat;

cfMyDoc := Clipboard.RegisterFormat('com.mycompany.mydoc');

A UTI string as shown is preferred on Apple platforms, though technically anything can be used.

Copy data as the custom format

Clipboard.Assign(cfMyDoc, SomeBytes);

Register a TPersistent class (TBrush in this case) using a default 'clipper', and copy an instance

TClipboard.RegisterSimpleClipper<TBrush>;
Clipboard.Assign(Rectangle1.Fill);

Paste the brush back in after checking an instance can be read first

if Clipboard.HasFormatFor(TBrush) then
  Rectangle1.Fill.Assign(Clipboard);

Alternatively (and perhaps preferably) the GetObjects syntax can be used:

Clipboard.GetObjects<TBrush>(
  procedure (const AssignTo: TProc<TBrush>; var LookForMore: Boolean)
  begin
    AssignTo(Rectangle1.Fill);
    LookForMore := False;
  end);

Drag and drop

Dragging the contents of a VCL TImage

Add CCR.Clipboard.VCL to the uses clause and handle the control's OnMouseDown event to call TClipboard.BeginDrag as appropriate:

procedure TMyForm.imgDragSourceMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (Button = mbLeft) and not imgDragSource.Picture.Graphic.Empty then
    TClipboard.BeginDrag(Image1,
      procedure (Clipboard: TClipboard)
      begin
        Clipboard.Assign(Image.Picture);
      end);
end;

If you prefer you can use a regular method instead of an anonymous one for the callback:

procedure TMyForm.ImageBeginDrag(Source: TObject; Clipboard: TClipboard)
begin
  Clipboard.Assign((Source as TImage).Picture);
end);

procedure TMyForm.imgDragSourceMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (Button = mbLeft) and not imgDragSource.Picture.Graphic.Empty then
    TClipboard.BeginDrag(Image1, ImageBeginDrag);
end;

Dragging the contents of a FMX TImage

1) Add CCR.Clipboard.FMX to the uses clause and ensure the form is registered for TClipboard-based drag and drop:

procedure TMyForm.FormCreate(Sender: TObject);
begin
  TClipboard.RegisterForDragAndDrop(Self);
end;

2) Either set the image's DragMode property in the Object Inspector to dmAutomatic or initiate a drag operation explicitly like in the VCL case:

procedure TMyForm.imgDragSourceMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  if (Button = mbLeft) and not imgDragSource.Bitmap.IsEmpty then
    TClipboard.BeginDrag(Image1,
      procedure (Clipboard: TClipboard)
      begin
        Clipboard.Assign(Image1.Bitmap);
      end);
end;

Making a VCL TImage a drop target for pictures

Each VCL control that wishes to be a drop target must be registered as such:

procedure TMyForm.FormCreate(Sender: TObject);
begin
  TClipboard.RegisterDropTargets([imgDropTarget]);
end;

Once that is done standard VCL drag and drop events can be handled to accept a TClipboard source:

procedure TMyForm.imgDropTargetDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := (Source is TClipboard) and TClipboard(Source).HasFormatFor(TPicture);
end;

procedure TMyForm.imgDropTargetDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
  imgDropTarget.Picture.Assign(Source as TClipboard);
end;

Data can be dragged from either internal or external sources, e.g. a picture in a Word document or a PNG file dragged from Explorer.

Making a FMX TImage a drop target for pictures

1) Ensure the form is registered for TClipboard-based drag and drop:

procedure TMyForm.FormCreate(Sender: TObject);
begin
  TClipboard.RegisterForDragAndDrop(Form);
end;

2) Handle standard FMX drag and drop events:

procedure TMyForm.imgDropTargetDragOver(Sender: TObject; 
  const Data: TDragObject; const Point: TPointF; var Operation: TDragOperation);
begin
  if (Data.Source is TClipboard) and TClipboard(Data.Source).HasFormatFor(TBitmap) then
    Operation := TDragOperation.Copy
  else
    Operation := TDragOperation.None;
end;

procedure TMyForm.imgDropTargetDragDrop(Sender: TObject;
  const Data: TDragObject; const Point: TPointF);
begin
  imgDropTarget.Bitmap.Assign(Data.Source as TClipboard);
end;

Demos

For further sample code, check out the demos -