Sign in Join
Wiki copytemporary

Clipboard: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Clipboard

OldNewLine
1+\
2+\
3+
4+## Predefined types
5+
6+| TPredefinedClipboardFormat | MIME type |
7+|----------------------------|----------------------------|
8+| pcfText | text/plain |
9+| pcfBitmap | image/bmp |
10+| pcfPixmap | image/xpm |
11+| pcfIcon | image/lcl.icon |
12+| pcfPicture | image/lcl.picture |
13+| pcfMetaFilePict | image/lcl.metafilepict |
14+| pcfObject | application/lcl.object |
15+| pcfComponent | application/lcl.component |
16+| pcfCustomData | application/lcl.customdata |
17+| | |
18+
19+## Text
20+
21+For use of simple text Clipboard offer property AsText which can be used for reading an writing plain text.
22+
23+Writing text:
24+
25+``` pascal
26+Clipboard.AsText := 'Hello clipboard!';
27+```
28+
29+Reading text:
30+
31+``` pascal
32+ShowMessage('Clipboard content: ' + Clipboard.AsText);
33+```
34+
35+Clipboard is a TClipboard variable and the Clipbrd unit should be added in the uses clause in order to use the variable:
36+
37+``` pascal
38+uses
39+..., Clipbrd;
40+```
41+
42+## Text oriented components
43+
44+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.
45+
46+``` pascal
47+
48+procedure CopyToClipboard;
49+ procedure CutToClipboard;
50+ procedure PasteFromClipboard;
51+```
52+
53+## HTML source
54+
55+The ClipBoard supports reading and writing of [[HTML]].
56+
57+Example of reading from and writing HTML source to clipboard:
58+
59+``` pascal
60+uses
61+ Clipbrd, ...;
62+var
63+ Html, PlainText: String;
64+...
65+begin
66+ Html := ClipBoard.GetAsHtml;
67+ ...
68+ Html := '<b>Formatted</b> text';
69+ PlainText := 'Simple Text';
70+ ClipBoard.SetAsHtml(Html, PlainText);
71+end.
72+```
73+
74+### Windows
75+
76+Handling Html content on the clipboard in Windows requires fidling with headers on Windows.\
77+While in the past users had to do this manually, this now is handled transparently by the ClipBrd unit.\
78+
79+## Image
80+
81+### Load from clipboard
82+
83+``` pascal
84+uses
85+ Clipbrd, LCLIntf, LCLType, ...;
86+
87+procedure LoadBitmapFromClipboard(Bitmap: TBitmap);
88+begin
89+ if Clipboard.HasFormat(PredefinedClipboardFormat(pcfDelphiBitmap)) then
90+ Bitmap.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfDelphiBitmap));
91+ if Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap)) then
92+ Bitmap.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfBitmap));
93+end;
94+```
95+
96+### Save to clipboard
97+
98+``` pascal
99+uses
100+ Clipbrd, ...;
101+
102+procedure SaveBitmapToClipboard(Bitmap: TBitmap);
103+begin
104+ Clipboard.Assign(Bitmap);
105+end;
106+```
107+
108+## Custom format
109+
110+## Multiple objects
111+
112+## Getting notified of changes
113+
114+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](http://wiki.lazarus.freepascal.org/Win32/64_Interface#Processing_non-user_messages_in_your_window)
115+
116+Sample code to implement message handler:
117+
118+``` pascal
119+unit Unit1;
120+
121+{$mode delphi}{$H+}
122+
123+interface
124+
125+uses
126+ Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
127+ Clipbrd, StdCtrls, Windows, Messages;
128+
129+type
130+ TForm1 = class(TForm)
131+ Button1: TButton;
132+ procedure FormCreate(Sender: TObject);
133+ procedure FormDestroy(Sender: TObject);
134+ private
135+ FNextClipboardOwner: HWnd; // handle to the next viewer
136+ // Here are the clipboard event handlers
137+ function WMChangeCBChain(AwParam: WParam; AlParam: LParam):LRESULT;
138+ function WMDrawClipboard(AwParam: WParam; AlParam: LParam):LRESULT;
139+ public
140+ end;
141+
142+var
143+ Form1: TForm1;
144+
145+implementation
146+
147+{$R *.lfm}
148+var
149+ PrevWndProc:windows.WNDPROC;
150+
151+function WndCallback(Ahwnd: HWND; uMsg: UINT; wParam: WParam;
152+ lParam: LParam): LRESULT; stdcall;
153+begin
154+ if uMsg = WM_CHANGECBCHAIN then begin
155+ Result := Form1.WMChangeCBChain(wParam, lParam);
156+ Exit;
157+ end
158+ else if uMsg=WM_DRAWCLIPBOARD then begin
159+ Result := Form1.WMDrawClipboard(wParam, lParam);
160+ Exit;
161+ end;
162+ Result := CallWindowProc(PrevWndProc, Ahwnd, uMsg, WParam, LParam);
163+end;
164+
165+{ TForm1 }
166+
167+{Don't forget to assign to the OnCreate event.}
168+procedure TForm1.FormCreate(Sender: TObject);
169+begin
170+ PrevWndProc := Windows.WNDPROC(SetWindowLongPtr(Self.Handle, GWL_WNDPROC, PtrUInt(@WndCallback)));
171+ FNextClipboardOwner := SetClipboardViewer(Self.Handle);
172+end;
173+
174+{Don't forget to assign to the OnDestroy event.}
175+procedure TForm1.FormDestroy(Sender: TObject);
176+begin
177+ ChangeClipboardChain(Handle, FNextClipboardOwner);
178+end;
179+
180+function TForm1.WMChangeCBChain(AwParam: WParam; AlParam: LParam): LRESULT;
181+var
182+ Remove, Next: THandle;
183+begin
184+ Remove := AwParam;
185+ Next := AlParam;
186+ if FNextClipboardOwner = Remove then FNextClipboardOwner := Next
187+ else if FNextClipboardOwner <> 0 then
188+ SendMessage(FNextClipboardOwner, WM_ChangeCBChain, Remove, Next)
189+ Result := 0;
190+end;
191+
192+function TForm1.WMDrawClipboard(AwParam: WParam; AlParam: LParam): LRESULT;
193+begin
194+ if Clipboard.HasFormat(CF_TEXT) Then Begin
195+ ShowMessage(Clipboard.AsText);
196+ end;
197+ SendMessage(FNextClipboardOwner, WM_DRAWCLIPBOARD, 0, 0); // VERY IMPORTANT
198+ Result := 0;
199+end;
200+
201+end.
202+```
203+
204+## View the clipboard contents
205+
206+Sometimes its useful to see whats actually in the clipboard at any one time. Here are a couple of methods I use, on a form with a TMemo and a one second timer to do just that -
207+
208+``` pascal
209+procedure TForm1.CheckClipboard();
210+var
211+ I : integer;
212+ List : TStringList;
213+begin
214+ memo1.clear;
215+ Memo1.Append('[' + Clipboard.AsText + ']');
216+ List := TStringList.Create;
217+ try
218+ ClipBoard.SupportedFormats(List);
219+ for i := 0 to List.Count-1 do begin
220+ //Memo1.Append(List.Strings[i]); // uncomment to see all available formats
221+ case List.Strings[i] of // show these specific ones
222+ 'Rich Text Format', 'text/plain', 'UTF8_STRING' :
223+ ReadClip(List.Strings[i]);
224+ end;
225+ end;
226+ finally
227+ List.Free;
228+ end;
229+end;
230+
231+function TForm1.ReadClip(TheFormat : ANSIString) : ANSIString;
232+var
233+ Stream: TMemoryStream;
234+ Fmt : TClipboardFormat;
235+ List : TStringList;
236+begin
237+ if TheFormat = '' then exit;
238+ Stream := TMemoryStream.Create;
239+ List := TStringList.Create;
240+ try
241+ if Clipboard.HasFormatName(TheFormat) then begin
242+ Memo1.Append(#10+TheFormat);
243+ Fmt := ClipBoard.FindFormatID(TheFormat);
244+ ClipBoard.GetFormat(Fmt, Stream);
245+ if Stream.Size > 0 then begin
246+ Stream.Seek(0, soFromBeginning);
247+ List.LoadFromStream(Stream);
248+ Memo1.Lines.AddStrings(List, False);
249+ end;
250+ end;
251+ finally
252+ List.Free;
253+ Stream.Free;
254+ end;
255+end;
256+```
257+
258+## How to fix empty GTK2 clipboard on exit
259+
260+Usually when your GTK2 app exits, it's clipboard becomes empty. Bad for usual user. This unit is a dirty fix, add it to "uses" somewhere.
261+
262+``` pascal
263+unit fix_gtk_clipboard;
264+
265+{$mode objfpc}{$H+}
266+
267+interface
268+
269+uses
270+ gtk2, gdk2, Clipbrd;
271+
272+implementation
273+
274+var
275+ c: PGtkClipboard;
276+ t: string;
277+
278+finalization
279+ c := gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
280+ t := Clipboard.AsText;
281+ gtk_clipboard_set_text(c, PChar(t), Length(t));
282+ gtk_clipboard_store(c);
283+end.
284+```
285+
286+In May 2018 I (dbannon) found that putting this bit of code in a finalization section did fix the problem when the clipboard contents had come from the application itself but if it was there before the application started, that is, the application did not write to the clipboard, it introduces another, similar problem. Clipboard contents are, again, cleared in that case. And appears to happen because by time the finalization clause is executed, the clipboard has already been cleared.
287+
288+An easy solution is to put that same code into the main form's onClose event. Its early enough that the contents, from either source are still there and late enough not to be subsequently cleared.
289+
290+``` pascal
291+uses .... {$ifdef LINUX}gtk2, gdk2, Clipbrd{$endif};
292+.....
293+procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
294+var
295+ c: PGtkClipboard;
296+ t: string;
297+begin
298+ {$ifdef LINUX}
299+ c := gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
300+ t := Clipboard.AsText;
301+ gtk_clipboard_set_text(c, PChar(t), Length(t));
302+ gtk_clipboard_store(c);
303+ {$endif}
304+end;
305+```
306+
307+## External links
308+
309+- [Basic Clipboard Operations (Cut/Copy/Paste) using the TClipboard object](http://delphi.about.com/od/vclusing/a/tclipboard.htm)
310+
311+---
312+*From the Free Pascal wiki, page "Clipboard", as it stood on 2020-05-10 after a change by Kegge13, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history