Copy Paste data ke Clipboard

August 10, 2006 at 3:02 pm | Posted in Code Samples, Components, General, Tutorials | 9 Comments

oleh Zamrony P Juhara

Kelas ini saya tulis untuk mengenkapsulasi proses mengkopi custom data dari dan ke clipboard. Saya pernah dihadapkan pada situasi dimana, software yang saya buat harus memiliki fitur untuk copy paste data ke clipboard dengan data berformat tertentu. Setelah googling kesana-sini saya menemukan solusinya dan mulai menulis implementasinya dan mengenkapsulasinya menjadi sebuah kelas.

Kelas enkapsulasi ini diturunkan dari TPersistent. Kelas ini menghandle sebuah internal buffer. Manajemen memorinya diatur internal oleh kelas ini. Agar fleksibel kelas ini hanya bertanggung jawab mengkopi data, bagaimana format data dan cara menginterpretasikan data yang dikopi dari dan ke clipboard ditentukan oleh aplikasi pemanggil.

Implementasi :

daftar unit yg diperlukan:classes,windows,clipbrd

Nama kelas:TClipboardCopier

Method:

  • Create (konstruktor)
  • Destroy (destruktor)

konstruktor dan destruktor berisi kode inisialisasi internal Buffer

  • AssignTo(Dst:TPersistent)
  • Assign(src:TPersistent)

Assign dan AssignTo milik TPersistent di-override agar kelas ini memiliki kemampuan untuk melakukan assignment dari dan ke clipboard. Data yang dikopi diambil dari internal buffer. contoh clipCopier adalah instance TClipboardCopier:

clipCopier.Assign(Clipboard);

otomatis akan mengkopi isi clipboard ke instance clipCopier (operasi paste). Atau sebaliknya,

clipboard.Assign(clipCopier);

otomatis akan mengkopi isi instance clipCopier ke clipboard (operasi copy)

AssignTo seperti TPersistent lingkupnya protected,Assign lingkupnya public

  • AssignToClipboard(Dst:TClipboard)
  • AssignClipboard(src:TClipboard);

AssignToClipboard, AssignClipboard adalah prosedur yang menghandle proses aktual pengkopian data. masing-masing dipanggil oleh AssignTo dan Assign.

  • procedure SaveToStream(Stream:TStream)
  • procedure LoadFromStream(Stream:TStream)

LoadFromStream dan SaveToStream digunakan untuk mengkopi data dari dan ke internal buffer instance kelas TClipboardCopier

Properti:

  • Format :word

Berisi format data clipboard yang dikembalikan oleh fungsi RegisterClipboardFormat() milik windows API. aplikasi harus melakukan registrasi clipboard format terlebih dahulu

Berikut ini listing lengkapnya:

unit uclip;

interface

uses classes,windows,clipbrd;

type TClipboardCopier=class(TPersistent)
     private
       FFormat:word;
       FBuffer:TMemoryStream;
       procedure AssignToClipboard(Dest:TClipboard);
       procedure AssignClipboard(source:TClipboard);
     protected
       procedure AssignTo(Dest:TPersistent);override;
     public
       constructor Create;
       destructor Destroy;override;
       procedure Assign(Source:TPersistent);override;

       procedure SaveToStream(Stream:TStream);
       procedure LoadFromStream(Stream:TStream);
     published
       property Format:word read FFormat write FFormat;
     end;

implementation

constructor TClipboardCopier.Create;
begin
  FBuffer:=TMemoryStream.Create;
end;

destructor TClipboardCopier.Destroy;
begin
  FBuffer.Free;
end;

procedure TClipboardCopier.AssignToClipboard(Dest:TClipboard);
var aDataHandle:THandle;
    aDataPtr:pointer;
begin
  if (Dest=nil) or (FFormat=0) then
    exit;

  //kalo Buffer tidak berisi data langsung keluar
  if FBuffer.Size=0 then
    exit;

 //buka clipboard
  Dest.Open;
  try
    //pastikan posisi stream berada diawal
    FBuffer.Seek(0,soFromBeginning);

    //memori GMEM_DDESHARE digunakan utk clipboard
    aDataHandle:=GlobalAlloc(GMEM_DDESHARE or
 GMEM_MOVEABLE,FBuffer.Size);
    try
      //lock untuk mendapatkan alamat memory handle krn kita
      //menggunakan GMEM_MOVEABLE ketika GlobalAlloc()
      aDataPtr:=GlobalLock(aDataHandle);
      try
        if aDataPtr<>nil then
        begin
          MoveMemory(aDataPtr,FBuffer.Memory,FBuffer.Size);

          //clear isi klipboard
          Dest.Clear;
          //set data. setelah setAsHAndle,
          //aDataHandle dikuasai miliki oleh klipboard. 
          //kita tidak boleh menghapusnya
          Dest.SetAsHandle(FFormat,aDataHandle);
        end;
      finally
        GlobalUnLock(aDataHandle);
      end;
    except
      //kalo gagal bebaskan handle
      GlobalFree(aDataHandle);
    end;
  finally
 //tutup
    Dest.Close;
  end;
