Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
331 lines (236 sloc) 10.4 KB

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

  • Custom formats along with copying and pasting text, components, graphics, files, and URLs are all supported on Windows, OS X and iOS.

  • Delayed rendering, TClipboard-based drag and drop, and virtual files are supported on Windows and OS X, although in the case of the latter Finder only supports virtual files being dragged to it, not pasted as well. For FMX, drag and drop also requires XE7 or later.

  • Change notifications require Vista or later on Windows.

  • Writing multiple instances of the same format is supported generically on OS X and iOS. On Windows it is only supported for files, virtual files and Delphi components; attempting to write the same format multiple times otherwise will just overwrite the previous attempt.

  • At present Android is only nominally supported with a text-only backend that delegates to the standard FMX clipboard code.

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 -

  • VCL: demonstrates a simple clipboard viewer, copying a range of formats, pasting, and drag and drop (Windows)

  • FMX desktop, XE2+: demonstrates the basics of copying and pasting, including a custom format (Windows and OS X)

  • FMX desktop, XE6+: similar the VCL demo, demonstrates a simple clipboard viewer, copying a range of formats, pasting, and drag and drop (Windows and OS X)

  • FMX mobile: mobile version of the XE6+ desktop demo, less drag and drop (iOS)

  • RegisterSimpleClipper and GetObjects: demo of registering and putting to work a default clipper for a TPersistent descendant (Windows, OS X, iOS)

  • Custom record clipper: demonstrates writing a custom clipper for TRectF (Windows, OS X, iOS)

  • Multiple format sets: simple example of NewFormatSet and EnumFormatSets (OS X and iOS)

  • Custom clipboard: demo of a system-managed private clipboard (OS X and iOS)

You can’t perform that action at this time.