mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
1816 lines
38 KiB
ObjectPascal
1816 lines
38 KiB
ObjectPascal
{
|
|
|
|
DRAGDROP.PAS -- simple realization of OLE drag and drop.
|
|
|
|
Author: Jim Mischel
|
|
|
|
Last modification date: 30/05/97
|
|
|
|
Add some changes for compatibility with FPC/Lazarus
|
|
|
|
Copyright (C) 2009 Alexander Koblov (Alexx2000@mail.ru)
|
|
|
|
}
|
|
|
|
unit uOleDragDrop;
|
|
|
|
{$mode delphi}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, ActiveX, Classes, Controls, uDragDropEx;
|
|
|
|
type
|
|
|
|
{ IEnumFormatEtc }
|
|
|
|
TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc)
|
|
|
|
private
|
|
|
|
FIndex: Integer;
|
|
|
|
public
|
|
|
|
constructor Create(Index: Integer = 0);
|
|
|
|
function Next(celt: LongWord; out elt: FormatEtc;
|
|
pceltFetched: pULong): HResult; stdcall;
|
|
|
|
function Skip(celt: LongWord): HResult; stdcall;
|
|
|
|
function Reset: HResult; stdcall;
|
|
|
|
function Clone(out enum: IEnumFormatEtc): HResult; stdcall;
|
|
|
|
end;
|
|
|
|
{ TDragDropInfo }
|
|
|
|
TDragDropInfo = class(TObject)
|
|
|
|
private
|
|
|
|
FInClientArea: boolean;
|
|
|
|
FDropPoint: TPoint;
|
|
|
|
FFileList: TStringList;
|
|
|
|
FPreferredWinDropEffect: DWORD;
|
|
|
|
|
|
function CreateHDrop(bUnicode: Boolean): HGlobal;
|
|
function CreateFileNames(bUnicode: Boolean): HGlobal;
|
|
function CreateURIs(bUnicode: Boolean): HGlobal;
|
|
function CreateShellIdListArray: HGlobal;
|
|
function MakeHGlobal(ptr: Pointer; Size: LongWord): HGlobal;
|
|
|
|
public
|
|
|
|
constructor Create(ADropPoint: TPoint; AInClient: boolean;
|
|
PreferredWinDropEffect: DWORD);
|
|
|
|
destructor Destroy; override;
|
|
|
|
procedure Add(const s: string);
|
|
|
|
function MakeDataInFormat(const formatEtc: TFormatEtc): HGlobal;
|
|
|
|
function CreatePreferredDropEffect(WinDropEffect: DWORD): HGlobal;
|
|
|
|
property InClientArea: boolean Read FInClientArea;
|
|
|
|
property DropPoint: TPoint Read FDropPoint;
|
|
|
|
property Files: TStringList Read FFileList;
|
|
|
|
end;
|
|
|
|
|
|
TDragDropTargetWindows = class; // forward declaration
|
|
|
|
{ TFileDropTarget çíàåò, êàê ïðèíèìàòü ñáðîøåííûå ôàéëû }
|
|
|
|
TFileDropTarget = class(TInterfacedObject, IDropTarget)
|
|
|
|
private
|
|
|
|
FHandle: HWND;
|
|
|
|
FReleased: Boolean;
|
|
|
|
FDragDropTarget: TDragDropTargetWindows;
|
|
|
|
public
|
|
|
|
constructor Create(DragDropTarget: TDragDropTargetWindows);
|
|
|
|
{en
|
|
Unregisters drag&drop target and releases the object (it is destroyed).
|
|
This is the function that should be called to cleanup the object instead
|
|
of Free. Do not use the object after calling it.
|
|
}
|
|
procedure FinalRelease;
|
|
|
|
|
|
{ èç IDropTarget }
|
|
|
|
function DragEnter(const dataObj: IDataObject; grfKeyState: LongWord;
|
|
pt: TPoint; var dwEffect: LongWord): HResult; stdcall;
|
|
|
|
function DragOver(grfKeyState: LongWord; pt: TPoint;
|
|
var dwEffect: LongWord): HResult; stdcall;
|
|
|
|
function DragLeave: HResult; stdcall;
|
|
|
|
function Drop(const dataObj: IDataObject; grfKeyState: LongWord;
|
|
pt: TPoint; var dwEffect: LongWord): HResult; stdcall;
|
|
|
|
{en
|
|
Retrieves the filenames from the HDROP format
|
|
as a list of UTF-8 strings.
|
|
@returns(List of filenames or nil in case of an error.)
|
|
}
|
|
class function GetDropFilenames(hDropData: HDROP): TStringList;
|
|
end;
|
|
|
|
{ TFileDropSource - èñòî÷íèê
|
|
|
|
äëÿ ïåðåòàñêèâàíèÿ ôàéëîâ }
|
|
|
|
TFileDropSource = class(TInterfacedObject, IDropSource)
|
|
|
|
constructor Create;
|
|
|
|
function QueryContinueDrag(fEscapePressed: BOOL;
|
|
grfKeyState: longint): HResult; stdcall;
|
|
|
|
function GiveFeedback(dwEffect: longint): HResult; stdcall;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ THDropDataObject - îáúåêò äàííûõ ñ
|
|
|
|
èíôîðìàöèåé î ïåðåòàñêèâàåìûõ ôàéëàõ }
|
|
|
|
THDropDataObject = class(TInterfacedObject, IDataObject)
|
|
|
|
private
|
|
|
|
FDropInfo: TDragDropInfo;
|
|
|
|
public
|
|
|
|
constructor Create(ADropPoint: TPoint; AInClient: boolean;
|
|
PreferredWinDropEffect: DWORD);
|
|
|
|
destructor Destroy; override;
|
|
|
|
procedure Add(const s: string);
|
|
|
|
{ èç IDataObject }
|
|
|
|
function GetData(const formatetcIn: TFormatEtc;
|
|
out medium: TStgMedium): HResult; stdcall;
|
|
|
|
function GetDataHere(const formatetc: TFormatEtc;
|
|
out medium: TStgMedium): HResult; stdcall;
|
|
|
|
function QueryGetData(const formatetc: TFormatEtc): HResult; stdcall;
|
|
|
|
function GetCanonicalFormatEtc(const formatetc: TFormatEtc;
|
|
out formatetcOut: TFormatEtc): HResult; stdcall;
|
|
|
|
function SetData(const formatetc: TFormatEtc; const medium: TStgMedium;
|
|
fRelease: BOOL): HResult; stdcall;
|
|
|
|
function EnumFormatEtc(dwDirection: LongWord;
|
|
out enumFormatEtc: IEnumFormatEtc): HResult; stdcall;
|
|
|
|
function DAdvise(const formatetc: TFormatEtc; advf: LongWord;
|
|
const advSink: IAdviseSink; out dwConnection: LongWord): HResult; stdcall;
|
|
|
|
function DUnadvise(dwConnection: LongWord): HResult; stdcall;
|
|
|
|
function EnumDAdvise(out enumAdvise: IEnumStatData): HResult; stdcall;
|
|
|
|
end;
|
|
|
|
TDragDropSourceWindows = class(TDragDropSource)
|
|
public
|
|
function RegisterEvents(DragBeginEvent : uDragDropEx.TDragBeginEvent;
|
|
RequestDataEvent: uDragDropEx.TRequestDataEvent;// not handled in Windows
|
|
DragEndEvent : uDragDropEx.TDragEndEvent): Boolean; override;
|
|
|
|
function DoDragDrop(const FileNamesList: TStringList;
|
|
MouseButton: TMouseButton;
|
|
ScreenStartPoint: TPoint
|
|
): Boolean; override;
|
|
end;
|
|
|
|
TDragDropTargetWindows = class(TDragDropTarget)
|
|
public
|
|
constructor Create(Control: TWinControl); override;
|
|
destructor Destroy; override;
|
|
|
|
function RegisterEvents(DragEnterEvent: uDragDropEx.TDragEnterEvent;
|
|
DragOverEvent : uDragDropEx.TDragOverEvent;
|
|
DropEvent : uDragDropEx.TDropEvent;
|
|
DragLeaveEvent: uDragDropEx.TDragLeaveEvent): Boolean; override;
|
|
|
|
procedure UnregisterEvents; override;
|
|
|
|
private
|
|
FDragDropTarget: TFileDropTarget;
|
|
end;
|
|
|
|
|
|
function GetEffectByKeyState(grfKeyState: LongWord) : Integer;
|
|
|
|
{ These functions convert Windows-specific effect value to
|
|
{ TDropEffect values and vice-versa. }
|
|
function WinEffectToDropEffect(dwEffect: LongWord): TDropEffect;
|
|
function DropEffectToWinEffect(DropEffect: TDropEffect): LongWord;
|
|
|
|
|
|
{ Query DROPFILES structure for [BOOL fWide] parameter }
|
|
function DragQueryWide( hGlobalDropInfo: HDROP ): boolean;
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils, ShellAPI, ShlObj, LCLIntf, Win32Proc, uClipboard, uLng;
|
|
|
|
var
|
|
// Supported formats by the source.
|
|
DataFormats: TList = nil; // of TFormatEtc
|
|
|
|
|
|
procedure InitDataFormats;
|
|
|
|
procedure AddFormat(FormatId: Word);
|
|
var
|
|
FormatEtc: PFormatEtc;
|
|
begin
|
|
if FormatId > 0 then
|
|
begin
|
|
New(FormatEtc);
|
|
if Assigned(FormatEtc) then
|
|
begin
|
|
DataFormats.Add(FormatEtc);
|
|
|
|
with FormatEtc^ do
|
|
begin
|
|
CfFormat := FormatId;
|
|
Ptd := nil;
|
|
dwAspect := DVASPECT_CONTENT;
|
|
lindex := -1;
|
|
tymed := TYMED_HGLOBAL;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
DataFormats := TList.Create;
|
|
|
|
AddFormat(CF_HDROP);
|
|
AddFormat(CFU_PREFERRED_DROPEFFECT);
|
|
AddFormat(CFU_FILENAME);
|
|
AddFormat(CFU_FILENAMEW);
|
|
AddFormat(CFU_UNIFORM_RESOURCE_LOCATOR);
|
|
AddFormat(CFU_UNIFORM_RESOURCE_LOCATORW);
|
|
AddFormat(CFU_SHELL_IDLIST_ARRAY);
|
|
end;
|
|
|
|
procedure DestroyDataFormats;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Assigned(DataFormats) then
|
|
begin
|
|
for i := 0 to DataFormats.Count - 1 do
|
|
if Assigned(DataFormats.Items[i]) then
|
|
Dispose(PFormatEtc(DataFormats.Items[i]));
|
|
|
|
FreeAndNil(DataFormats);
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TEnumFormatEtc }
|
|
|
|
constructor TEnumFormatEtc.Create(Index: Integer);
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
FIndex := Index;
|
|
|
|
end;
|
|
|
|
{
|
|
|
|
Next èçâëåêàåò çàäàííîå êîëè÷åñòâî
|
|
|
|
ñòðóêòóð TFormatEtc
|
|
|
|
â ïåðåäàâàåìûé ìàññèâ elt.
|
|
|
|
Èçâëåêàåòñÿ celt ýëåìåíòîâ, íà÷èíàÿ ñ
|
|
|
|
òåêóùåé ïîçèöèè â ñïèñêå.
|
|
|
|
}
|
|
|
|
function TEnumFormatEtc.Next(celt: LongWord; out elt: FormatEtc;
|
|
pceltFetched: pULong): HResult;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
eltout: PFormatEtc;
|
|
|
|
begin
|
|
|
|
// Support returning only 1 format at a time.
|
|
if celt > 1 then celt := 1;
|
|
|
|
eltout := @elt;
|
|
|
|
i := 0;
|
|
|
|
|
|
|
|
while (i < celt) and (FIndex < DataFormats.Count) do
|
|
|
|
begin
|
|
|
|
(eltout + i)^ := PFormatEtc(DataFormats.Items[FIndex])^;
|
|
|
|
Inc(FIndex);
|
|
|
|
Inc(i);
|
|
|
|
end;
|
|
|
|
|
|
|
|
if (pceltFetched <> nil) then
|
|
|
|
pceltFetched^ := i;
|
|
|
|
|
|
|
|
if (I = celt) then
|
|
|
|
Result := S_OK
|
|
|
|
else
|
|
|
|
Result := S_FALSE;
|
|
|
|
end;
|
|
|
|
{
|
|
|
|
Skip ïðîïóñêàåò celt ýëåìåíòîâ ñïèñêà,
|
|
|
|
óñòàíàâëèâàÿ òåêóùóþ ïîçèöèþ
|
|
|
|
íà (CurrentPointer + celt) èëè íà êîíåö
|
|
|
|
ñïèñêà â ñëó÷àå ïåðåïîëíåíèÿ.
|
|
|
|
}
|
|
|
|
function TEnumFormatEtc.Skip(celt: LongWord): HResult;
|
|
|
|
begin
|
|
|
|
if (celt <= DataFormats.Count - FIndex) then
|
|
|
|
begin
|
|
|
|
FIndex := FIndex + celt;
|
|
|
|
Result := S_OK;
|
|
|
|
end
|
|
else
|
|
|
|
begin
|
|
|
|
FIndex := DataFormats.Count;
|
|
|
|
Result := S_FALSE;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ Reset óñòàíàâëèâàåò óêàçàòåëü òåêóùåé
|
|
|
|
ïîçèöèè íà íà÷àëî ñïèñêà }
|
|
|
|
function TEnumFormatEtc.Reset: HResult;
|
|
|
|
begin
|
|
|
|
FIndex := 0;
|
|
|
|
Result := S_OK;
|
|
|
|
end;
|
|
|
|
{ Clone êîïèðóåò ñïèñîê ñòðóêòóð }
|
|
|
|
function TEnumFormatEtc.Clone(out enum: IEnumFormatEtc): HResult;
|
|
|
|
begin
|
|
|
|
enum := TEnumFormatEtc.Create(FIndex);
|
|
|
|
Result := S_OK;
|
|
|
|
end;
|
|
|
|
|
|
{ TDragDropInfo }
|
|
|
|
constructor TDragDropInfo.Create(ADropPoint: TPoint; AInClient: boolean;
|
|
PreferredWinDropEffect: DWORD);
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
FFileList := TStringList.Create;
|
|
|
|
FDropPoint := ADropPoint;
|
|
|
|
FInClientArea := AInClient;
|
|
|
|
FPreferredWinDropEffect := PreferredWinDropEffect;
|
|
|
|
end;
|
|
|
|
destructor TDragDropInfo.Destroy;
|
|
|
|
begin
|
|
|
|
FFileList.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
procedure TDragDropInfo.Add(const s: string);
|
|
|
|
begin
|
|
|
|
Files.Add(s);
|
|
|
|
end;
|
|
|
|
function TDragDropInfo.MakeDataInFormat(const formatEtc: TFormatEtc): HGlobal;
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
if (formatEtc.tymed = DWORD(-1)) or // Transport medium not specified.
|
|
(Boolean(formatEtc.tymed and TYMED_HGLOBAL)) // Support only HGLOBAL medium.
|
|
then
|
|
|
|
begin
|
|
|
|
if formatEtc.CfFormat = CF_HDROP then
|
|
begin
|
|
Result := CreateHDrop(Win32Proc.UnicodeEnabledOS)
|
|
end
|
|
|
|
else if formatEtc.CfFormat = CFU_PREFERRED_DROPEFFECT then
|
|
|
|
begin
|
|
Result := CreatePreferredDropEffect(FPreferredWinDropEffect);
|
|
end
|
|
|
|
else if (formatEtc.CfFormat = CFU_FILENAME) then
|
|
|
|
begin
|
|
Result := CreateFileNames(False);
|
|
end
|
|
|
|
else if (formatEtc.CfFormat = CFU_FILENAMEW) then
|
|
|
|
begin
|
|
Result := CreateFileNames(True);
|
|
end
|
|
|
|
// URIs disabled for now. It may be enough to just report that URL format
|
|
// is supported, but not actually format data this way.
|
|
{else if (formatEtc.CfFormat = CFU_UNIFORM_RESOURCE_LOCATOR) then
|
|
|
|
begin
|
|
Result := CreateURIs(False);
|
|
end
|
|
|
|
else if (formatEtc.CfFormat = CFU_UNIFORM_RESOURCE_LOCATORW) then
|
|
|
|
begin
|
|
Result := CreateURIs(True);
|
|
end}
|
|
|
|
else if (formatEtc.CfFormat = CFU_SHELL_IDLIST_ARRAY) then
|
|
|
|
begin
|
|
Result := CreateShellIdListArray;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function TDragDropInfo.CreateFileNames(bUnicode: Boolean): HGlobal;
|
|
|
|
var
|
|
|
|
FileList: AnsiString;
|
|
wsFileList: WideString;
|
|
|
|
begin
|
|
|
|
if Files.Count = 0 then Exit;
|
|
|
|
if bUnicode then
|
|
begin
|
|
|
|
wsFileList := UTF8Decode(Self.Files[0]) + #0;
|
|
|
|
Result := MakeHGlobal(PWideChar(wsFileList),
|
|
Length(wsFileList) * SizeOf(WideChar));
|
|
end
|
|
else
|
|
begin
|
|
|
|
FileList := Utf8ToAnsi(Self.Files[0]) + #0;
|
|
|
|
Result := MakeHGlobal(PAnsiChar(FileList),
|
|
Length(FileList) * SizeOf(AnsiChar));
|
|
end;
|
|
end;
|
|
|
|
function TDragDropInfo.CreateURIs(bUnicode: Boolean): HGlobal;
|
|
|
|
var
|
|
|
|
UriList: AnsiString;
|
|
wsUriList: WideString;
|
|
I: Integer;
|
|
|
|
begin
|
|
|
|
wsUriList := '';
|
|
|
|
for I := 0 to Self.Files.Count - 1 do
|
|
begin
|
|
if I > 0 then
|
|
wsUriList := wsUriList + LineEnding;
|
|
|
|
wsUriList := wsUriList
|
|
+ fileScheme + '//' { don't put hostname }
|
|
+ URIEncode(UTF8Decode(
|
|
StringReplace(Files[I], '\', '/', [rfReplaceAll] )));
|
|
end;
|
|
|
|
wsUriList := wsUriList + #0;
|
|
|
|
if bUnicode then
|
|
|
|
Result := MakeHGlobal(PWideChar(wsUriList),
|
|
Length(wsUriList) * SizeOf(WideChar))
|
|
else
|
|
|
|
begin
|
|
|
|
// Wide to Ansi
|
|
UriList := Utf8ToAnsi(UTF8Encode(wsUriList));
|
|
|
|
Result := MakeHGlobal(PAnsiChar(UriList),
|
|
Length(UriList) * SizeOf(AnsiChar));
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function TDragDropInfo.CreateShellIdListArray: HGlobal;
|
|
|
|
var
|
|
pidl: LPITEMIDLIST;
|
|
pidlSize: Integer;
|
|
pIdA: LPIDA = nil; // ShellIdListArray structure
|
|
ShellDesktop: IShellFolder = nil;
|
|
CurPosition: UINT;
|
|
dwTotalSizeToAllocate: DWORD;
|
|
I: Integer;
|
|
|
|
function GetPidlFromPath(ShellFolder: IShellFolder; Path: WideString): LPITEMIDLIST;
|
|
var
|
|
chEaten: ULONG = 0;
|
|
dwAttributes: ULONG = 0;
|
|
begin
|
|
if ShellFolder.ParseDisplayName(0, nil, PWideChar(Path), chEaten,
|
|
Result, dwAttributes) <> S_OK then
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
|
|
function GetPidlSize(Pidl: LPITEMIDLIST): Integer;
|
|
var
|
|
pidlTmp: LPITEMIDLIST;
|
|
begin
|
|
Result := 0;
|
|
pidlTmp := pidl;
|
|
|
|
while pidlTmp^.mkid.cb <> 0 do
|
|
begin
|
|
Result := Result + pidlTmp^.mkid.cb;
|
|
pidlTmp := LPITEMIDLIST(LPBYTE(pidlTmp) + PtrInt(pidlTmp^.mkid.cb)); // Next Item.
|
|
end;
|
|
|
|
Inc(Result, SizeOf(BYTE) * 2); // PIDL ends with two zeros.
|
|
end;
|
|
|
|
begin
|
|
Result := 0;
|
|
|
|
// Get Desktop shell interface.
|
|
if SHGetDesktopFolder(ShellDesktop) = S_OK then
|
|
begin
|
|
// Get Desktop PIDL, which will be the root PIDL for the files' PIDLs.
|
|
if SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, pidl) = S_OK then
|
|
begin
|
|
pidlSize := GetPidlSize(pidl);
|
|
|
|
// How much memory to allocate for the whole structure.
|
|
// We don't know how much memory each PIDL takes yet
|
|
// (estimate using desktop pidl size).
|
|
dwTotalSizeToAllocate := SizeOf(_IDA.cidl)
|
|
+ SizeOf(UINT) * (Files.Count + 1) // PIDLs' offsets
|
|
+ pidlSize * (Files.Count + 1); // PIDLs
|
|
|
|
pIda := AllocMem(dwTotalSizeToAllocate);
|
|
|
|
// Number of files PIDLs (without root).
|
|
pIdA^.cidl := Files.Count;
|
|
|
|
// Calculate offset for the first pidl (root).
|
|
CurPosition := SizeOf(_IDA.cidl) + SizeOf(UINT) * (Files.Count + 1);
|
|
|
|
// Write first PIDL.
|
|
pIdA^.aoffset[0] := CurPosition;
|
|
CopyMemory(LPBYTE(pIda) + PtrInt(CurPosition), pidl, pidlSize);
|
|
Inc(CurPosition, pidlSize);
|
|
|
|
CoTaskMemFree(pidl);
|
|
|
|
for I := 0 to Self.Files.Count - 1 do
|
|
begin
|
|
// Get PIDL for each file (if Desktop is the root, then
|
|
// absolute paths are acceptable).
|
|
pidl := GetPidlFromPath(ShellDesktop, UTF8Decode(Files[i]));
|
|
|
|
if pidl <> nil then
|
|
begin
|
|
pidlSize := GetPidlSize(pidl);
|
|
|
|
// If not enough memory then reallocate.
|
|
if dwTotalSizeToAllocate < CurPosition + pidlSize then
|
|
begin
|
|
// Estimate using current PIDL's size.
|
|
Inc(dwTotalSizeToAllocate, (Files.Count - i) * pidlSize);
|
|
|
|
pIdA := ReAllocMem(pIda, dwTotalSizeToAllocate);
|
|
|
|
if not Assigned(pIda) then
|
|
Break;
|
|
end;
|
|
|
|
// Write PIDL.
|
|
{$R-}
|
|
pIdA^.aoffset[i + 1] := CurPosition;
|
|
{$R+}
|
|
CopyMemory(LPBYTE(pIdA) + PtrInt(CurPosition), pidl, pidlSize);
|
|
Inc(CurPosition, pidlSize);
|
|
|
|
CoTaskMemFree(pidl);
|
|
end;
|
|
end;
|
|
|
|
if Assigned(pIda) then
|
|
begin
|
|
// Current position it at the end of the structure.
|
|
Result := MakeHGlobal(pIdA, CurPosition);
|
|
Freemem(pIda);
|
|
end;
|
|
|
|
end; // SHGetSpecialFolderLocation
|
|
|
|
ShellDesktop := nil;
|
|
|
|
end; // SHGetDesktopFolder
|
|
|
|
end;
|
|
|
|
function TDragDropInfo.CreatePreferredDropEffect(WinDropEffect: DWORD) : HGlobal;
|
|
begin
|
|
Result := MakeHGlobal(@WinDropEffect, SizeOf(WinDropEffect));
|
|
end;
|
|
|
|
function TDragDropInfo.MakeHGlobal(ptr: Pointer; Size: LongWord): HGlobal;
|
|
|
|
var
|
|
DataPointer : Pointer;
|
|
DataHandle : HGLOBAL;
|
|
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
if Assigned(ptr) then
|
|
begin
|
|
|
|
DataHandle := GlobalAlloc(GMEM_MOVEABLE or GMEM_ZEROINIT, Size);
|
|
|
|
if (DataHandle <> 0) then
|
|
|
|
begin
|
|
|
|
DataPointer := GlobalLock(DataHandle);
|
|
|
|
if Assigned(DataPointer) then
|
|
begin
|
|
|
|
CopyMemory(DataPointer, ptr, Size);
|
|
|
|
GlobalUnlock(DataHandle);
|
|
|
|
Result := DataHandle;
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
GlobalFree(DataHandle);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
function TDragDropInfo.CreateHDrop(bUnicode: Boolean): HGlobal;
|
|
|
|
var
|
|
|
|
RequiredSize: Integer;
|
|
|
|
I: Integer;
|
|
|
|
hGlobalDropInfo: HGlobal;
|
|
|
|
DropFiles: PDropFiles;
|
|
|
|
FileList: AnsiString = '';
|
|
|
|
wsFileList: WideString = '';
|
|
|
|
begin
|
|
|
|
{
|
|
|
|
Ïîñòðîèì ñòðóêòóðó TDropFiles â ïàìÿòè,
|
|
|
|
âûäåëåííîé ÷åðåç
|
|
|
|
GlobalAlloc. Îáëàñòü ïàìÿòè ñäåëàåì ãëîáàëüíîé
|
|
|
|
è ñîâìåñòíîé,
|
|
|
|
ïîñêîëüêó îíà, âåðîÿòíî, áóäåò ïåðåäàâàòüñÿ
|
|
|
|
äðóãîìó ïðîöåññó.
|
|
|
|
}
|
|
|
|
{
|
|
Bring the filenames in a form,
|
|
separated by #0 and ending with a double #0#0
|
|
}
|
|
|
|
if bUnicode then
|
|
begin
|
|
|
|
for I := 0 to Self.Files.Count - 1 do
|
|
wsFileList := wsFileList + UTF8Decode(Self.Files[I]) + #0;
|
|
|
|
wsFileList := wsFileList + #0;
|
|
|
|
{ Îïðåäåëÿåì íåîáõîäèìûé ðàçìåð ñòðóêòóðû }
|
|
|
|
RequiredSize := SizeOf(TDropFiles) + Length(wsFileList) * SizeOf(WChar);
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
for I := 0 to Self.Files.Count - 1 do
|
|
FileList := FileList + Utf8ToAnsi(Self.Files[I]) + #0;
|
|
|
|
FileList := FileList + #0;
|
|
|
|
{ Îïðåäåëÿåì íåîáõîäèìûé ðàçìåð ñòðóêòóðû }
|
|
|
|
RequiredSize := SizeOf(TDropFiles) + Length(FileList) * SizeOf(AnsiChar);
|
|
|
|
end;
|
|
|
|
|
|
hGlobalDropInfo := GlobalAlloc(GMEM_MOVEABLE or GMEM_ZEROINIT, RequiredSize);
|
|
|
|
if (hGlobalDropInfo <> 0) then
|
|
|
|
begin
|
|
{ Çàáëîêèðóåì îáëàñòü ïàìÿòè, ÷òîáû ê íåé
|
|
|
|
ìîæíî áûëî îáðàòèòüñÿ
|
|
|
|
}
|
|
|
|
DropFiles := GlobalLock(hGlobalDropInfo);
|
|
|
|
|
|
{ Çàïîëíèì ïîëÿ ñòðóêòóðû DropFiles }
|
|
|
|
{
|
|
|
|
pFiles -- ñìåùåíèå îò íà÷àëà
|
|
|
|
ñòðóêòóðû äî ïåðâîãî áàéòà ìàññèâà
|
|
|
|
ñ èìåíàìè ôàéëîâ.
|
|
|
|
}
|
|
|
|
DropFiles.pFiles := SizeOf(TDropFiles);
|
|
|
|
DropFiles.pt := Self.FDropPoint;
|
|
|
|
DropFiles.fNC := Self.InClientArea;
|
|
|
|
DropFiles.fWide := bUnicode;
|
|
|
|
{
|
|
|
|
Êîïèðóåì èìåíà ôàéëîâ â áóôåð.
|
|
|
|
Áóôåð íà÷èíàåòñÿ ñî ñìåùåíèÿ
|
|
|
|
DropFiles + DropFiles.pFiles,
|
|
|
|
òî åñòü ïîñëå ïîñëåäíåãî ïîëÿ ñòðóêòóðû.
|
|
|
|
}
|
|
|
|
{ The pointer should be aligned nicely,
|
|
because the TDropFiles record is not packed. }
|
|
DropFiles := Pointer(DropFiles) + DropFiles.pFiles;
|
|
|
|
if bUnicode then
|
|
CopyMemory(DropFiles, PWideChar(wsFileList), Length(wsFileList) * SizeOf(WChar))
|
|
else
|
|
CopyMemory(DropFiles, PAnsiChar(FileList), Length(FileList) * SizeOf(AnsiChar));
|
|
|
|
|
|
|
|
{ Ñíèìàåì áëîêèðîâêó }
|
|
|
|
GlobalUnlock(hGlobalDropInfo);
|
|
|
|
end;
|
|
|
|
|
|
|
|
Result := hGlobalDropInfo;
|
|
|
|
end;
|
|
|
|
|
|
{ TFileDropTarget }
|
|
|
|
constructor TFileDropTarget.Create(DragDropTarget: TDragDropTargetWindows);
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
// Here RefCount is 1 - as set in TInterfacedObject.NewInstance,
|
|
// but it's decremented back in TInterfacedObject.AfterConstruction
|
|
// (when this constructor finishes). So we must manually again increase it.
|
|
_AddRef;
|
|
|
|
FReleased := False;
|
|
|
|
FDragDropTarget := DragDropTarget;
|
|
|
|
// Increases RefCount.
|
|
ActiveX.CoLockObjectExternal(Self, True, False);
|
|
|
|
// Increases RefCount.
|
|
if ActiveX.RegisterDragDrop(DragDropTarget.GetControl.Handle, Self) = S_OK then
|
|
|
|
FHandle := DragDropTarget.GetControl.Handle
|
|
|
|
else
|
|
|
|
FHandle := 0;
|
|
|
|
end;
|
|
|
|
|
|
procedure TFileDropTarget.FinalRelease;
|
|
|
|
begin
|
|
|
|
if not FReleased then
|
|
|
|
begin
|
|
|
|
FReleased := True;
|
|
|
|
// Decreases reference count.
|
|
ActiveX.CoLockObjectExternal(Self, False, True);
|
|
|
|
// Check if window was not already destroyed.
|
|
if (FHandle <> 0) and (IsWindow(FHandle)) then
|
|
begin
|
|
|
|
// Decreases reference count.
|
|
ActiveX.RevokeDragDrop(FHandle);
|
|
|
|
FHandle := 0;
|
|
|
|
end
|
|
else
|
|
_Release; // Cannot revoke - just release reference.
|
|
|
|
_Release; // For _AddRef in Create.
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function TFileDropTarget.DragEnter(const dataObj: IDataObject;
|
|
grfKeyState: LongWord; pt: TPoint; var dwEffect: LongWord): HResult; stdcall;
|
|
|
|
var
|
|
DropEffect: TDropEffect;
|
|
|
|
begin
|
|
// dwEffect parameter states which effects are allowed by the source.
|
|
dwEffect := dwEffect and GetEffectByKeyState(grfKeyState);
|
|
|
|
if Assigned(FDragDropTarget.GetDragEnterEvent) then
|
|
begin
|
|
DropEffect := WinEffectToDropEffect(dwEffect);
|
|
|
|
if FDragDropTarget.GetDragEnterEvent()(DropEffect, pt) = True then
|
|
begin
|
|
dwEffect := DropEffectToWinEffect(DropEffect);
|
|
Result := S_OK
|
|
end
|
|
else
|
|
Result := S_FALSE;
|
|
end
|
|
else
|
|
Result := S_OK;
|
|
end;
|
|
|
|
function TFileDropTarget.DragOver
|
|
|
|
(grfKeyState: LongWord; pt: TPoint; var dwEffect: LongWord): HResult; stdcall;
|
|
|
|
var
|
|
DropEffect: TDropEffect;
|
|
|
|
begin
|
|
// dwEffect parameter states which effects are allowed by the source.
|
|
dwEffect := dwEffect and GetEffectByKeyState(grfKeyState);
|
|
|
|
if Assigned(FDragDropTarget.GetDragOverEvent) then
|
|
begin
|
|
DropEffect := WinEffectToDropEffect(dwEffect);
|
|
|
|
if FDragDropTarget.GetDragOverEvent()(DropEffect, pt) = True then
|
|
begin
|
|
dwEffect := DropEffectToWinEffect(DropEffect);
|
|
Result := S_OK
|
|
end
|
|
else
|
|
Result := S_FALSE;
|
|
end
|
|
else
|
|
Result := S_OK;
|
|
end;
|
|
|
|
function TFileDropTarget.DragLeave: HResult; stdcall;
|
|
|
|
begin
|
|
|
|
if Assigned(FDragDropTarget.GetDragLeaveEvent) then
|
|
begin
|
|
if FDragDropTarget.GetDragLeaveEvent() = True then
|
|
Result := S_OK
|
|
else
|
|
Result := S_FALSE;
|
|
end
|
|
else
|
|
Result := S_OK;
|
|
end;
|
|
|
|
{
|
|
|
|
Îáðàáîòêà ñáðîøåííûõ äàííûõ.
|
|
|
|
}
|
|
|
|
function TFileDropTarget.Drop(const dataObj: IDataObject; grfKeyState: LongWord;
|
|
pt: TPoint; var dwEffect: LongWord): HResult; stdcall;
|
|
|
|
var
|
|
|
|
Medium: TSTGMedium;
|
|
|
|
Format: TFormatETC;
|
|
|
|
i: Integer;
|
|
|
|
DropInfo: TDragDropInfo;
|
|
|
|
FileNames: TStringList;
|
|
|
|
InClient: boolean;
|
|
|
|
DropPoint: TPoint;
|
|
|
|
DropEffect: TDropEffect;
|
|
|
|
begin
|
|
|
|
dataObj._AddRef;
|
|
|
|
{
|
|
|
|
Ïîëó÷àåì äàííûå. Ñòðóêòóðà TFormatETC
|
|
|
|
ñîîáùàåò
|
|
|
|
dataObj.GetData, êàê ïîëó÷èòü äàííûå
|
|
|
|
è â êàêîì ôîðìàòå
|
|
|
|
îíè äîëæíû õðàíèòüñÿ (ýòà èíôîðìàöèÿ
|
|
|
|
ñîäåðæèòñÿ â
|
|
|
|
ñòðóêòóðå TSTGMedium).
|
|
|
|
}
|
|
|
|
Format.cfFormat := CF_HDROP;
|
|
|
|
Format.ptd := nil;
|
|
|
|
Format.dwAspect := DVASPECT_CONTENT;
|
|
|
|
Format.lindex := -1;
|
|
|
|
Format.tymed := TYMED_HGLOBAL;
|
|
|
|
|
|
|
|
{ Çàíîñèì äàííûå â ñòðóêòóðó Medium }
|
|
|
|
Result := dataObj.GetData(Format, Medium);
|
|
|
|
|
|
|
|
{
|
|
|
|
Åñëè âñå ïðîøëî óñïåøíî, äàëåå
|
|
|
|
äåéñòâóåì, êàê ïðè îïåðàöèè ôàéëîâîãî
|
|
|
|
ïåðåòàñêèâàíèÿ FMDD.
|
|
|
|
}
|
|
|
|
if (Result = S_OK) then
|
|
|
|
begin
|
|
|
|
case Medium.Tymed of
|
|
|
|
TYMED_HGLOBAL:
|
|
|
|
begin
|
|
|
|
InClient := DragQueryPoint(Medium.hGlobal, @DropPoint);
|
|
|
|
if (DropPoint.X = 0) and (DropPoint.Y = 0) then
|
|
DropPoint := pt;
|
|
|
|
{ Ñîçäàåì îáúåêò TDragDropInfo }
|
|
|
|
DropInfo := TDragDropInfo.Create(DropPoint, InClient, dwEffect);
|
|
|
|
|
|
{ Retrieve file names }
|
|
|
|
FileNames := GetDropFilenames(Medium.hGlobal);
|
|
|
|
if Assigned(FileNames) then
|
|
begin
|
|
|
|
for i := 0 to FileNames.Count - 1 do
|
|
|
|
begin
|
|
|
|
DropInfo.Add(FileNames[i]);
|
|
|
|
end;
|
|
|
|
FreeAndNil(FileNames);
|
|
end;
|
|
|
|
|
|
{ Åñëè óêàçàí îáðàáîò÷èê, âûçûâàåì åãî }
|
|
|
|
if (Assigned(FDragDropTarget.GetDropEvent)) then
|
|
|
|
begin
|
|
|
|
// Set default effect by examining keyboard keys, taking into
|
|
// consideration effects allowed by the source (dwEffect parameter).
|
|
dwEffect := dwEffect and GetEffectByKeyState(grfKeyState);
|
|
|
|
DropEffect := WinEffectToDropEffect(dwEffect);
|
|
|
|
if FDragDropTarget.GetDropEvent()(DropInfo.Files, DropEffect, DropInfo.DropPoint) = False then
|
|
|
|
;
|
|
|
|
dwEffect := DropEffectToWinEffect(DropEffect);
|
|
|
|
end;
|
|
|
|
DropInfo.Free;
|
|
|
|
end; // TYMED_HGLOBAL
|
|
|
|
end; // case
|
|
|
|
|
|
if (Medium.PUnkForRelease = nil) then
|
|
|
|
// Drop target must release the medium allocated by GetData.
|
|
|
|
// This does the same as DragFinish(Medium.hGlobal) in this case,
|
|
// but can support other media.
|
|
ReleaseStgMedium(@Medium)
|
|
|
|
else
|
|
|
|
// Drop source is responsible for releasing medium via this object.
|
|
IUnknown(Medium.PUnkForRelease)._Release;
|
|
|
|
end;
|
|
|
|
|
|
dataObj._Release;
|
|
|
|
end;
|
|
|
|
class function TFileDropTarget.GetDropFilenames(hDropData: HDROP): TStringList;
|
|
|
|
var
|
|
NumFiles: Integer;
|
|
i: Integer;
|
|
wszFilename: PWideChar;
|
|
FileName: WideString;
|
|
RequiredSize: Cardinal;
|
|
|
|
begin
|
|
|
|
Result := nil;
|
|
|
|
if hDropData <> 0 then
|
|
begin
|
|
|
|
Result := TStringList.Create;
|
|
|
|
try
|
|
|
|
NumFiles := DragQueryFileW(hDropData, $FFFFFFFF, nil, 0);
|
|
|
|
for i := 0 to NumFiles - 1 do
|
|
begin
|
|
RequiredSize := DragQueryFileW(hDropData, i, nil, 0) + 1; // + 1 = terminating zero
|
|
|
|
wszFilename := GetMem(RequiredSize * SizeOf(WideChar));
|
|
if Assigned(wszFilename) then
|
|
try
|
|
if DragQueryFileW(hDropData, i, wszFilename, RequiredSize) > 0 then
|
|
begin
|
|
FileName := wszFilename;
|
|
|
|
// Windows inserts '?' character where Wide->Ansi conversion
|
|
// of a character was not possible, in which case filename is invalid.
|
|
// This may happen if a non-Unicode application was the source.
|
|
if Pos('?', FileName) = 0 then
|
|
Result.Add(UTF8Encode(FileName))
|
|
else
|
|
raise Exception.Create(rsMsgInvalidFilename + ': ' + LineEnding +
|
|
UTF8Encode(FileName));
|
|
end;
|
|
|
|
finally
|
|
FreeMem(wszFilename);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
except
|
|
FreeAndNil(Result);
|
|
raise;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ TFileDropSource }
|
|
|
|
constructor TFileDropSource.Create;
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
_AddRef;
|
|
|
|
end;
|
|
|
|
|
|
{
|
|
|
|
QueryContinueDrag îïðåäåëÿåò íåîáõîäèìûå äåéñòâèÿ.
|
|
|
|
}
|
|
|
|
function TFileDropSource.QueryContinueDrag(fEscapePressed: BOOL;
|
|
grfKeyState: longint): HResult;
|
|
|
|
var
|
|
Point:TPoint;
|
|
|
|
begin
|
|
|
|
if (fEscapePressed) then
|
|
|
|
begin
|
|
|
|
Result := DRAGDROP_S_CANCEL;
|
|
|
|
// Set flag to notify that dragging was canceled by the user.
|
|
uDragDropEx.TransformDragging := False;
|
|
|
|
end
|
|
|
|
else if ((grfKeyState and (MK_LBUTTON or MK_MBUTTON or MK_RBUTTON)) = 0) then
|
|
|
|
begin
|
|
|
|
Result := DRAGDROP_S_DROP;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
if uDragDropEx.AllowTransformToInternal then
|
|
|
|
begin
|
|
|
|
GetCursorPos(Point);
|
|
|
|
// Call LCL function, not the Windows one.
|
|
// LCL version will return 0 if mouse is over a window belonging to another process.
|
|
if LCLIntf.WindowFromPoint(Point) <> 0 then
|
|
|
|
begin
|
|
// Mouse cursor has been moved back into the application window.
|
|
|
|
// Cancel external dragging.
|
|
Result := DRAGDROP_S_CANCEL;
|
|
|
|
// Set flag to notify that dragging has not finished,
|
|
// but rather it is to be transformed into internal dragging.
|
|
uDragDropEx.TransformDragging := True;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Result := S_OK; // Continue dragging
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Result := S_OK; // Continue dragging
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function TFileDropSource.GiveFeedback(dwEffect: longint): HResult;
|
|
|
|
begin
|
|
|
|
case LongWord(dwEffect) of
|
|
|
|
DROPEFFECT_NONE,
|
|
|
|
DROPEFFECT_COPY,
|
|
|
|
DROPEFFECT_MOVE,
|
|
|
|
DROPEFFECT_LINK,
|
|
|
|
DROPEFFECT_SCROLL:
|
|
|
|
Result := DRAGDROP_S_USEDEFAULTCURSORS;
|
|
|
|
else
|
|
|
|
Result := S_OK;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
{ THDropDataObject }
|
|
|
|
constructor THDropDataObject.Create(ADropPoint: TPoint; AInClient: boolean;
|
|
PreferredWinDropEffect: DWORD);
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
_AddRef;
|
|
|
|
FDropInfo := TDragDropInfo.Create(ADropPoint, AInClient, PreferredWinDropEffect);
|
|
|
|
end;
|
|
|
|
destructor THDropDataObject.Destroy;
|
|
|
|
begin
|
|
|
|
if (FDropInfo <> nil) then
|
|
|
|
FDropInfo.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
procedure THDropDataObject.Add(const s: string);
|
|
|
|
begin
|
|
|
|
FDropInfo.Add(s);
|
|
|
|
end;
|
|
|
|
function THDropDataObject.GetData(const formatetcIn: TFormatEtc;
|
|
out medium: TStgMedium): HResult;
|
|
|
|
begin
|
|
|
|
Result := DV_E_FORMATETC;
|
|
|
|
{ Íåîáõîäèìî îáíóëèòü âñå ïîëÿ medium
|
|
|
|
íà ñëó÷àé îøèáêè}
|
|
|
|
medium.tymed := 0;
|
|
|
|
medium.hGlobal := 0;
|
|
|
|
medium.PUnkForRelease := nil;
|
|
|
|
|
|
|
|
{ Åñëè ôîðìàò ïîääåðæèâàåòñÿ, ñîçäàåì
|
|
|
|
è âîçâðàùàåì äàííûå }
|
|
|
|
if (QueryGetData(formatetcIn) = S_OK) then
|
|
|
|
begin
|
|
|
|
if (FDropInfo <> nil) then
|
|
|
|
begin
|
|
|
|
{ Create data in specified format. }
|
|
{ The hGlobal will be released by the caller of GetData. }
|
|
|
|
medium.hGlobal := FDropInfo.MakeDataInFormat(formatetcIn);
|
|
|
|
if medium.hGlobal <> 0 then
|
|
|
|
begin
|
|
|
|
medium.tymed := TYMED_HGLOBAL;
|
|
|
|
Result := S_OK;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function THDropDataObject.GetDataHere(const formatetc: TFormatEtc;
|
|
out medium: TStgMedium): HResult;
|
|
|
|
begin
|
|
|
|
Result := DV_E_FORMATETC; { Ê ñîæàëåíèþ,
|
|
|
|
íå ïîääåðæèâàåòñÿ }
|
|
|
|
end;
|
|
|
|
function THDropDataObject.QueryGetData(const formatetc: TFormatEtc): HResult;
|
|
|
|
var
|
|
i:Integer;
|
|
|
|
begin
|
|
|
|
with formatetc do
|
|
|
|
if dwAspect = DVASPECT_CONTENT then
|
|
|
|
begin
|
|
|
|
Result := DV_E_FORMATETC; // begin with 'format not supported'
|
|
|
|
// See if the queried format is supported.
|
|
for i := 0 to DataFormats.Count - 1 do
|
|
begin
|
|
|
|
if Assigned(DataFormats[i]) then
|
|
begin
|
|
|
|
if cfFormat = PFormatEtc(DataFormats[i])^.CfFormat then
|
|
begin
|
|
|
|
// Format found, see if transport medium is supported.
|
|
|
|
if (tymed = DWORD(-1)) or
|
|
(Boolean(tymed and PFormatEtc(DataFormats[i])^.tymed)) then
|
|
begin
|
|
|
|
Result := S_OK;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Result := DV_E_TYMED; // transport medium not supported
|
|
|
|
|
|
Exit; // exit if format found (regardless of transport medium)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Result := DV_E_DVASPECT; // aspect not supported
|
|
|
|
end;
|
|
|
|
function THDropDataObject.GetCanonicalFormatEtc(const formatetc: TFormatEtc;
|
|
out formatetcOut: TFormatEtc): HResult;
|
|
|
|
begin
|
|
|
|
formatetcOut.ptd := nil;
|
|
|
|
Result := E_NOTIMPL;
|
|
|
|
end;
|
|
|
|
function THDropDataObject.SetData(const formatetc: TFormatEtc;
|
|
const medium: TStgMedium; fRelease: BOOL): HResult;
|
|
|
|
begin
|
|
|
|
Result := E_NOTIMPL;
|
|
|
|
end;
|
|
|
|
|
|
{ EnumFormatEtc âîçâðàùàåò ñïèñîê ïîääåðæèâàåìûõ ôîðìàòîâ }
|
|
|
|
function THDropDataObject.EnumFormatEtc(dwDirection: LongWord;
|
|
out enumFormatEtc: IEnumFormatEtc): HResult;
|
|
|
|
begin
|
|
|
|
{ Ïîääåðæèâàåòñÿ òîëüêî Get. Çàäàòü
|
|
|
|
ñîäåðæèìîå äàííûõ íåëüçÿ }
|
|
|
|
if dwDirection = DATADIR_GET then
|
|
|
|
begin
|
|
|
|
enumFormatEtc := TEnumFormatEtc.Create;
|
|
|
|
Result := S_OK;
|
|
|
|
end
|
|
else
|
|
|
|
begin
|
|
|
|
enumFormatEtc := nil;
|
|
|
|
Result := E_NOTIMPL;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ Ôóíêöèè Advise íå ïîääåðæèâàþòñÿ }
|
|
|
|
function THDropDataObject.DAdvise(const formatetc: TFormatEtc;
|
|
advf: LongWord; const advSink: IAdviseSink; out dwConnection: LongWord): HResult;
|
|
|
|
begin
|
|
|
|
Result := OLE_E_ADVISENOTSUPPORTED;
|
|
|
|
end;
|
|
|
|
function THDropDataObject.DUnadvise(dwConnection: LongWord): HResult;
|
|
|
|
begin
|
|
|
|
Result := OLE_E_ADVISENOTSUPPORTED;
|
|
|
|
end;
|
|
|
|
function THDropDataObject.EnumDAdvise(out enumAdvise: IEnumStatData): HResult;
|
|
|
|
begin
|
|
|
|
Result := OLE_E_ADVISENOTSUPPORTED;
|
|
|
|
end;
|
|
|
|
|
|
function GetEffectByKeyState(grfKeyState: LongWord): Integer;
|
|
begin
|
|
Result := DROPEFFECT_COPY; { default effect }
|
|
|
|
if (grfKeyState and MK_CONTROL) > 0 then
|
|
begin
|
|
if (grfKeyState and MK_SHIFT) > 0 then
|
|
Result := DROPEFFECT_LINK
|
|
else
|
|
Result := DROPEFFECT_COPY;
|
|
end
|
|
else if (grfKeyState and MK_SHIFT) > 0 then
|
|
Result := DROPEFFECT_MOVE;
|
|
|
|
end;
|
|
|
|
function WinEffectToDropEffect(dwEffect: LongWord): TDropEffect;
|
|
begin
|
|
case dwEffect of
|
|
DROPEFFECT_COPY: Result := DropCopyEffect;
|
|
DROPEFFECT_MOVE: Result := DropMoveEffect;
|
|
DROPEFFECT_LINK: Result := DropLinkEffect;
|
|
else Result := DropNoEffect;
|
|
end;
|
|
end;
|
|
|
|
function DropEffectToWinEffect(DropEffect: TDropEffect): LongWord;
|
|
begin
|
|
case DropEffect of
|
|
DropCopyEffect: Result := DROPEFFECT_COPY;
|
|
DropMoveEffect: Result := DROPEFFECT_MOVE;
|
|
DropLinkEffect: Result := DROPEFFECT_LINK;
|
|
else Result := DROPEFFECT_NONE;
|
|
end;
|
|
end;
|
|
|
|
function DragQueryWide( hGlobalDropInfo: HDROP ): boolean;
|
|
var DropFiles: PDropFiles;
|
|
begin
|
|
DropFiles := GlobalLock( hGlobalDropInfo );
|
|
Result := DropFiles^.fWide;
|
|
GlobalUnlock( hGlobalDropInfo );
|
|
end;
|
|
|
|
{ ---------------------------------------------------------}
|
|
{ TDragDropSourceWindows }
|
|
|
|
function TDragDropSourceWindows.RegisterEvents(
|
|
DragBeginEvent : uDragDropEx.TDragBeginEvent;
|
|
RequestDataEvent: uDragDropEx.TRequestDataEvent; // not Handled in Windows
|
|
DragEndEvent : uDragDropEx.TDragEndEvent): Boolean;
|
|
begin
|
|
inherited;
|
|
|
|
// RequestDataEvent is not handled, because the system has control of all data transfer.
|
|
|
|
Result := True; // confirm that events are registered
|
|
end;
|
|
|
|
function TDragDropSourceWindows.DoDragDrop(const FileNamesList: TStringList;
|
|
MouseButton: TMouseButton;
|
|
ScreenStartPoint: TPoint): Boolean;
|
|
var
|
|
DropSource: TFileDropSource;
|
|
DropData: THDropDataObject;
|
|
Rslt: HRESULT;
|
|
dwEffect: LongWord;
|
|
I: Integer;
|
|
begin
|
|
|
|
// Simulate drag-begin event.
|
|
if Assigned(GetDragBeginEvent) then
|
|
begin
|
|
Result := GetDragBeginEvent()();
|
|
if Result = False then Exit;
|
|
end;
|
|
|
|
// Create source-object
|
|
DropSource:= TFileDropSource.Create;
|
|
|
|
// and data object
|
|
DropData:= THDropDataObject.Create(ScreenStartPoint, False,
|
|
DROPEFFECT_COPY { default effect } );
|
|
|
|
for I:= 0 to FileNamesList.Count - 1 do
|
|
DropData.Add (FileNamesList[i]);
|
|
|
|
// Start OLE Drag&Drop
|
|
Rslt:= ActiveX.DoDragDrop(DropData, DropSource,
|
|
DROPEFFECT_MOVE or DROPEFFECT_COPY or DROPEFFECT_LINK, // Allowed effects
|
|
@dwEffect);
|
|
|
|
case Rslt of
|
|
DRAGDROP_S_DROP:
|
|
begin
|
|
FLastStatus := DragDropSuccessful;
|
|
Result := True;
|
|
end;
|
|
|
|
DRAGDROP_S_CANCEL:
|
|
begin
|
|
FLastStatus := DragDropAborted;
|
|
Result := False;
|
|
end;
|
|
|
|
E_OUTOFMEMORY:
|
|
begin
|
|
MessageBox(0, 'Out of memory', 'Error!', 16);
|
|
FLastStatus := DragDropError;
|
|
Result := False;
|
|
end;
|
|
|
|
else
|
|
begin
|
|
MessageBox(0, 'Something bad happened', 'Error!', 16);
|
|
FLastStatus := DragDropError;
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
// Simulate drag-end event. This must be called here,
|
|
// after DoDragDrop returns from the system.
|
|
if Assigned(GetDragEndEvent) then
|
|
begin
|
|
if Result = True then
|
|
Result := GetDragEndEvent()()
|
|
else
|
|
GetDragEndEvent()()
|
|
end;
|
|
|
|
// Release created objects.
|
|
DropSource._Release;
|
|
DropData._Release;
|
|
end;
|
|
|
|
|
|
{ ---------------------------------------------------------}
|
|
{ TDragDropTargetWindows }
|
|
|
|
constructor TDragDropTargetWindows.Create(Control: TWinControl);
|
|
begin
|
|
FDragDropTarget := nil;
|
|
inherited Create(Control);
|
|
end;
|
|
|
|
destructor TDragDropTargetWindows.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
if Assigned(FDragDropTarget) then
|
|
begin
|
|
FDragDropTarget.FinalRelease;
|
|
FDragDropTarget := nil;
|
|
end;
|
|
end;
|
|
|
|
function TDragDropTargetWindows.RegisterEvents(
|
|
DragEnterEvent: uDragDropEx.TDragEnterEvent;
|
|
DragOverEvent : uDragDropEx.TDragOverEvent;
|
|
DropEvent : uDragDropEx.TDropEvent;
|
|
DragLeaveEvent: uDragDropEx.TDragLeaveEvent): Boolean;
|
|
begin
|
|
// Unregister if registered before.
|
|
UnregisterEvents;
|
|
|
|
inherited; // Call inherited Register now.
|
|
|
|
GetControl.HandleNeeded; // force creation of the handle
|
|
if GetControl.HandleAllocated = True then
|
|
begin
|
|
FDragDropTarget := TFileDropTarget.Create(Self);
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TDragDropTargetWindows.UnregisterEvents;
|
|
begin
|
|
inherited;
|
|
if Assigned(FDragDropTarget) then
|
|
begin
|
|
FDragDropTarget.FinalRelease; // Releasing will unregister events
|
|
FDragDropTarget := nil;
|
|
end;
|
|
end;
|
|
|
|
|
|
initialization
|
|
|
|
OleInitialize(nil);
|
|
InitDataFormats;
|
|
|
|
|
|
finalization
|
|
|
|
OleUninitialize;
|
|
DestroyDataFormats;
|
|
|
|
end.
|
|
|