end;

procedure TClipboardCopier.AssignClipboard(Source:TClipboard);
var aDataHandle:THandle;
    aDataPtr:pointer;
begin
  if (Source=nil) or (FFormat=0) then
    exit;

  //check klipboard apakah berisi data berformat FFormat.
  if Source.HasFormat(FFormat) then
  begin
    //buka clipboard
    Source.Open;
    try
      //ambil handle ke data klipboard
      aDataHandle:=Source.GetAsHandle(FFormat);

      //lock untuk mendapatkan alamat memory handle krn kita
      //menggunakan GMEM_MOVEABLE ketika GlobalAlloc()
      aDataPtr:=GlobalLock(aDataHandle);
      try
        FBuffer.Clear;
        FBuffer.Size:=GlobalSize(aDataHandle);
        //copy klipboard ke Buffer
        MoveMemory(FBuffer.Memory,aDataPtr,FBuffer.Size);

      finally
        GlobalUnLock(aDataHandle);
      end;
    finally
      //tutup klipboard
      Source.Close;
    end;
  end;
end;

procedure TClipboardCopier.AssignTo(Dest:TPersistent);
begin
  if Dest is TClipboard then
    AssignToClipboard(Dest as TClipboard)
  else
    inherited AssignTo(Dest);
end;

procedure TClipboardCopier.Assign(Source:TPersistent);
begin
  if Source is TClipboard then
    AssignClipboard(Source as TClipboard)
  else
    inherited Assign(Source);
end;

procedure TClipboardCopier.LoadFromStream(Stream:TStream);
begin
  //copy data dari Stream ke Buffer
  FBuffer.Clear;
  if Stream.Size<0 then
    FBuffer.CopyFrom(Stream,Stream.Size);
end;

procedure TClipboardCopier.SaveToStream(Stream:TStream);
begin
  //copy data dari Buffer ke Stream
  //Stream tidak perlu diclear. pemanggil yng wajib
  //mengatur posisi Stream
  FBuffer.Seek(0,soFromBeginning);
  if FBuffer.Size<0 then
    Stream.CopyFrom(FBuffer,FBuffer.Size);
end;

end.

Berikut ini adalah contoh program yg menggunakan kelas TClipboardCopier. Siapkan sebuah form utama berisi tiga buah kontrol TEdit, tiga buah kontrol TLabel dan tiga buah kontrol TButton.

Ubah properti Name masing-masing TEdit menjadi:
edNama
edAlamat
edTelp

Ubah properti Name masing-masing TButton:
btnCut
btnCopy
btnPaste

Ubah properti Caption masing-masing TLabel:
Nama
Alamat
Telp

Listing lengkapnya adalah sebagai berikut:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics,
 Controls, Forms, Dialogs,
  StdCtrls,clipbrd,uclip;

type
  TForm1 = class(TForm)
    edNama: TEdit;
    Label1: TLabel;
    edAlamat: TEdit;
    Label2: TLabel;
    edTelp: TEdit;
    Label3: TLabel;
    btnCut: TButton;
    btnPaste: TButton;
    btnCopy: TButton;
    procedure btnCutClick(Sender: TObject);
    procedure btnCopyClick(Sender: TObject);
    procedure btnPasteClick(Sender: TObject);
  private
    clipCopier:TClipboardCopier;
    { Private declarations }
  public
    constructor Create(AOwner:TComponent);override;
    destructor Destroy;override;
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}
var CF_MyFormat:word;

constructor TForm1.Create(AOwner:TComponent);
begin
  inherited;
  clipCopier:=TClipboardCopier.Create;
  clipCopier.Format:=CF_MyFormat;
end;

destructor TForm1.Destroy;
begin
  clipCopier.Free;
  inherited;
end;

procedure WriteStringToStream(Stream:TStream;const
 str:string);
var len:integer;
begin
  len:=Length(Str);
  Stream.WriteBuffer(len,sizeof(integer));
  Stream.WriteBuffer(Str[1],len);
end;

procedure ReadStringFromStream(Stream:TStream;var
 str:string);
var len:integer;
begin
  Stream.ReadBuffer(len,sizeof(integer));
  SetLength(str,len);
  Stream.ReadBuffer(Str[1],len);
end;

procedure TForm1.btnCutClick(Sender: TObject);
begin
  btnCopyClick(sender);
 //kalau cut, clear edit
  edNama.Text:='';
  edAlamat.Text:='';
  edTelp.Text:='';
end;

