mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
378 lines
13 KiB
ObjectPascal
378 lines
13 KiB
ObjectPascal
unit VirtualTrees.DragnDrop;
|
|
|
|
interface
|
|
|
|
uses
|
|
WinApi.Windows,
|
|
WinApi.ActiveX,
|
|
WinApi.ShlObj,
|
|
System.Types,
|
|
Vcl.Graphics,
|
|
Vcl.Controls,
|
|
VirtualTrees.Types,
|
|
VirtualTrees.BaseTree,
|
|
VirtualTrees.Header;
|
|
|
|
type
|
|
TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc)
|
|
private
|
|
FFormatEtcArray : TFormatEtcArray;
|
|
FCurrentIndex : Integer;
|
|
public
|
|
constructor Create(const AFormatEtcArray : TFormatEtcArray);
|
|
|
|
function Clone(out Enum : IEnumFormatEtc) : HResult; stdcall;
|
|
function Next(celt : Integer; out elt; pceltFetched : PLongint) : HResult; stdcall;
|
|
function Reset : HResult; stdcall;
|
|
function Skip(celt : Integer) : HResult; stdcall;
|
|
end;
|
|
|
|
// TVTDragManager is a class to manage drag and drop in a Virtual Treeview.
|
|
TVTDragManager = class(TInterfacedObject, IVTDragManager, IDropSource, IDropTarget)
|
|
private
|
|
FOwner, // The tree which is responsible for drag management.
|
|
FDragSource : TBaseVirtualTree; // Reference to the source tree if the source was a VT, might be different than the owner tree.
|
|
FHeader : TVTHeader;
|
|
FIsDropTarget : Boolean; // True if the owner is currently the drop target.
|
|
FDataObject : IDataObject; // A reference to the data object passed in by DragEnter (only used when the owner tree is the current drop target).
|
|
FDropTargetHelper : IDropTargetHelper; // Win2k > Drag image support
|
|
FFullDragging : BOOL; // True, if full dragging is currently enabled in the system.
|
|
|
|
function GetDataObject : IDataObject; stdcall;
|
|
function GetDragSource : TBaseVirtualTree; stdcall;
|
|
function GetIsDropTarget : Boolean; stdcall;
|
|
public
|
|
constructor Create(AOwner : TBaseVirtualTree); virtual;
|
|
destructor Destroy; override;
|
|
|
|
function DragEnter(const DataObject : IDataObject; KeyState : Integer; Pt : TPoint; var Effect : Longint) : HResult; stdcall;
|
|
function DragLeave : HResult; stdcall;
|
|
function DragOver(KeyState : Integer; Pt : TPoint; var Effect : Longint) : HResult; stdcall;
|
|
function Drop(const DataObject : IDataObject; KeyState : Integer; Pt : TPoint; var Effect : Integer) : HResult; stdcall;
|
|
procedure ForceDragLeave; stdcall;
|
|
function GiveFeedback(Effect : Integer) : HResult; stdcall;
|
|
function QueryContinueDrag(EscapePressed : BOOL; KeyState : Integer) : HResult; stdcall;
|
|
class function GetTreeFromDataObject(const DataObject: TVTDragDataObject): TBaseVirtualTree;
|
|
end;
|
|
|
|
var
|
|
StandardOLEFormat : TFormatEtc = (
|
|
// Format must later be set.
|
|
cfFormat : 0;
|
|
// No specific target device to render on.
|
|
ptd : nil;
|
|
// Normal content to render.
|
|
dwAspect : DVASPECT_CONTENT;
|
|
// No specific page of multipage data (we don't use multipage data by default).
|
|
lindex : - 1;
|
|
// Acceptable storage formats are IStream and global memory. The first is preferred.
|
|
tymed : TYMED_ISTREAM or TYMED_HGLOBAL;
|
|
);
|
|
|
|
implementation
|
|
|
|
uses
|
|
VirtualTrees.Clipboard,
|
|
VirtualTrees.DataObject;
|
|
|
|
type
|
|
TBaseVirtualTreeCracker = class(TBaseVirtualTree);
|
|
|
|
TVTDragManagerHelper = class helper for TVTDragManager
|
|
function TreeView : TBaseVirtualTreeCracker;
|
|
end;
|
|
|
|
|
|
//----------------- TEnumFormatEtc -------------------------------------------------------------------------------------
|
|
|
|
constructor TEnumFormatEtc.Create(const AFormatEtcArray : TFormatEtcArray);
|
|
var
|
|
I : Integer;
|
|
begin
|
|
inherited Create;
|
|
// Make a local copy of the format data.
|
|
SetLength(FFormatEtcArray, Length(AFormatEtcArray));
|
|
for I := 0 to High(AFormatEtcArray) do
|
|
FFormatEtcArray[I] := AFormatEtcArray[I];
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TEnumFormatEtc.Clone(out Enum : IEnumFormatEtc) : HResult;
|
|
var
|
|
AClone : TEnumFormatEtc;
|
|
begin
|
|
Result := S_OK;
|
|
try
|
|
AClone := TEnumFormatEtc.Create(FFormatEtcArray);
|
|
AClone.FCurrentIndex := FCurrentIndex;
|
|
Enum := AClone as IEnumFormatEtc;
|
|
except
|
|
Result := E_FAIL;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TEnumFormatEtc.Next(celt : Integer; out elt; pceltFetched : PLongint) : HResult;
|
|
var
|
|
CopyCount : Integer;
|
|
begin
|
|
Result := S_FALSE;
|
|
CopyCount := Length(FFormatEtcArray) - FCurrentIndex;
|
|
if celt < CopyCount then
|
|
CopyCount := celt;
|
|
if CopyCount > 0 then
|
|
begin
|
|
Move(FFormatEtcArray[FCurrentIndex], elt, CopyCount * SizeOf(TFormatEtc));
|
|
Inc(FCurrentIndex, CopyCount);
|
|
Result := S_OK;
|
|
end;
|
|
if Assigned(pceltFetched) then
|
|
pceltFetched^ := CopyCount;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TEnumFormatEtc.Reset : HResult;
|
|
begin
|
|
FCurrentIndex := 0;
|
|
Result := S_OK;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TEnumFormatEtc.Skip(celt : Integer) : HResult;
|
|
begin
|
|
if FCurrentIndex + celt < High(FFormatEtcArray) then
|
|
begin
|
|
Inc(FCurrentIndex, celt);
|
|
Result := S_OK;
|
|
end
|
|
else
|
|
Result := S_FALSE;
|
|
end;
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
// OLE drag and drop support classes
|
|
// This is quite heavy stuff (compared with the VCL implementation) but is much better suited to fit the needs
|
|
// of DD'ing various kinds of virtual data and works also between applications.
|
|
|
|
|
|
//----------------- TVTDragManager -------------------------------------------------------------------------------------
|
|
|
|
constructor TVTDragManager.Create(AOwner : TBaseVirtualTree);
|
|
begin
|
|
inherited Create;
|
|
FOwner := AOwner;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
destructor TVTDragManager.Destroy;
|
|
begin
|
|
// Set the owner's reference to us to nil otherwise it will access an invalid pointer
|
|
// after our desctruction is complete.
|
|
TreeView.ClearDragManager;
|
|
inherited;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.GetDataObject : IDataObject;
|
|
begin
|
|
// When the owner tree starts a drag operation then it gets a data object here to pass it to the OLE subsystem.
|
|
// In this case there is no local reference to a data object and one is created (but not stored).
|
|
// If there is a local reference then the owner tree is currently the drop target and the stored interface is
|
|
// that of the drag initiator.
|
|
if Assigned(FDataObject) then
|
|
Result := FDataObject
|
|
else
|
|
begin
|
|
Result := TreeView.DoCreateDataObject;
|
|
if (Result = nil) and not Assigned(TreeView.OnCreateDataObject) then
|
|
// Do not create a TVTDataObject if the event handler explicitely decided not to supply one, issue #736.
|
|
Result := TVTDataObject.Create(FOwner, False) as IDataObject;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.GetDragSource : TBaseVirtualTree;
|
|
begin
|
|
Result := FDragSource;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.GetIsDropTarget : Boolean;
|
|
begin
|
|
Result := FIsDropTarget;
|
|
end;
|
|
|
|
class function TVTDragManager.GetTreeFromDataObject(const DataObject: TVTDragDataObject): TBaseVirtualTree;
|
|
// Returns the owner/sender of the given data object by means of a special clipboard format
|
|
// or nil if the sender is in another process or no virtual tree at all.
|
|
|
|
var
|
|
Medium: TStgMedium;
|
|
Data: PVTReference;
|
|
|
|
begin
|
|
Result := nil;
|
|
if Assigned(DataObject) then
|
|
begin
|
|
StandardOLEFormat.cfFormat := CF_VTREFERENCE;
|
|
if DataObject.GetData(StandardOLEFormat, Medium) = S_OK then
|
|
begin
|
|
Data := GlobalLock(Medium.hGlobal);
|
|
if Assigned(Data) then
|
|
begin
|
|
if Data.Process = GetCurrentProcessID then
|
|
Result := Data.Tree;
|
|
GlobalUnlock(Medium.hGlobal);
|
|
end;
|
|
ReleaseStgMedium(Medium);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.DragEnter(const DataObject : IDataObject; KeyState : Integer; Pt : TPoint; var Effect : Integer) : HResult;
|
|
var
|
|
Medium: TStgMedium;
|
|
HeaderFormatEtc: TFormatEtc;
|
|
begin
|
|
if not Assigned(FDropTargetHelper) then
|
|
CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, FDropTargetHelper);
|
|
|
|
FDataObject := DataObject;
|
|
FIsDropTarget := True;
|
|
|
|
SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, @FFullDragging, 0);
|
|
// If full dragging of window contents is disabled in the system then our tree windows will be locked
|
|
// and cannot be updated during a drag operation. With the following call painting is again enabled.
|
|
if not FFullDragging then
|
|
LockWindowUpdate(0);
|
|
if Assigned(FDropTargetHelper) and FFullDragging then
|
|
begin
|
|
if (toAutoScroll in TreeView.TreeOptions.AutoOptions) and (toAcceptOLEDrop in TreeView.TreeOptions.MiscOptions) then
|
|
FDropTargetHelper.DragEnter(FOwner.Handle, DataObject, Pt, Effect)
|
|
else
|
|
FDropTargetHelper.DragEnter(0, DataObject, Pt, Effect); // Do not pass handle, otherwise the IDropTargetHelper will perform autoscroll. Issue #486
|
|
end;
|
|
FDragSource := GetTreeFromDataObject(DataObject);
|
|
Result := TreeView.DragEnter(KeyState, Pt, Effect);
|
|
HeaderFormatEtc := StandardOLEFormat;
|
|
HeaderFormatEtc.cfFormat := CF_VTHEADERREFERENCE;
|
|
if (DataObject.GetData(HeaderFormatEtc, Medium) = S_OK) and (FDragSource = FOWner) then
|
|
begin
|
|
FHeader := FDragSource.Header;
|
|
FDRagSource := nil;
|
|
end
|
|
else
|
|
begin
|
|
fHeader := nil;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.DragLeave : HResult;
|
|
begin
|
|
if Assigned(FDropTargetHelper) and FFullDragging then
|
|
FDropTargetHelper.DragLeave;
|
|
|
|
if (toAcceptOLEDrop in TreeView.TreeOptions.MiscOptions) then
|
|
TreeView.DragLeave;
|
|
FIsDropTarget := False;
|
|
FDragSource := nil;
|
|
FDataObject := nil;
|
|
fHeader := nil;
|
|
Result := NOERROR;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.DragOver(KeyState : Integer; Pt : TPoint; var Effect : Integer) : HResult;
|
|
begin
|
|
if Assigned(FDropTargetHelper) and FFullDragging then
|
|
FDropTargetHelper.DragOver(Pt, Effect);
|
|
|
|
Result := NOERROR;
|
|
if Assigned(fHeader) then
|
|
begin
|
|
TreeView.Header.DragTo(Pt);
|
|
end
|
|
else if (toAcceptOLEDrop in TreeView.TreeOptions.MiscOptions) then
|
|
Result := TreeView.DragOver(FDragSource, KeyState, dsDragMove, Pt, Effect);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.Drop(const DataObject : IDataObject; KeyState : Integer; Pt : TPoint; var Effect : Integer) : HResult;
|
|
begin
|
|
if Assigned(FDropTargetHelper) and FFullDragging then
|
|
FDropTargetHelper.Drop(DataObject, Pt, Effect);
|
|
|
|
if Assigned(fHeader) then
|
|
begin
|
|
FHeader.ColumnDropped(Pt);
|
|
Result := NO_ERROR;
|
|
end
|
|
else
|
|
Result := TreeView.DragDrop(DataObject, KeyState, Pt, Effect);
|
|
FIsDropTarget := False;
|
|
FDataObject := nil;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TVTDragManager.ForceDragLeave;
|
|
// Some drop targets, e.g. Internet Explorer leave a drag image on screen instead removing it when they receive
|
|
// a drop action. This method calls the drop target helper's DragLeave method to ensure it removes the drag image from
|
|
// screen. Unfortunately, sometimes not even this does help (e.g. when dragging text from VT to a text field in IE).
|
|
begin
|
|
if Assigned(FDropTargetHelper) and FFullDragging then
|
|
FDropTargetHelper.DragLeave;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.GiveFeedback(Effect : Integer) : HResult;
|
|
begin
|
|
Result := DRAGDROP_S_USEDEFAULTCURSORS;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TVTDragManager.QueryContinueDrag(EscapePressed : BOOL; KeyState : Integer) : HResult;
|
|
var
|
|
RButton, LButton : Boolean;
|
|
begin
|
|
LButton := (KeyState and MK_LBUTTON) <> 0;
|
|
RButton := (KeyState and MK_RBUTTON) <> 0;
|
|
|
|
// Drag'n drop canceled by pressing both mouse buttons or Esc?
|
|
if (LButton and RButton) or EscapePressed then
|
|
Result := DRAGDROP_S_CANCEL
|
|
else
|
|
// Drag'n drop finished?
|
|
if not (LButton or RButton) then
|
|
Result := DRAGDROP_S_DROP
|
|
else
|
|
Result := S_OK;
|
|
end;
|
|
|
|
{ TVTDragManagerHelper }
|
|
|
|
function TVTDragManagerHelper.TreeView : TBaseVirtualTreeCracker;
|
|
begin
|
|
Result := TBaseVirtualTreeCracker(FOwner);
|
|
end;
|
|
|
|
end.
|