Clipboard

From Lazarus wiki
Revision as of 02:18, 12 March 2015 by Mike.cornflake (talk | contribs) (→‎Windows: Don)
Jump to navigationJump to search

Deutsch (de) English (en) magyar (hu) русский (ru)


Predefined types

TPredefinedClipboardFormat MIME type
pcfText text/plain
pcfBitmap image/bmp
pcfPixmap image/xpm
pcfIcon image/lcl.icon
pcfPicture image/lcl.picture
pcfObject application/lcl.object
pcfComponent application/lcl.component
pcfCustomData application/lcl.customdata
pcfDelphiText text/plain
pcfDelphiBitmap text/delphi.bitmap
pcfDelphiPicture Delphi picture
pcfDelphiMetaFilePict image/delphimetafilepict
pcfDelphiObject application/delphi.object
pcfDelphiComponent Delphi component
pcfKylixPicture image/delphi.picture
pcfKylixBitmap image/delphi.bitmap
pcfKylixDrawing image/delphi.drawing
pcfKylixComponent application/delphi.component

Text

For use of simple text Clipboard offer property AsText which can be used for reading an writing plain text.

Writing text:

Clipboard.AsText := 'Hello clipboard!';

Reading text:

ShowMessage('Clipboard content: ' + Clipboard.AsText);

Clipboard is a TClipboard variable and the Clipbrd unit should be added in the uses clause in order to use the variable:

uses
..., Clipbrd;

Text oriented components

Some visual components like TEdit, TMemo, TStringGrid, TLabeledEdit, TMaskEdit, TSpinEdit and TFloatSpinEdit have ability to select a part of contained text and offer additional functionality for handling clipboard operations on selected text.

  procedure CopyToClipboard; 
  procedure CutToClipboard; 
  procedure PasteFromClipboard;

HTML source

Example of reading HTML source from clipboard:

uses
  Clipbrd, ...;

procedure InsertHTMLSourceFromClipboard(Strings: TString);
var
  Fid: TClipboardFormat
  Str: WideString;
  Stream: TMemoryStream;
begin
  Fid := Clipboard.FindFormatID('text/html');
  if Fid <> 0 then 
  try
    Stream := TMemoryStream.Create;
    if Clipboard.GetFormat(Fid, Stream) then 
    begin
      Stream.Write(#0#0, Length(#0#0));
      Stream.Position := 0;
      Str := PWideChar(Stream.Memory);     
      Strings.Text := UTF8Encode(Str);
    end;
  finally
    Stream.Free;
  end;
end;

Write html source to clipboard. This can be done with AddFormat either using TStream or memory Buffer.

// register the mime type for text/html. You can do this once at program start:
  ClipbrdFmtHTML := RegisterClipboardFormat('text/html');
...
  // Clear any previous formats off the clipboard before starting
  Clipboard.Clear;

  // put text and html on the clipboard. Other applications will choose the best format automatically.
  ThePlainUTF8Text := 'Simple text';
  Clipboard.AsText := ThePlainUTF8Text; 

  HTMLSource := '<b>Formatted</b> text'; // text with formatrings
  Clipboard.AddFormat(ClipbrdFmtHTML, HTMLSource[1], Length(HTMLSource));

Windows

Not all applications support the 'text/html' mime type. Microsoft operating systems and applications instead support 'HTML Format' and the required contents of the clipboard are specified here:

https://msdn.microsoft.com/en-us/library/aa767917%28v=vs.85%29.aspx

(if URL changes, Document title: HTML Clipboard Format, and is part of the MSHTML Editing documentation)

This specification contains the requirement for the header to specify the length of the header, which can be annoying to calculate given that the count itself changes the number of characters. The easiest way to resolve this is to the output the various counts and positions as 10 digits, and then use leading zeros as shown below.

procedure AddHTMLToClipboard(AHTML: String);
const
  HTML_MIME = 'HTML Format';
  NATIVEHEADER = 'Version:0.9' + #13#10 +
                 'StartHTML:%.10d' + #13#10+
                 'EndHTML:%.10d' + #13#10+
                 'StartFragment:%.10d' + #13#10+
                 'EndFragment:%.10d' + #13#10;
  HEADER = '<html><head></head><body><!--StartFragment-->';
  FOOTER1 = '<!--EndFragment-->';
  FOOTER2 = '</body></html>';
var
  cfHTMLFormat: TClipboardFormat;
  HTMLSource : String;
  iStartHTML: Integer;
  iStartFragment: Integer;
  iEndFragment: Integer;
  iEndHTML: Integer;

begin
  // Ensure the 'HTML Format' mime type is registered
  cfHTMLFormat := Clipboard.FindFormatID(HTML_MIME);
  If cfHTMLFormat = 0 Then
    cfHTMLFormat := RegisterClipboardFormat(HTML_MIME);

  iStartHTML := 105;
  iStartFragment := iStartHTML + Length(HEADER);
  iEndFragment := iStartFragment + Length(AHTML) + Length(FOOTER1);
  iEndHTML := iEndFragment + LENGTH(FOOTER2);

  // insert the native header and opening html tags at the start of the string
  HTMLSource := Format(NATIVEHEADER, [iStartHTML, iEndHTML, iStartFragment,
    iEndFragment]) + HEADER + AHTML + FOOTER1 + FOOTER2;

  Clipboard.AddFormat(cfHTMLFormat, HTMLSource[1], Length(HTMLSource));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Clipboard.Clear;

  Clipboard.AsText:='Simple text';

  AddHTMLToClipboard('<b>Bold text</b>');
end;

As the Clipboard can support multiple formats, you can place SimpleText, 'text/html' and 'HTML Format' items at the same time...

Image

Load from clipboard

uses 
  Clipbrd, LCLIntf, LCLType, ...;

procedure LoadBitmapFromClipboard(Bitmap: TBitmap);
begin
  if Clipboard.HasFormat(PredefinedClipboardFormat(pcfDelphiBitmap)) then
    Bitmap.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfDelphiBitmap));
  if Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap)) then
    Bitmap.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfBitmap));