procedure TForm1.btnCopyClick(Sender: TObject);
var aData:TMemoryStream;
begin
  adata:=TMemoryStream.Create;
  try
 //simpan data nama,alamant dan telp ke stream
    WriteStringToStream(aData,edNama.Text);
    WriteStringToStream(aData,edAlamat.Text);
    WriteStringToStream(aData,edTelp.Text);
 //kembalikan ke posisi awal
    aData.Seek(0,soFromBeginning);

 //kopi data ke buffer
    ClipCopier.LoadFromStream(aData);
  finally
 //bebaskan karena sudah tidak diperlukan
    adata.Free;
  end;

  Clipboard.Assign(clipCopier);
end;

procedure TForm1.btnPasteClick(Sender: TObject);
var aData:TMemoryStream;
    astr:string;
begin
  clipCopier.Assign(clipBoard);

  adata:=TMemoryStream.Create;
  try
    ClipCopier.SaveToStream(aData);
 //pastikan posisi stream berada diawal
    aData.Seek(0,soFromBeginning);

    ReadStringFromStream(aData,aStr);
    edNama.Text:=aStr;
    ReadStringFromStream(aData,aStr);
    edAlamat.Text:=aStr;
    ReadStringFromStream(aData,aStr);
    edTelp.Text:=aStr;
  finally
    adata.Free;
  end;
end;

initialization
 CF_MyFormat:=RegisterClipboardFormat('TutorialClipboardDelphi');
end.

Untuk mencobanya, isikan teks dimasing-masing edit kontrol, tekan tombol Cut atau Copy untuk mengkopi dan Paste untuk pasting.
Setelah cut atau copy, cobalah exit program dan jalankan lagi program diatas dan tekan Paste. Masing-masing kontrol edit akan diisi teks-teks yang dikopi ke clipboard.

Contoh diatas termasuk sederhana, tapi anda bisa menggunakan TClipboardCopier untuk menghandle data yang lebih kompleks dengan mudah.

Monitoring Isi Clipboard

Jika anda perhatikan, beberapa aplikasi mampu mendeteksi apakah klipboard berisi suatu format tertentu dan mengenable/disable menu Cut, Copy dan Paste sesuai kondisi isi klipboard.

Untuk artikel kedua, kita akan mendiskusikan bagaimana melakukan monitor terhadap isi klipboard. Dengan melakukan proses monitoring kita dapat menentukan apakah isi klipboard telah berubah.

Untuk dapat memonitor clipboard kita harus menciptakan window dan menambahkan handle window aplikasi clipboard chain. Setelah menambahkan handle ke clipboard chain, kita akan dikirimi pesan WM_DRAWCLIPBOARD ketika isi klipboard berubah dan WM_CLIPBOARDCHAIN ketika susunan klipboard chain berubah.

Proses monitoring ini akan kita enkapsulasi sebagai sebuah kelas yang akan menyederhanakan proses menciptakan dan menambahkan window ke clipboard chain.

Kita akan mengimplementasikan dua buah property event yang akan di generate ketika isi klipboard berubah dan ketika susunan klipboard chain berubah.

Implementasi:

unit yg diperlukan: classes,windows,forms,messages

method:

  • Create (konstruktor)
  • Destroy (destruktor)

konstruktor digunakan untuk menciptakan window dan menambahkan handle window ke clipboard chain. Untuk menciptakan window kita akan menggunakan fungsi AllocateHwnd (dideklarasikan di unit classes).
Destruktor digunakan untuk membebaskan
menghapus handle window dari clipboard chain dan
membebaskan handle window.

-WindowProc

window handler yang akan menerima dan menghandle pesan
WM_DRAWCLIPBOARD dan WM_CLIPBOARDCHAIN

property:
-OnClipboardChange bertipe TNotify event. Digenerate
tiap kali isi clipboard berubah.
-OnClipboardChainChange bertipe TNotify event.
Digenerate tiap kali susunan clipboard chain berubah.

Ok listing lengkapnya adalah sebagai berikut:

unit uclipboardviewer;

interface

uses classes,windows,messages,sysutils,clipbrd;

type
{=======================================
 Kelas abstraksi proses monitoring content
 clipboard
 ========================================
 (c) 2005-2006 zamrony p juhara
 ========================================}
      TClipboardViewer=class(TComponent)
      private
         FNextChainView,FHandle:HWND;
         FOnDrawClipboard,FOnClipboardChainChange:TNotifyEvent;
         procedure WMDrawClipboard(var msg:TWMDrawClipboard);
         procedure WMChangeCBChain(var msg:TWMChangeCBChain);
         procedure _OnDrawClipboard(sender:TObject);
         procedure _OnChangeCBChain(sender:TObject);
      protected
         procedure DoDrawClipboard(sender:TObject);virtual;
         procedure DoChangeCBChain(sender:TObject);virtual;
         procedure WndProc(var Msg: TMessage);virtual;
      public
         constructor Create(AOwner:TComponent);override;
         destructor Destroy;override;
      published
 //event ini digenerate ketika isi clipboard berubah
         property OnDrawClipboard:TNotifyEvent read FOnDrawClipboard 
                                    write FOnDrawClipboard;
 //event ini digenerate ketika clipboard viewer chain berubah
         property OnClipboardChainChange:TNotifyEvent 
                                 read FOnClipboardChainChange
                                 write FOnClipboardChainChange;
      end;

