Subversion Repositories decoder

Rev

Blame | Last modification | View Log | RSS feed

  1. unit DropPIDLSource;
  2.  
  3. // -----------------------------------------------------------------------------
  4. // Project:         Drag and Drop Component Suite
  5. // Component Names: TDropPIDLSource
  6. // Module:          DropPIDLSource
  7. // Description:     Implements Dragging & Dropping of PIDLs
  8. //                  FROM your application to another.
  9. // Version:         3.7
  10. // Date:            22-JUL-1999
  11. // Target:          Win32, Delphi 3 - Delphi 5, C++ Builder 3, C++ Builder 4
  12. // Authors:         Angus Johnson,   ajohnson@rpi.net.au
  13. //                  Anders Melander, anders@melander.dk
  14. //                                   http://www.melander.dk
  15. // Copyright        © 1997-99 Angus Johnson & Anders Melander
  16. // -----------------------------------------------------------------------------
  17.  
  18. interface
  19.  
  20. uses
  21.   DropSource,
  22.   Classes, ActiveX, ShlObj;
  23.  
  24. {$include DragDrop.inc}
  25.  
  26. type
  27.   TDropPIDLSource = class(TDropSource)
  28.   private
  29.     fPIDLs: TStrings; //NOTE: contains folder PIDL as well as file PIDLs
  30.     fMappedNames: TStrings;
  31.     function GetFilename(index: integer): string; //used internally
  32.     procedure SetMappedNames(names: TStrings);
  33.   protected
  34.     function DoGetData(const FormatEtcIn: TFormatEtc;
  35.       OUT Medium: TStgMedium):HRESULT; Override;
  36.     function CutOrCopyToClipboard: boolean; Override;
  37.   public
  38.     constructor Create(aOwner: TComponent); Override;
  39.     destructor Destroy; Override;
  40.     procedure CopyFolderPidlToList(pidl: PItemIDList);
  41.     procedure CopyFilePidlToList(pidl: PItemIDList);
  42.     property MappedNames: TStrings read fMappedNames write SetMappedNames;
  43.   end;
  44.  
  45. procedure Register;
  46.  
  47. //Exposed as also used by DropPIDLTarget...
  48. function PidlToString(pidl: PItemIDList): String;
  49. function JoinPidlStrings(pidl1,pidl2: string): String;
  50.  
  51. implementation
  52.  
  53. uses
  54.   Windows,
  55.   SysUtils,
  56.   ClipBrd;
  57.  
  58. procedure Register;
  59. begin
  60.   RegisterComponents('DragDrop', [TDropPIDLSource]);
  61. end;
  62.  
  63. // -----------------------------------------------------------------------------
  64. //                      Miscellaneous Functions...
  65. // -----------------------------------------------------------------------------
  66.  
  67. function GetSizeOfPidl(pidl: PItemIDList): integer;
  68. var
  69.   i: integer;
  70. begin
  71.   result := SizeOf(Word);
  72.   repeat
  73.     i := pSHItemID(pidl)^.cb;
  74.     inc(result,i);
  75.     inc(longint(pidl),i);
  76.   until i = 0;
  77. end;
  78. // -----------------------------------------------------------------------------
  79.  
  80. function PidlToString(pidl: PItemIDList): String;
  81. var
  82.   PidlLength: integer;
  83. begin
  84.   PidlLength := GetSizeOfPidl(pidl);
  85.   setlength(result,PidlLength);
  86.   Move(pidl^,pchar(result)^,PidlLength);
  87. end;
  88. // -----------------------------------------------------------------------------
  89.  
  90. function JoinPidlStrings(pidl1,pidl2: string): String;
  91. var
  92.   PidlLength: integer;
  93. begin
  94.   if Length(pidl1) <= 2 then PidlLength := 0
  95.   else PidlLength := Length(pidl1)-2;
  96.   setlength(result,PidlLength+length(pidl2));
  97.   if PidlLength > 0 then Move(pidl1[1],result[1],PidlLength);
  98.   Move(pidl2[1],result[PidlLength+1],length(pidl2));
  99. end;
  100.  
  101. // -----------------------------------------------------------------------------
  102. //                      TDropPIDLSource
  103. // -----------------------------------------------------------------------------
  104.  
  105. constructor TDropPIDLSource.Create(aOwner: TComponent);
  106. begin
  107.   inherited Create(aOwner);
  108.   fPIDLs := TStringList.create;
  109.   fMappedNames := TStringList.Create;
  110.   AddFormatEtc(CF_HDROP, NIL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
  111.   AddFormatEtc(CF_IDLIST, NIL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
  112.   AddFormatEtc(CF_PREFERREDDROPEFFECT, NIL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
  113.   AddFormatEtc(CF_FILENAMEMAP, NIL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
  114.   AddFormatEtc(CF_FILENAMEMAPW, NIL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
  115. end;
  116. // -----------------------------------------------------------------------------
  117.  
  118. destructor TDropPIDLSource.Destroy;
  119. begin
  120.   fPIDLs.free;
  121.   fMappedNames.free;
  122.   inherited Destroy;
  123. end;
  124. // -----------------------------------------------------------------------------
  125.  
  126. //this function is used internally by DoGetData()...
  127. function TDropPIDLSource.GetFilename(index: integer): string;
  128. var
  129.   PidlStr: string;
  130.   buff: array [0..MAX_PATH] of char;
  131. begin
  132.   if (index < 1) or (index >= fPIDLs.count) then result := ''
  133.   else
  134.   begin
  135.     PidlStr := JoinPidlStrings(fPIDLs[0], fPIDLs[index]);
  136.     SHGetPathFromIDList(PItemIDList(pChar(PidlStr)),buff);
  137.     result := buff;
  138.   end;
  139. end;
  140. // -----------------------------------------------------------------------------
  141.  
  142. //Note: Once the PIDL has been copied into the list it can be 'freed'.
  143. procedure TDropPIDLSource.CopyFolderPidlToList(pidl: PItemIDList);
  144. begin
  145.   fPIDLs.clear;
  146.   fMappedNames.clear;
  147.   fPIDLs.add(PidlToString(pidl));
  148. end;
  149. // -----------------------------------------------------------------------------
  150.  
  151. //Note: Once the PIDL has been copied into the list it can be 'freed'.
  152. procedure TDropPIDLSource.CopyFilePidlToList(pidl: PItemIDList);
  153. begin
  154.   if fPIDLs.count < 1 then exit; //no folder pidl has been added!
  155.   fPIDLs.add(PidlToString(pidl));
  156. end;
  157. // -----------------------------------------------------------------------------
  158.  
  159. procedure TDropPIDLSource.SetMappedNames(names: TStrings);
  160. begin
  161.   fMappedNames.assign(names);
  162. end;
  163. // -----------------------------------------------------------------------------
  164.  
  165. function TDropPIDLSource.CutOrCopyToClipboard: boolean;
  166. var
  167.   FormatEtcIn: TFormatEtc;
  168.   Medium: TStgMedium;
  169. begin
  170.   FormatEtcIn.cfFormat := CF_IDLIST;
  171.   FormatEtcIn.dwAspect := DVASPECT_CONTENT;
  172.   FormatEtcIn.tymed := TYMED_HGLOBAL;
  173.   if (fPIDLs.count < 2) then result := false
  174.   else if GetData(formatetcIn,Medium) = S_OK then
  175.   begin
  176.     Clipboard.SetAsHandle(CF_IDLIST,Medium.hGlobal);
  177.     result := true;
  178.   end else result := false;
  179. end;
  180. // -----------------------------------------------------------------------------
  181.  
  182. function TDropPIDLSource.DoGetData(const FormatEtcIn: TFormatEtc;
  183.   OUT Medium: TStgMedium):HRESULT;
  184. var
  185.   i, MemSpace, CidaSize, Offset, StrLength: integer;
  186.   pCIDA: PIDA;
  187.   pInt: ^UINT;
  188.   pOffset: PChar;
  189.   DropEffect: ^DWORD;
  190.   dropfiles: pDropFiles;
  191.   fFiles: string;
  192.   pFileList: PChar;
  193.   pFileW: PWideChar;
  194. begin
  195.   Medium.tymed := 0;
  196.   Medium.UnkForRelease := NIL;
  197.   Medium.hGlobal := 0;
  198.   if fPIDLs.count < 2 then result := E_UNEXPECTED
  199.   //--------------------------------------------------------------------------
  200.   else if (FormatEtcIn.cfFormat = CF_HDROP) and
  201.     (FormatEtcIn.dwAspect = DVASPECT_CONTENT) and
  202.     (FormatEtcIn.tymed and TYMED_HGLOBAL <> 0) then
  203.   begin
  204.     fFiles := '';
  205.     for i := 1 to fPIDLs.Count-1 do
  206.       AppendStr(fFiles,GetFilename(i)+#0);
  207.     AppendStr(fFiles,#0);
  208.  
  209.     Medium.hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_ZEROINIT,
  210.         SizeOf(TDropFiles)+length(fFiles));
  211.     if (Medium.hGlobal = 0) then
  212.       result:=E_OUTOFMEMORY
  213.     else
  214.     begin
  215.       Medium.tymed := TYMED_HGLOBAL;
  216.       dropfiles := GlobalLock(Medium.hGlobal);
  217.       try
  218.         dropfiles^.pfiles := SizeOf(TDropFiles);
  219.         dropfiles^.fwide := False;
  220.         longint(pFileList) := longint(dropfiles)+SizeOf(TDropFiles);
  221.         move(fFiles[1],pFileList^,length(fFiles));
  222.       finally
  223.         GlobalUnlock(Medium.hGlobal);
  224.       end;
  225.       result := S_OK;
  226.     end;
  227.   end
  228.   //--------------------------------------------------------------------------
  229.   else if (FormatEtcIn.cfFormat = CF_FILENAMEMAP) and
  230.     (FormatEtcIn.dwAspect = DVASPECT_CONTENT) and
  231.     (FormatEtcIn.tymed and TYMED_HGLOBAL <> 0) and
  232.     //make sure there is a Mapped Name for each filename...
  233.     (fMappedNames.Count = fPidls.Count-1) then
  234.   begin
  235.     strlength := 0;
  236.     for i := 0 to fMappedNames.Count-1 do
  237.       Inc(strlength, Length(fMappedNames[i])+1);
  238.  
  239.     Medium.hGlobal :=
  240.       GlobalAlloc(GMEM_SHARE or GMEM_ZEROINIT, strlength+1);
  241.     if (Medium.hGlobal = 0) then
  242.       result:=E_OUTOFMEMORY
  243.     else
  244.     begin
  245.       Medium.tymed := TYMED_HGLOBAL;
  246.       pFileList := GlobalLock(Medium.hGlobal);
  247.       try
  248.         for i := 0 to fMappedNames.Count-1 do
  249.         begin
  250.           StrPCopy(pFileList,fMappedNames[i]);
  251.           Inc(pFileList, Length(fMappedNames[i])+1);
  252.         end;
  253.         pFileList^ := #0;
  254.       finally
  255.         GlobalUnlock(Medium.hGlobal);
  256.       end;
  257.       result := S_OK;
  258.     end;
  259.   end
  260.   //--------------------------------------------------------------------------
  261.   else if (FormatEtcIn.cfFormat = CF_FILENAMEMAPW) and
  262.     (FormatEtcIn.dwAspect = DVASPECT_CONTENT) and
  263.     (FormatEtcIn.tymed and TYMED_HGLOBAL <> 0) and
  264.     //make sure there is a Mapped Name for each filename...
  265.     (fMappedNames.Count = fPidls.Count-1) then
  266.   begin
  267.     strlength := 2;
  268.     for i := 0 to fMappedNames.Count-1 do
  269.       Inc(strlength, (Length(fMappedNames[i])+1)*2);
  270.  
  271.     Medium.hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_ZEROINIT, strlength);
  272.     if (Medium.hGlobal = 0) then
  273.       result:=E_OUTOFMEMORY
  274.     else
  275.     begin
  276.       Medium.tymed := TYMED_HGLOBAL;
  277.       pFileW := GlobalLock(Medium.hGlobal);
  278.       try
  279.         for i := 0 to fMappedNames.Count-1 do
  280.         begin
  281.           StringToWideChar(fMappedNames[i],
  282.               pFileW, (length(fMappedNames[i])+1)*2);
  283.           Inc(pFileW, Length(fMappedNames[i])+1);
  284.         end;
  285.       pFileW^ := #0;
  286.       finally
  287.         GlobalUnlock(Medium.hGlobal);
  288.       end;
  289.       result := S_OK;
  290.     end;
  291.   end
  292.   //--------------------------------------------------------------------------
  293.   else if (FormatEtcIn.cfFormat = CF_IDLIST) and
  294.     (FormatEtcIn.dwAspect = DVASPECT_CONTENT) and
  295.     (FormatEtcIn.tymed and TYMED_HGLOBAL <> 0) then
  296.   begin
  297.     CidaSize := sizeof(UINT)*(1+fPIDLs.Count); //size of CIDA structure
  298.     MemSpace := CidaSize;
  299.     for i := 0 to fPIDLs.Count-1 do
  300.       Inc(MemSpace, Length(fPIDLs[i]));
  301.     Medium.hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_ZEROINIT, MemSpace);
  302.  
  303.     if (Medium.hGlobal = 0) then
  304.       result := E_OUTOFMEMORY
  305.     else
  306.     begin
  307.       medium.tymed := TYMED_HGLOBAL;
  308.       pCIDA := PIDA(GlobalLock(Medium.hGlobal));
  309.       try
  310.         pCIDA^.cidl := fPIDLs.count-1; //don't count folder
  311.         pInt := @(pCIDA^.aoffset); //points to aoffset[0];
  312.         pOffset := pChar(pCIDA);
  313.         //move pOffset to point to the Folder PIDL location
  314.         inc(pOffset,CidaSize);
  315.         offset := CidaSize;
  316.         for i := 0 to fPIDLs.Count-1 do
  317.         begin
  318.           pInt^ := offset; //store 'offset' into aoffset[i]
  319.           //copy the PIDL into pOffset
  320.           Move(pointer(fPIDLs[i])^,pOffset^,length(fPIDLs[i]));
  321.           //increase 'offset' by the size of the last pidl
  322.           inc(offset,length(fPIDLs[i]));
  323.           inc(pInt); //increment the aoffset pointer
  324.           //move pOffset ready for the next PIDL
  325.           inc(pOffset,length(fPIDLs[i]));
  326.         end;
  327.       finally
  328.         GlobalUnlock(Medium.hGlobal);
  329.       end;
  330.      result := S_OK;
  331.     end;
  332.   end
  333.   //--------------------------------------------------------------------------
  334.   else if (FormatEtcIn.cfFormat = CF_PREFERREDDROPEFFECT) and
  335.     (FormatEtcIn.dwAspect = DVASPECT_CONTENT) and
  336.     (FormatEtcIn.tymed and TYMED_HGLOBAL <> 0) then
  337.   begin
  338.     Medium.tymed := TYMED_HGLOBAL;
  339.     Medium.hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_ZEROINIT, SizeOf(DWORD));
  340.     if Medium.hGlobal = 0 then
  341.       result:=E_OUTOFMEMORY
  342.     else
  343.     begin
  344.       DropEffect := GlobalLock(Medium.hGlobal);
  345.       try
  346.         DropEffect^ := DWORD(FeedbackEffect);
  347.       finally
  348.         GlobalUnLock(Medium.hGlobal);
  349.       end;
  350.       result := S_OK;
  351.     end;
  352.   end
  353.   //--------------------------------------------------------------------------
  354.   else
  355.     result := DV_E_FORMATETC;
  356. end;
  357. // -----------------------------------------------------------------------------
  358. // -----------------------------------------------------------------------------
  359.  
  360. end.
  361.