end;

Save to clipboard

uses 
  Clipbrd, ...;

procedure SaveBitmapToClipboard(Bitmap: TBitmap);
begin
  Clipboard.Assign(Bitmap);
end;

Custom format

Multiple objects

Getting notified of changes

The LCL does not pass on Windows messages. It only passes on messages > WM_USER. This means you have to write your own message handler. Processing non - user messages in your window

Sample code to implement message handler:

unit Unit1;

{$mode delphi}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  Clipbrd, StdCtrls, Windows, Messages;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FNextClipboardOwner: HWnd;   // handle to the next viewer
    // Here are the clipboard event handlers
    function WMChangeCBChain(wParam: WParam; lParam: LParam):LRESULT;
    function WMDrawClipboard(wParam: WParam; lParam: LParam):LRESULT;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}
var
  PrevWndProc:windows.WNDPROC;

function WndCallback(Ahwnd: HWND; uMsg: UINT; wParam: WParam;
  lParam: LParam): LRESULT; stdcall;
begin
  if uMsg = WM_CHANGECBCHAIN then begin
    Result := Form1.WMChangeCBChain(wParam, lParam);
    Exit;
  end 
  else if uMsg=WM_DRAWCLIPBOARD then begin
    Result := Form1.WMDrawClipboard(wParam, lParam);
    Exit;
  end;
  Result := CallWindowProc(PrevWndProc, Ahwnd, uMsg, WParam, LParam);
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  PrevWndProc := Windows.WNDPROC(SetWindowLong(Self.Handle, GWL_WNDPROC, PtrInt(@WndCallback)));
  FNextClipboardOwner := SetClipboardViewer(Self.Handle);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ChangeClipboardChain(Handle, FNextClipboardOwner);
end;

function TForm1.WMChangeCBChain(wParam: WParam; lParam: LParam): LRESULT;
var
  Remove, Next: THandle;
begin
  Remove := WParam;
  Next := LParam;
  if FNextClipboardOwner = Remove then FNextClipboardOwner := Next
    else if FNextClipboardOwner <> 0 then
      SendMessage(FNextClipboardOwner, WM_ChangeCBChain, Remove, Next)
end;

function TForm1.WMDrawClipboard(wParam: WParam; lParam: LParam): LRESULT;
begin
  if Clipboard.HasFormat(CF_TEXT) Then Begin
    ShowMessage(Clipboard.AsText);
  end;
  SendMessage(FNextClipboardOwner, WM_DRAWCLIPBOARD, 0, 0);   // VERY IMPORTANT
  Result := 0;
end;

end.

External links