implementation

{ TClipboardViewer }
procedure TClipboardViewer.WndProc(var Msg: TMessage);
begin
  case msg.msg of
   WM_DRAWCLIPBOARD:begin
                      WMDrawClipboard(TWMDrawClipboard(msg));
                    end;
   WM_CHANGECBCHAIN:begin
                      WMChangeCBChain(TWMChangeCBChain(msg));
                    end;
   else
    DefWindowProc(FHandle, msg.Msg, msg.wParam, msg.lParam);
  end;
end;

constructor TClipboardViewer.Create;
begin
  inherited Create(AOwner);
  FOnDrawClipboard:=nil;
  FOnClipboardChainChange:=nil;
  FHandle:=classes.AllocateHWnd(WndProc);
  FNextChainView:=SetClipboardViewer(FHandle);
end;

destructor TClipboardViewer.Destroy;
begin
  ChangeClipboardChain(FHandle,FNextChainView);
  classes.DeallocateHwnd(FHandle);
  inherited;
end;

procedure TClipboardViewer.WMDrawClipboard;
begin
  _OnDrawClipboard(self);
  SendMessage(FNextChainView,WM_DRAWCLIPBOARD,0,0);
end;

procedure TClipboardViewer.WMChangeCBChain;
begin
  _OnChangeCBChain(self);
  if FNextChainView<0 then
  begin
    if msg.Remove<FNextChainView then
      SendMessage(FNextChainView,WM_CHANGECBCHAIN,
                    msg.remove,msg.next)
    else
     FNextChainView:=msg.Next;
  end;
  msg.Result:=0;
end;

procedure TClipboardViewer.DoDrawClipboard;
begin
 //default:nothing
end;

procedure TClipboardViewer.DoChangeCBChain;
begin
 //default:nothing
end;

procedure TClipboardViewer._OnDrawClipboard;
begin
  DoDrawClipboard(self);
  if Assigned(FOnDrawClipboard) then
    FOnDrawClipboard(self);
end;

procedure TClipboardViewer._OnChangeCBChain;
begin
  DoChangeCBChain(self);  
  if Assigned(FOnClipboardChainChange) then
    FOnClipboardChainChange(self);
end;

end.

untuk melakukan monitor isi clipboard kita cukup menghandle event OnDrawClipboard. untuk mengetahui status clipboard chain cukup dengan menghandle OnClipboardChainChange.

Jika pada saat event OnDrawClipboard, Clipboard mengandung data format yang kita support, kita enable-kan menu Paste.

Contoh:

procedure TForm1.DoOnDrawClipboard(sender:TObject);
begin
  PasteMenu.Enabled:=Clipboard.HasFormat(CF_MyFormat);
end;

Ok sampai disini soal clipboard. Hepi koding…

9 Comments »

RSS feed for comments on this post. TrackBack URI

  1. Weleh, ada juga program clipboard yang katanya sih buatan orang indo, itu orangnya ngambil kodenya dari sini ya?

    http://sourceforge.net/projects/klipboard/

  2. Saya cuma memberi konfirmasi bahwa artikel ini tidak ada hubungannya dengan project

    http://sourceforge.net/projects/klipboard/

    walaupun sama-sama berurusan dengan clipboard. Tidak ada kode yang diambil dari project tsb diatas.

  3. auto rv trader

  4. Artikelnya bagus. Thanks.

  5. winter olympics of 1972

  6. […] Dig deeper into the topic here […]

  7. Very interesting site. I am proud of my slowly rock I have a fresh joke for you) What is a Mummy’s favorite kind of music? RAGtime music! or wRAP!!!

  8. mohon pencerahannya : klo clipboard monitoring source code nya gmn ya pak? saya sementara buat software kamus, jadi ada fitur ini, fungsinya program akan otomatis meterjemahkan kata yang ada di clipboard. Jadi tinggal melakukan Copy text yang diinginkan, maka kamus akan mencari terjemahnya….makasih pak

    • Untuk format teks, Anda bisa menggunakan CF_TEXT.

      procedure TForm1.DoOnDrawClipboard(sender:TObject);
      begin
      if Clipboard.HasFormat(CF_TEXT) then
      begin
      //translate text…
      end;
      end;


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.
Entries and comments feeds.

%d bloggers like this: