main.pas 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. unit main;
  2. interface
  3. uses
  4. DragDrop, DropSource, DragDropFormats,
  5. ActiveX, Windows, Classes, Controls, Forms, StdCtrls, ComCtrls, ExtCtrls;
  6. type
  7. TFormMain = class(TForm)
  8. Timer1: TTimer;
  9. StatusBar1: TStatusBar;
  10. Panel2: TPanel;
  11. DropEmptySource1: TDropEmptySource;
  12. DataFormatAdapterSource: TDataFormatAdapter;
  13. ProgressBar1: TProgressBar;
  14. Panel3: TPanel;
  15. Panel4: TPanel;
  16. Memo2: TMemo;
  17. Panel5: TPanel;
  18. Memo1: TMemo;
  19. Panel6: TPanel;
  20. RadioButtonNormal: TRadioButton;
  21. RadioButtonAsync: TRadioButton;
  22. Memo3: TMemo;
  23. PaintBoxPie: TPaintBox;
  24. procedure Timer1Timer(Sender: TObject);
  25. procedure DropEmptySource1Drop(Sender: TObject; DragType: TDragType;
  26. var ContinueDrop: Boolean);
  27. procedure DropEmptySource1AfterDrop(Sender: TObject;
  28. DragResult: TDragResult; Optimized: Boolean);
  29. procedure DropEmptySource1GetData(Sender: TObject;
  30. const FormatEtc: tagFORMATETC; out Medium: tagSTGMEDIUM;
  31. var Handled: Boolean);
  32. procedure PanelMouseDown(Sender: TObject; Button: TMouseButton;
  33. Shift: TShiftState; X, Y: Integer);
  34. procedure FormCreate(Sender: TObject);
  35. procedure FormDestroy(Sender: TObject);
  36. private
  37. Tick: integer;
  38. EvenOdd: boolean;
  39. procedure OnGetStream(Sender: TFileContentsStreamOnDemandClipboardFormat;
  40. Index: integer; out AStream: IStream);
  41. procedure OnProgress(Sender: TObject; Count, MaxCount: integer);
  42. public
  43. end;
  44. var
  45. FormMain: TFormMain;
  46. implementation
  47. {$R *.DFM}
  48. uses
  49. ShlObj,
  50. Graphics;
  51. const
  52. TestFileSize = 1024*1024*10; // 10Mb
  53. procedure TFormMain.FormCreate(Sender: TObject);
  54. begin
  55. // Setup event handler to let a drop target request data from our drop source.
  56. (DataFormatAdapterSource.DataFormat as TVirtualFileStreamDataFormat).OnGetStream := OnGetStream;
  57. end;
  58. procedure TFormMain.FormDestroy(Sender: TObject);
  59. begin
  60. Timer1.Enabled := False;
  61. end;
  62. procedure TFormMain.PanelMouseDown(Sender: TObject; Button: TMouseButton;
  63. Shift: TShiftState; X, Y: Integer);
  64. begin
  65. StatusBar1.SimpleText := '';
  66. if DragDetectPlus(Handle, Point(X, Y)) then
  67. begin
  68. StatusBar1.SimpleText := 'Dragging data';
  69. // Transfer the file names to the data format. The content will be extracted
  70. // by the target on-demand.
  71. TVirtualFileStreamDataFormat(DataFormatAdapterSource.DataFormat).FileNames.Clear;
  72. TVirtualFileStreamDataFormat(DataFormatAdapterSource.DataFormat).FileNames.Add('big text file.txt');
  73. // Set the size and timestamp attributes of the filename we just added.
  74. with PFileDescriptor(TVirtualFileStreamDataFormat(DataFormatAdapterSource.DataFormat).FileDescriptors[0])^ do
  75. begin
  76. GetSystemTimeAsFileTime(ftLastWriteTime);
  77. nFileSizeLow := TestFileSize;
  78. nFileSizeHigh := 0; // I assume the test file doesn't grow beyond 4Gb...
  79. dwFlags := FD_WRITESTIME or FD_FILESIZE;
  80. end;
  81. // Determine if we should perform an async drag or a normal drag.
  82. if (RadioButtonAsync.Checked) then
  83. begin
  84. // Create a thread to perform the drag...
  85. with TDropSourceThread.Create(DropEmptySource1, False) do
  86. try
  87. // ...and launch it.
  88. Resume;
  89. // Wait for the thread to terminate.
  90. while not Terminated do
  91. Application.ProcessMessages;
  92. finally
  93. Free;
  94. end;
  95. end else
  96. // Perform a normal drag (in the main thread).
  97. DropEmptySource1.Execute;
  98. StatusBar1.SimpleText := 'Drop completed';
  99. end;
  100. end;
  101. procedure TFormMain.Timer1Timer(Sender: TObject);
  102. procedure DrawPie(Percent: integer);
  103. var
  104. Center: TPoint;
  105. Radial: TPoint;
  106. v: Double;
  107. Radius: integer;
  108. begin
  109. // Assume paintbox width is smaller than height.
  110. Radius := PaintBoxPie.Width div 2 - 10;
  111. Center := Point(PaintBoxPie.Width div 2, PaintBoxPie.Height div 2);
  112. v := Percent * Pi / 50; // Convert percent to radians.
  113. Radial.X := Center.X+trunc(Radius * Cos(v));
  114. Radial.Y := Center.Y-trunc(Radius * Sin(v));
  115. PaintBoxPie.Canvas.Brush.Style := bsSolid;
  116. PaintBoxPie.Canvas.Pen.Color := clGray;
  117. PaintBoxPie.Canvas.Pen.Style := psSolid;
  118. if (EvenOdd) then
  119. PaintBoxPie.Canvas.Brush.Color := clRed
  120. else
  121. PaintBoxPie.Canvas.Brush.Color := Color;
  122. PaintBoxPie.Canvas.Pie(Center.X-Radius, Center.Y-Radius,
  123. Center.X+Radius, Center.Y+Radius,
  124. Radial.X, Radial.Y,
  125. Center.X+Radius, Center.Y);
  126. if (Percent <> 0) then
  127. begin
  128. if not(EvenOdd) then
  129. PaintBoxPie.Canvas.Brush.Color := clRed
  130. else
  131. PaintBoxPie.Canvas.Brush.Color := Color;
  132. PaintBoxPie.Canvas.Pie(Center.X-Radius, Center.Y-Radius,
  133. Center.X+Radius, Center.Y+Radius,
  134. Center.X+Radius, Center.Y,
  135. Radial.X, Radial.Y);
  136. end;
  137. end;
  138. begin
  139. // Update the pie to indicate that the application is responding to
  140. // messages (i.e. isn't blocked).
  141. Tick := (Tick + 10) mod 100;
  142. if (Tick = 0) then
  143. EvenOdd := not EvenOdd;
  144. // Draw an animated pie chart to show that application is responsive to events.
  145. DrawPie(Tick);
  146. end;
  147. procedure TFormMain.DropEmptySource1Drop(Sender: TObject;
  148. DragType: TDragType; var ContinueDrop: Boolean);
  149. begin
  150. StatusBar1.SimpleText := 'Target processing drop';
  151. end;
  152. procedure TFormMain.DropEmptySource1AfterDrop(Sender: TObject;
  153. DragResult: TDragResult; Optimized: Boolean);
  154. begin
  155. StatusBar1.SimpleText := 'Drop completed';
  156. end;
  157. procedure TFormMain.DropEmptySource1GetData(Sender: TObject;
  158. const FormatEtc: tagFORMATETC; out Medium: tagSTGMEDIUM;
  159. var Handled: Boolean);
  160. begin
  161. StatusBar1.SimpleText := 'Target reading data';
  162. end;
  163. type
  164. TStreamProgressEvent = procedure(Sender: TObject; Count, MaxCount: integer) of object;
  165. // TFakeStream is a read-only stream which produces its contents on-the-run.
  166. // It is used for this demo so we can simulate transfer of very large and
  167. // arbitrary amounts of data without using any memory.
  168. TFakeStream = class(TStream)
  169. private
  170. FSize, FPosition, FMaxCount: Longint;
  171. FProgress: TStreamProgressEvent;
  172. protected
  173. public
  174. constructor Create(ASize, AMaxCount: LongInt);
  175. function Read(var Buffer; Count: Longint): Longint; override;
  176. function Seek(Offset: Longint; Origin: Word): Longint; override;
  177. procedure SetSize(NewSize: Longint); override;
  178. function Write(const Buffer; Count: Longint): Longint; override;
  179. property OnProgress: TStreamProgressEvent read FProgress write FProgress;
  180. end;
  181. constructor TFakeStream.Create(ASize, AMaxCount: LongInt);
  182. begin
  183. inherited Create;
  184. FSize := ASize;
  185. FMaxCount := AMaxCount;
  186. end;
  187. function TFakeStream.Read(var Buffer; Count: Integer): Longint;
  188. begin
  189. if (FPosition >= 0) and (Count >= 0) then
  190. begin
  191. Result := FSize - FPosition;
  192. if Result > 0 then
  193. begin
  194. if Result > Count then
  195. Result := Count;
  196. if Result > FMaxCount then
  197. Result := FMaxCount;
  198. FillChar(Buffer, Result, ord('X'));
  199. Inc(FPosition, Result);
  200. if Assigned(FProgress) then
  201. FProgress(Self, FPosition, FSize);
  202. Exit;
  203. end;
  204. end;
  205. Result := 0;
  206. end;
  207. function TFakeStream.Seek(Offset: Integer; Origin: Word): Longint;
  208. begin
  209. case Origin of
  210. soFromBeginning: FPosition := Offset;
  211. soFromCurrent: Inc(FPosition, Offset);
  212. soFromEnd: FPosition := FSize + Offset;
  213. end;
  214. if Assigned(FProgress) then
  215. FProgress(Self, FPosition, FMaxCount);
  216. Result := FPosition;
  217. end;
  218. procedure TFakeStream.SetSize(NewSize: Integer);
  219. begin
  220. end;
  221. function TFakeStream.Write(const Buffer; Count: Integer): Longint;
  222. begin
  223. Result := 0;
  224. end;
  225. procedure TFormMain.OnGetStream(
  226. Sender: TFileContentsStreamOnDemandClipboardFormat; Index: integer;
  227. out AStream: IStream);
  228. var
  229. Stream: TStream;
  230. begin
  231. // Note: This method might be called in the context of the transfer thread.
  232. // See TFormMain.OnProgress for a comment on this.
  233. // This event handler is called by TFileContentsStreamOnDemandClipboardFormat
  234. // when the drop target requests data from the drop source (that's us).
  235. StatusBar1.SimpleText := 'Transfering data';
  236. // Create a stream which contains the data we will transfer...
  237. // In this case we just create a dummy stream which contains 10Mb of 'X'
  238. // characters. In order to provide smoth feedback through the progress bar,
  239. // the stream will only transfer up to 1K at a time - Each time TStream.Read
  240. // is called the progress bar is updated via the stream's progress event.
  241. Stream := TFakeStream.Create(TestFileSize, 1024);
  242. try
  243. TFakeStream(Stream).OnProgress := OnProgress;
  244. // ...and return the stream back to the target as an IStream. Note that the
  245. // target is responsible for deleting the stream (via reference counting).
  246. AStream := TFixedStreamAdapter.Create(Stream, soOwned);
  247. except
  248. Stream.Free;
  249. raise;
  250. end;
  251. ProgressBar1.Position := 0;
  252. end;
  253. procedure TFormMain.OnProgress(Sender: TObject; Count, MaxCount: integer);
  254. begin
  255. // Note that during an asyncronous transfer, the progress event handler is
  256. // being called in the context of the transfer thread. This means that this
  257. // event handler should abide to all the normal thread safety rules (i.e.
  258. // don't call GDI or mess with non-thread safe objects).
  259. // Update progress bar to show how much data has been transfered so far.
  260. // This isn't really thread safe since it modifies the form, but so far it
  261. // hasn't crashed on me.
  262. ProgressBar1.Max := MaxCount;
  263. ProgressBar1.Position := Count;
  264. end;
  265. end.