Main.pas 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. unit Main;
  2. interface
  3. uses
  4. DragDrop, DropSource, DropTarget, DragDropFormats, ActiveX,
  5. Windows, Classes, Controls, Forms, ExtCtrls, StdCtrls, ComCtrls;
  6. type
  7. ////////////////////////////////////////////////////////////////////////////////
  8. //
  9. // TFormMain
  10. //
  11. ////////////////////////////////////////////////////////////////////////////////
  12. TFormMain = class(TForm)
  13. DropDummy1: TDropDummy;
  14. Panel1: TPanel;
  15. ListView1: TListView;
  16. Panel2: TPanel;
  17. Memo1: TMemo;
  18. DropEmptySource1: TDropEmptySource;
  19. DropEmptyTarget1: TDropEmptyTarget;
  20. DataFormatAdapterSource: TDataFormatAdapter;
  21. DataFormatAdapterTarget: TDataFormatAdapter;
  22. procedure OnMouseDown(Sender: TObject; Button: TMouseButton;
  23. Shift: TShiftState; X, Y: Integer);
  24. procedure FormCreate(Sender: TObject);
  25. procedure DropEmptyTarget1Drop(Sender: TObject; ShiftState: TShiftState;
  26. Point: TPoint; var Effect: Integer);
  27. procedure DropEmptyTarget1Enter(Sender: TObject;
  28. ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
  29. procedure DropEmptySource1AfterDrop(Sender: TObject;
  30. DragResult: TDragResult; Optimized: Boolean);
  31. private
  32. procedure OnGetStream(Sender: TFileContentsStreamOnDemandClipboardFormat;
  33. Index: integer; out AStream: IStream);
  34. public
  35. end;
  36. var
  37. FormMain: TFormMain;
  38. implementation
  39. {$R *.DFM}
  40. ////////////////////////////////////////////////////////////////////////////////
  41. //
  42. // TFormMain
  43. //
  44. ////////////////////////////////////////////////////////////////////////////////
  45. procedure TFormMain.FormCreate(Sender: TObject);
  46. begin
  47. // Setup event handler to let a drop target request data from our drop source.
  48. (DataFormatAdapterSource.DataFormat as TVirtualFileStreamDataFormat).OnGetStream := OnGetStream;
  49. end;
  50. procedure TFormMain.OnMouseDown(Sender: TObject; Button: TMouseButton;
  51. Shift: TShiftState; X, Y: Integer);
  52. var
  53. i: integer;
  54. begin
  55. if DragDetectPlus(Handle, Point(X,Y)) then
  56. begin
  57. // Transfer the file names to the data format. The content will be extracted
  58. // by the target on-demand.
  59. TVirtualFileStreamDataFormat(DataFormatAdapterSource.DataFormat).FileNames.Clear;
  60. for i := 0 to ListView1.Items.Count-1 do
  61. TVirtualFileStreamDataFormat(DataFormatAdapterSource.DataFormat).FileNames.Add(ListView1.Items[i].Caption);
  62. // ...and let it rip!
  63. DropEmptySource1.Execute;
  64. end;
  65. end;
  66. procedure TFormMain.DropEmptyTarget1Drop(Sender: TObject;
  67. ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
  68. var
  69. OldCount: integer;
  70. Item: TListItem;
  71. s: string;
  72. p: PChar;
  73. i: integer;
  74. Stream: IStream;
  75. StatStg: TStatStg;
  76. Total, BufferSize, Chunk, Size: longInt;
  77. FirstChunk: boolean;
  78. const
  79. MaxBufferSize = 32*1024; // 32Kb
  80. begin
  81. // Transfer the file names and contents from the data format.
  82. if (TVirtualFileStreamDataFormat(DataFormatAdapterTarget.DataFormat).FileNames.Count > 0) then
  83. begin
  84. ListView1.Items.BeginUpdate;
  85. try
  86. // Note: Since we can actually drag and drop from and onto ourself, we
  87. // can't clear the ListView until the data has been read from the listview
  88. // (by the source) and inserted into it again (by the target). To
  89. // accomplish this, we add the dropped items to the list first and then
  90. // delete the old items afterwards.
  91. // Another, and more common, approach would be to reject or disable drops
  92. // onto ourself while we are performing drag/drop operations.
  93. OldCount := ListView1.Items.Count;
  94. for i := 0 to TVirtualFileStreamDataFormat(DataFormatAdapterTarget.DataFormat).FileNames.Count-1 do
  95. begin
  96. Item := ListView1.Items.Add;
  97. Item.Caption := TVirtualFileStreamDataFormat(DataFormatAdapterTarget.DataFormat).FileNames[i];
  98. // Get data stream from source.
  99. Stream := TVirtualFileStreamDataFormat(DataFormatAdapterTarget.DataFormat).FileContentsClipboardFormat.GetStream(i);
  100. if (Stream <> nil) then
  101. begin
  102. // Read data from stream.
  103. Stream.Stat(StatStg, STATFLAG_NONAME);
  104. Total := StatStg.cbSize;
  105. // Assume that stream is at EOF, so set it to BOF.
  106. // See comment in TCustomSimpleClipboardFormat.DoSetData (in
  107. // DragDropFormats.pas) for an explanation of this.
  108. Stream.Seek(0, STREAM_SEEK_SET, PLargeuint(nil)^);
  109. // If a really big hunk of data has been dropped on us we display a
  110. // small part of it since there isn't much point in trying to display
  111. // it in the limted space we have available.
  112. // Additionally, it would be *really* bad for performce if we tried to
  113. // allocated a too big buffer and read sequentially into it. Tests has
  114. // shown that allocating a 10Mb buffer and trying to read data into it
  115. // in 1Kb chunks takes several minutes, while the same data can be
  116. // read into a 32Kb buffer in 1Kb chunks in seconds. The Windows
  117. // explorer uses a 1 Mb buffer, but that's too big for this demo.
  118. // Thes above tests were performed using the AsyncSource demo.
  119. BufferSize := Total;
  120. if (BufferSize > MaxBufferSize) then
  121. BufferSize := MaxBufferSize;
  122. SetLength(s, BufferSize);
  123. p := PChar(s);
  124. Chunk := BufferSize;
  125. FirstChunk := True;
  126. while (Total > 0) do
  127. begin
  128. Stream.Read(p, Chunk, @Size);
  129. if (Size = 0) then
  130. break;
  131. inc(p, Size);
  132. dec(Total, Size);
  133. dec(Chunk, Size);
  134. if (Chunk = 0) or (Total = 0) then
  135. begin
  136. // Display a small fraction of the first chunk.
  137. if (FirstChunk) then
  138. Item.SubItems.Add(copy(s, 1, 1024));
  139. p := PChar(s);
  140. // In a real-world application we would write the buffer to disk
  141. // now. E.g.:
  142. // FileStream.WriteBuffer(p^, BufferSize-Chunk);
  143. Chunk := BufferSize;
  144. FirstChunk := False;
  145. end;
  146. end;
  147. // Display a small fraction of the first chunk.
  148. if (FirstChunk) then
  149. Item.SubItems.Add(copy(s, 1, 1024));
  150. end else
  151. Item.SubItems.Add('***failed to read content***');
  152. end;
  153. // Delete the old items.
  154. for i := OldCount-1 downto 0 do
  155. ListView1.Items.Delete(i);
  156. finally
  157. ListView1.Items.EndUpdate;
  158. end;
  159. end;
  160. end;
  161. procedure TFormMain.DropEmptyTarget1Enter(Sender: TObject;
  162. ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
  163. begin
  164. // Reject the drop unless the source supports *both* the FileContents and
  165. // FileGroupDescriptor formats in the storage medium we require (IStream).
  166. // Normally a drop is accepted if just one of our formats is supported.
  167. if (DropEmptyTarget1.DataObject.QueryGetData(
  168. TVirtualFileStreamDataFormat(DataFormatAdapterTarget.DataFormat).FileContentsClipboardFormat.FormatEtc) <> S_OK) or
  169. (DropEmptyTarget1.DataObject.QueryGetData(
  170. TVirtualFileStreamDataFormat(DataFormatAdapterTarget.DataFormat).FileGroupDescritorClipboardFormat.FormatEtc) <> S_OK) then
  171. Effect := DROPEFFECT_NONE;
  172. end;
  173. procedure TFormMain.DropEmptySource1AfterDrop(Sender: TObject;
  174. DragResult: TDragResult; Optimized: Boolean);
  175. begin
  176. // Clear the listview if items were moved.
  177. // Note: If we drag-move from and drop onto ourself, this would cause the
  178. // listview to clear after we have successfully transfered the data. To avoid
  179. // this (and to avoid files being accidentally deleted), our drop target
  180. // doesn't accept move operations. If you wan't it to be able to accept move
  181. // operations, you'll have to avoid the above situation somehow. I'll leave it
  182. // up to you to figure out how to do that.
  183. if (DragResult = drDropMove) then
  184. ListView1.Items.Clear;
  185. end;
  186. procedure TFormMain.OnGetStream(Sender: TFileContentsStreamOnDemandClipboardFormat;
  187. Index: integer; out AStream: IStream);
  188. var
  189. Stream: TMemoryStream;
  190. s: string;
  191. begin
  192. // This event handler is called by TFileContentsStreamOnDemandClipboardFormat
  193. // when the drop target requests data from the drop source (that's us).
  194. Stream := TMemoryStream.Create;
  195. try
  196. // Write the file contents to a regular stream...
  197. s := ListView1.Items[Index].SubItems[0];
  198. Stream.Write(PChar(s)^, Length(s));
  199. Stream.Position := 0;
  200. // ...and return the stream back to the target as an IStream. Note that the
  201. // target is responsible for deleting the stream (via reference counting).
  202. AStream := TFixedStreamAdapter.Create(Stream, soOwned);
  203. except
  204. Stream.Free;
  205. raise;
  206. end;
  207. end;
  208. end.