Subversion Repositories decoder

Rev

Blame | Last modification | View Log | RSS feed

  1. unit DropTarget;
  2.  
  3. // -----------------------------------------------------------------------------
  4. // Project:         Drag and Drop Component Suite
  5. // Module:          DropTarget
  6. // Description:     Implements the drop target base classes which allows your
  7. //                  application to accept data dropped on it from other
  8. //                  applications.
  9. // Version:         4.0
  10. // Date:            18-MAY-2001
  11. // Target:          Win32, Delphi 5-6
  12. // Authors:         Anders Melander, anders@melander.dk, http://www.melander.dk
  13. // Copyright        © 1997-2001 Angus Johnson & Anders Melander
  14. // -----------------------------------------------------------------------------
  15. // General changes:
  16. // - Some component glyphs has changed.
  17. // - New components:
  18. //   * TDropMetaFileTarget
  19. //   * TDropImageTarget
  20. //   * TDropSuperTarget
  21. //   * Replaced all use of KeysToShiftState with KeysToShiftStatePlus for
  22. //     correct mapping of Alt key.
  23. // TCustomDropTarget changes:
  24. // - New protected method SetDataObject.
  25. //   Provides write access to DataObject property for use in descendant classes.
  26. // - New protected methods: GetPreferredDropEffect and SetPerformedDropEffect.
  27. // - New protected method DoUnregister handles unregistration of all or
  28. //   individual targets.
  29. // - Unregister method has been overloaded to handle multiple drop targets
  30. //   (Delphi 4 and later only).
  31. // - All private methods has been made protected.
  32. // - New public methods: FindTarget and FindNearestTarget.
  33. //   For use with multiple drop targets.
  34. // - New published property MultiTarget enables multiple drop targets.
  35. // - New public property Targets for support of multiple drop targets.
  36. // - Visibility of Target property has changed from public to published and
  37. //   has been made writable.
  38. // - PasteFromClipboard method now handles all formats via DoGetData.
  39. // - Now "handles" situations where the target window handle is recreated.
  40. // - Implemented TCustomDropTarget.Assign to assign from TClipboard and any object
  41. //   which implements IDataObject.
  42. // - Added support for optimized moves and delete-on-paste with new
  43. //   OptimizedMove property.
  44. // - Fixed inconsistency between GetValidDropEffect and standard IDropTarget
  45. //   behaviour.
  46. // - The HasValidFormats method has been made public and now accepts an
  47. //   IDataObject as a parameter.
  48. // - The OnGetDropEffect Effect parameter is now initialized to the drop
  49. //   source's allowed drop effect mask prior to entry.
  50. // - Added published AutoScroll property and OnScroll even´t and public
  51. //   NoScrollZone property.
  52. //   Auto scroling can now be completely customized via the OnDragEnter,
  53. //   OnDragOver OnGetDropEffect and OnScroll events and the above properties.
  54. // - Added support for IDropTargetHelper interface.
  55. // - Added support for IAsyncOperation interface.
  56. // - New OnStartAsyncTransfer and OnEndAsyncTransfer events.
  57. //
  58. // TDropDummy changes:
  59. // - Bug in HasValidFormats fixed. Spotted by David Polberger.
  60. //   Return value changed from True to False.
  61. //
  62. // -----------------------------------------------------------------------------
  63.  
  64. interface
  65.  
  66. uses
  67.   DragDrop,
  68.   Windows, ActiveX, Classes, Controls, CommCtrl, ExtCtrls, Forms;
  69.  
  70. {$include DragDrop.inc}
  71.  
  72. ////////////////////////////////////////////////////////////////////////////////
  73. //
  74. //              TControlList
  75. //
  76. ////////////////////////////////////////////////////////////////////////////////
  77. // List of TWinControl objects.
  78. // Used for the TCustomDropTarget.Targets property.
  79. ////////////////////////////////////////////////////////////////////////////////
  80. type
  81.   TControlList = class(TObject)
  82.   private
  83.     FList: TList;
  84.     function GetControl(AIndex: integer): TWinControl;
  85.     function GetCount: integer;
  86.   protected
  87.     function Add(AControl: TWinControl): integer;
  88.     procedure Insert(Index: Integer; AControl: TWinControl);
  89.     procedure Remove(AControl: TWinControl);
  90.     procedure Delete(AIndex: integer);
  91.   public
  92.     constructor Create;
  93.     destructor Destroy; override;
  94.     function IndexOf(AControl: TWinControl): integer;
  95.     property Count: integer read GetCount;
  96.     property Controls[AIndex: integer]: TWinControl read GetControl; default;
  97.   end;
  98.  
  99.  
  100. ////////////////////////////////////////////////////////////////////////////////
  101. //
  102. //              TCustomDropTarget
  103. //
  104. ////////////////////////////////////////////////////////////////////////////////
  105. // Top level abstract base class for all drop target classes.
  106. // Implements the IDropTarget and IDataObject interfaces.
  107. // Do not derive from TCustomDropTarget! Instead derive from TCustomDropTarget.
  108. // TCustomDropTarget will be replaced by/renamed to TCustomDropTarget in a future
  109. // version.
  110. ////////////////////////////////////////////////////////////////////////////////
  111. type
  112.   TScrolDirection = (sdUp, sdDown, sdLeft, sdRight);
  113.   TScrolDirections = set of TScrolDirection;
  114.  
  115.   TDropTargetScrollEvent = procedure(Sender: TObject; Point: TPoint;
  116.     var Scroll: TScrolDirections; var Interval: integer) of object;
  117.  
  118.   TScrollBars = set of TScrollBarKind;
  119.  
  120.   TDropTargetEvent = procedure(Sender: TObject; ShiftState: TShiftState;
  121.     APoint: TPoint; var Effect: Longint) of object;
  122.  
  123.   TCustomDropTarget = class(TDragDropComponent, IDropTarget)
  124.   private
  125.     FDataObject         : IDataObject;
  126.     FDragTypes          : TDragTypes;
  127.     FGetDataOnEnter     : boolean;
  128.     FOnEnter            : TDropTargetEvent;
  129.     FOnDragOver         : TDropTargetEvent;
  130.     FOnLeave            : TNotifyEvent;
  131.     FOnDrop             : TDropTargetEvent;
  132.     FOnGetDropEffect    : TDropTargetEvent;
  133.     FOnScroll           : TDropTargetScrollEvent;
  134.     FTargets            : TControlList;
  135.     FMultiTarget        : boolean;
  136.     FOptimizedMove      : boolean;
  137.     FTarget             : TWinControl;
  138.  
  139.     FImages             : TImageList;
  140.     FDragImageHandle    : HImageList;
  141.     FShowImage          : boolean;
  142.     FImageHotSpot       : TPoint;
  143.     FDropTargetHelper   : IDropTargetHelper;
  144.     // FLastPoint points to where DragImage was last painted (used internally)
  145.     FLastPoint          : TPoint;
  146.     // Auto scrolling enables scrolling of target window during drags and
  147.     // paints any drag image 'cleanly'.
  148.     FScrollBars         : TScrollBars;
  149.     FScrollTimer        : TTimer;
  150.     FAutoScroll         : boolean;
  151.     FNoScrollZone       : TRect;
  152.     FIsAsync            : boolean;
  153.     FOnEndAsyncTransfer : TNotifyEvent;
  154.     FOnStartAsyncTransfer: TNotifyEvent;
  155.     FAllowAsync          : boolean;
  156.   protected
  157.     // IDropTarget  implementation
  158.     function DragEnter(const DataObj: IDataObject; grfKeyState: Longint;
  159.       pt: TPoint; var dwEffect: Longint): HRESULT; stdcall;
  160.     function DragOver(grfKeyState: Longint; pt: TPoint;
  161.       var dwEffect: Longint): HRESULT; stdcall;
  162.     function DragLeave: HRESULT; stdcall;
  163.     function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint;
  164.       var dwEffect: Longint): HRESULT; stdcall;
  165.  
  166.     procedure DoEnter(ShiftState: TShiftState; Point: TPoint; var Effect: Longint); virtual;
  167.     procedure DoDragOver(ShiftState: TShiftState; Point: TPoint; var Effect: Longint); virtual;
  168.     procedure DoDrop(ShiftState: TShiftState; Point: TPoint; var Effect: Longint); virtual;
  169.     procedure DoLeave; virtual;
  170.     procedure DoOnPaste(var Effect: Integer); virtual;
  171.     procedure DoScroll(Point: TPoint; var Scroll: TScrolDirections;
  172.       var Interval: integer); virtual;
  173.  
  174.     function GetData(Effect: longInt): boolean; virtual;
  175.     function DoGetData: boolean; virtual; abstract;
  176.     procedure ClearData; virtual; abstract;
  177.     function GetValidDropEffect(ShiftState: TShiftState; pt: TPoint;
  178.       dwEffect: LongInt): LongInt; virtual; // V4: Improved
  179.     function GetPreferredDropEffect: LongInt; virtual; // V4: New
  180.     function SetPerformedDropEffect(Effect: LongInt): boolean; virtual; // V4: New
  181.     function SetPasteSucceded(Effect: LongInt): boolean; virtual; // V4: New
  182.     procedure DoUnregister(ATarget: TWinControl); // V4: New
  183.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  184.     function GetTarget: TWinControl;
  185.     procedure SetTarget(const Value: TWinControl);
  186.     procedure DoAutoScroll(Sender: TObject); // V4: Renamed from DoTargetScroll.
  187.     procedure SetShowImage(Show: boolean);
  188.     procedure SetDataObject(Value: IDataObject); // V4: New
  189.     procedure DoEndAsyncTransfer(Sender: TObject);
  190.     property DropTargetHelper: IDropTargetHelper read FDropTargetHelper;
  191.   public
  192.     constructor Create(AOwner: TComponent); override;
  193.     destructor Destroy; override;
  194.     procedure Register(ATarget: TWinControl);
  195. {$ifdef VER12_PLUS}
  196.     procedure Unregister(ATarget: TWinControl = nil); // V4: New
  197. {$else}
  198.     procedure Unregister;
  199. {$endif}
  200.     function FindTarget(p: TPoint): TWinControl; virtual; // V4: New
  201.     function FindNearestTarget(p: TPoint): TWinControl; // V4: New
  202.     procedure Assign(Source: TPersistent); override; // V4: New
  203.     function HasValidFormats(ADataObject: IDataObject): boolean; virtual; abstract; // V4: Improved
  204.     function PasteFromClipboard: longint; virtual; // V4: Improved
  205.     property DataObject: IDataObject read FDataObject;
  206.     property Targets: TControlList read FTargets; // V4: New
  207.     property NoScrollZone: TRect read FNoScrollZone write FNoScrollZone; // V4: New
  208.     property AsyncTransfer: boolean read FIsAsync;
  209.   published
  210.     property Dragtypes: TDragTypes read FDragTypes write FDragTypes;
  211.     property GetDataOnEnter: Boolean read FGetDataOnEnter write FGetDataOnEnter;
  212.     // Events...
  213.     property OnEnter: TDropTargetEvent read FOnEnter write FOnEnter;
  214.     property OnDragOver: TDropTargetEvent read FOnDragOver write FOnDragOver;
  215.     property OnLeave: TNotifyEvent read FOnLeave write FOnLeave;
  216.     property OnDrop: TDropTargetEvent read FOnDrop write FOnDrop;
  217.     property OnGetDropEffect: TDropTargetEvent read FOnGetDropEffect
  218.       write FOnGetDropEffect; // V4: Improved
  219.     property OnScroll: TDropTargetScrollEvent read FOnScroll write FOnScroll; // V4: New
  220.     property OnStartAsyncTransfer: TNotifyEvent read FOnStartAsyncTransfer
  221.       write FOnStartAsyncTransfer;
  222.     property OnEndAsyncTransfer: TNotifyEvent read FOnEndAsyncTransfer
  223.       write FOnEndAsyncTransfer;
  224.     // Drag Images...
  225.     property ShowImage: boolean read FShowImage write SetShowImage;
  226.     // Target
  227.     property Target: TWinControl read GetTarget write SetTarget; // V4: Improved
  228.     property MultiTarget: boolean read FMultiTarget write FMultiTarget default False; // V4: New
  229.     // Auto scroll
  230.     property AutoScroll: boolean read FAutoScroll write FAutoScroll default True; // V4: New
  231.     // Misc
  232.     property OptimizedMove: boolean read FOptimizedMove write FOptimizedMove default False; // V4: New
  233.     // Async transfer...
  234.     property AllowAsyncTransfer: boolean read FAllowAsync write FAllowAsync;
  235.   end;
  236.  
  237.  
  238. ////////////////////////////////////////////////////////////////////////////////
  239. //
  240. //              TDropTarget
  241. //
  242. ////////////////////////////////////////////////////////////////////////////////
  243. // Deprecated base class for all drop target components.
  244. // Replaced by the TCustomDropTarget class.
  245. ////////////////////////////////////////////////////////////////////////////////
  246.   TDropTarget = class(TCustomDropTarget)
  247.   end;
  248.  
  249. ////////////////////////////////////////////////////////////////////////////////
  250. //
  251. //              TDropDummy
  252. //
  253. ////////////////////////////////////////////////////////////////////////////////
  254. // The sole purpose of this component is to enable drag images to be displayed
  255. // over the registered TWinControl(s). The component does not accept any drops.
  256. ////////////////////////////////////////////////////////////////////////////////
  257.   TDropDummy = class(TCustomDropTarget)
  258.   protected
  259.     procedure ClearData; override;
  260.     function DoGetData: boolean; override;
  261.   public
  262.     function HasValidFormats(ADataObject: IDataObject): boolean; override;
  263.   end;
  264.  
  265.  
  266. ////////////////////////////////////////////////////////////////////////////////
  267. //
  268. //              TCustomDropMultiTarget
  269. //
  270. ////////////////////////////////////////////////////////////////////////////////
  271. // Drop target base class which can accept multiple formats.
  272. ////////////////////////////////////////////////////////////////////////////////
  273.   TAcceptFormatEvent = procedure(Sender: TObject;
  274.     const DataFormat: TCustomDataFormat; var Accept: boolean) of object;
  275.  
  276.   TCustomDropMultiTarget = class(TCustomDropTarget)
  277.   private
  278.     FOnAcceptFormat: TAcceptFormatEvent;
  279.   protected
  280.     procedure ClearData; override;
  281.     function DoGetData: boolean; override;
  282.     procedure DoAcceptFormat(const DataFormat: TCustomDataFormat;
  283.       var Accept: boolean); virtual;
  284.     property OnAcceptFormat: TAcceptFormatEvent read FOnAcceptFormat
  285.       write FOnAcceptFormat;
  286.   public
  287.     constructor Create(AOwner: TComponent); override;
  288.     destructor Destroy; override;
  289.     function HasValidFormats(ADataObject: IDataObject): boolean; override;
  290.     property DataFormats;
  291.   end;
  292.  
  293. ////////////////////////////////////////////////////////////////////////////////
  294. //
  295. //              TDropEmptyTarget
  296. //
  297. ////////////////////////////////////////////////////////////////////////////////
  298. // Do-nothing target for use with TDataFormatAdapter and such
  299. ////////////////////////////////////////////////////////////////////////////////
  300.   TDropEmptyTarget = class(TCustomDropMultiTarget);
  301.  
  302.  
  303. ////////////////////////////////////////////////////////////////////////////////
  304. //
  305. //              Misc.
  306. //
  307. ////////////////////////////////////////////////////////////////////////////////
  308.  
  309.  
  310. ////////////////////////////////////////////////////////////////////////////////
  311. //
  312. //              Component registration
  313. //
  314. ////////////////////////////////////////////////////////////////////////////////
  315. procedure Register;
  316.  
  317.  
  318. (*******************************************************************************
  319. **
  320. **                      IMPLEMENTATION
  321. **
  322. *******************************************************************************)
  323. implementation
  324.  
  325. uses
  326.   DragDropFormats,
  327.   ComObj,
  328.   SysUtils,
  329.   Graphics,
  330.   Messages,
  331.   ShlObj,
  332.   ClipBrd,
  333.   ComCtrls;
  334.  
  335. resourcestring
  336.   sAsyncBusy = 'Can''t clear data while async data transfer is in progress';
  337.   // sRegisterFailed    = 'Failed to register %s as a drop target';
  338.   // sUnregisterActiveTarget = 'Can''t unregister target while drag operation is in progress';
  339.  
  340. ////////////////////////////////////////////////////////////////////////////////
  341. //
  342. //              Component registration
  343. //
  344. ////////////////////////////////////////////////////////////////////////////////
  345. procedure Register;
  346. begin
  347.   RegisterComponents(DragDropComponentPalettePage, [TDropEmptyTarget, TDropDummy]);
  348. end;
  349.  
  350.  
  351. ////////////////////////////////////////////////////////////////////////////////
  352. //
  353. //              Misc.
  354. //
  355. ////////////////////////////////////////////////////////////////////////////////
  356.  
  357.  
  358. ////////////////////////////////////////////////////////////////////////////////
  359. //
  360. //              TControlList
  361. //
  362. ////////////////////////////////////////////////////////////////////////////////
  363. constructor TControlList.Create;
  364. begin
  365.   inherited Create;
  366.   FList := TList.Create;
  367. end;
  368.  
  369. destructor TControlList.Destroy;
  370. begin
  371.   FList.Free;
  372.   inherited Destroy;
  373. end;
  374.  
  375. function TControlList.Add(AControl: TWinControl): integer;
  376. begin
  377.   Result := FList.Add(AControl);
  378. end;
  379.  
  380. procedure TControlList.Insert(Index: Integer; AControl: TWinControl);
  381. begin
  382.   FList.Insert(Index, AControl);
  383. end;
  384.  
  385. procedure TControlList.Delete(AIndex: integer);
  386. begin
  387.   FList.Delete(AIndex);
  388. end;
  389.  
  390. function TControlList.IndexOf(AControl: TWinControl): integer;
  391. begin
  392.   Result := FList.IndexOf(AControl);
  393. end;
  394.  
  395. function TControlList.GetControl(AIndex: integer): TWinControl;
  396. begin
  397.   Result := TWinControl(FList[AIndex]);
  398. end;
  399.  
  400. function TControlList.GetCount: integer;
  401. begin
  402.   Result := FList.Count;
  403. end;
  404.  
  405. procedure TControlList.Remove(AControl: TWinControl);
  406. begin
  407.   FList.Remove(AControl);
  408. end;
  409.  
  410.  
  411. ////////////////////////////////////////////////////////////////////////////////
  412. //
  413. //              TCustomDropTarget
  414. //
  415. ////////////////////////////////////////////////////////////////////////////////
  416. constructor TCustomDropTarget.Create(AOwner: TComponent);
  417. var
  418.   bm                    : TBitmap;
  419. begin
  420.   inherited Create(AOwner);
  421.   FScrollTimer := TTimer.Create(Self);
  422.   FScrollTimer.Enabled := False;
  423.   FScrollTimer.OnTimer := DoAutoScroll;
  424.  
  425.   // Note: Normally we would call _AddRef or coLockObjectExternal(Self) here to
  426.   // make sure that the component wasn't deleted prematurely (e.g. after a call
  427.   // to RegisterDragDrop), but since our ancestor class TInterfacedComponent
  428.   // disables reference counting, we do not need to do so.
  429.  
  430.   FGetDataOnEnter := False;
  431.   FTargets :=  TControlList.Create;
  432.  
  433.   FImages := TImageList.Create(Self);
  434.   // Create a blank image for FImages which we will use to hide any cursor
  435.   // 'embedded' in a drag image.
  436.   // This avoids the possibility of two cursors showing.
  437.   bm := TBitmap.Create;
  438.   try
  439.     bm.Height := 32;
  440.     bm.Width := 32;
  441.     bm.Canvas.Brush.Color := clWindow;
  442.     bm.Canvas.FillRect(bm.Canvas.ClipRect);
  443.     FImages.AddMasked(bm, clWindow);
  444.   finally
  445.     bm.Free;
  446.   end;
  447.   FDataObject := nil;
  448.   ShowImage := True;
  449.   FMultiTarget := False;
  450.   FOptimizedMove := False;
  451.   FAutoScroll := True;
  452. end;
  453.  
  454. destructor TCustomDropTarget.Destroy;
  455. begin
  456.   FDataObject := nil;
  457.   FDropTargetHelper := nil;
  458.   Unregister;
  459.   FImages.Free;
  460.   FScrollTimer.Free;
  461.   FTargets.Free;
  462.   inherited Destroy;
  463. end;
  464.  
  465. // TDummyWinControl is declared just to expose the protected property - Font -
  466. // which is used to calculate the 'scroll margin' for the target window.
  467. type
  468.   TDummyWinControl = Class(TWinControl);
  469.  
  470. function TCustomDropTarget.DragEnter(const dataObj: IDataObject; grfKeyState: Longint;
  471.   pt: TPoint; var dwEffect: Longint): HRESULT;
  472. var
  473.   ShiftState            : TShiftState;
  474.   TargetStyles          : longint;
  475. begin
  476.   ClearData;
  477.   FDataObject := dataObj;
  478.   Result := S_OK;
  479.  
  480.   // Find the target control.
  481.   FTarget := FindTarget(pt);
  482.  
  483.   (*
  484.   ** If no target control has been registered we disable all features which
  485.   ** depends on the existence of a drop target (e.g. drag images and auto
  486.   ** scroll). Presently, this situation can only arise if the drop target is
  487.   ** being used as a drop handler (TDrophandler component).
  488.   ** Note also that if no target control exists, the mouse coordinates are
  489.   ** relative to the screen, not the control as is normally the case.
  490.   *)
  491.   if (FTarget = nil) then
  492.   begin
  493.     ShowImage := False;
  494.     AutoScroll := False;
  495.   end else
  496.   begin
  497.     pt := FTarget.ScreenToClient(pt);
  498.     FLastPoint := pt;
  499.   end;
  500.  
  501.   (*
  502.   ** Refuse the drag if we can't handle any of the data formats offered by
  503.   ** the drop source. We must return S_OK here in order for the drop to continue
  504.   ** to generate DragOver events for this drop target (needed for drag images).
  505.   *)
  506.   if HasValidFormats(FDataObject) then
  507.   begin
  508.  
  509.     FScrollBars := [];
  510.  
  511.     if (AutoScroll) then
  512.     begin
  513.       // Determine if the target control has scroll bars (and which).
  514.       TargetStyles := GetWindowLong(FTarget.Handle, GWL_STYLE);
  515.       if (TargetStyles and WS_HSCROLL <> 0) then
  516.         include(FScrollBars, sbHorizontal);
  517.       if (TargetStyles and WS_VSCROLL <> 0) then
  518.         include(FScrollBars, sbVertical);
  519.  
  520.       // The Windows UI guidelines recommends that the scroll margin be based on
  521.       // the width/height of the scroll bars:
  522.       // From "The Windows Interface Guidelines for Software Design", page 82:
  523.       //   "Use twice the width of a vertical scroll bar or height of a
  524.       //   horizontal scroll bar to determine the width of the hot zone."
  525.       // Previous versions of these components used the height of the current
  526.       // target control font as the scroll margin. Yet another approach would be
  527.       // to use the DragDropScrollInset constant.
  528.       if (FScrollBars <> []) then
  529.       begin
  530.         FNoScrollZone := FTarget.ClientRect;
  531.         if (sbVertical in FScrollBars) then
  532.           InflateRect(FNoScrollZone, 0, -GetSystemMetrics(SM_CYHSCROLL));
  533.           // InflateRect(FNoScrollZone, 0, -abs(TDummyWinControl(FTarget).Font.Height));
  534.         if (sbHorizontal in FScrollBars) then
  535.           InflateRect(FNoScrollZone, -GetSystemMetrics(SM_CXHSCROLL), 0);
  536.           // InflateRect(FNoScrollZone, -abs(TDummyWinControl(FTarget).Font.Height), 0);
  537.       end;
  538.     end;
  539.  
  540.     // It's generally more efficient to get data only if and when a drop occurs
  541.     // rather than on entering a potential target window.
  542.     // However - sometimes there is a good reason to get it here.
  543.     if FGetDataOnEnter then
  544.       if (not GetData(dwEffect)) then
  545.       begin
  546.         FDataObject := nil;
  547.         dwEffect := DROPEFFECT_NONE;
  548.         Result := DV_E_CLIPFORMAT;
  549.         exit;
  550.       end;
  551.  
  552.     ShiftState := KeysToShiftStatePlus(grfKeyState);
  553.  
  554.     // Create a default drop effect based on the shift state and allowed
  555.     // drop effects (or an OnGetDropEffect event if implemented).
  556.     dwEffect := GetValidDropEffect(ShiftState, Pt, dwEffect);
  557.  
  558.     // Generate an OnEnter event
  559.     DoEnter(ShiftState, pt, dwEffect);
  560.  
  561.     // If IDropTarget.DragEnter returns with dwEffect set to DROPEFFECT_NONE it
  562.     // means that the drop has been rejected and IDropTarget.DragOver should
  563.     // not be called (according to MSDN). Unfortunately IDropTarget.DragOver is
  564.     // called regardless of the value of dwEffect. We work around this problem
  565.     // (bug?) by setting FDataObject to nil and thus internally rejecting the
  566.     // drop in TCustomDropTarget.DragOver.
  567.     if (dwEffect = DROPEFFECT_NONE) then
  568.       FDataObject := nil;
  569.  
  570.   end else
  571.   begin
  572.     FDataObject := nil;
  573.     dwEffect := DROPEFFECT_NONE;
  574.   end;
  575.  
  576.   // Display drag image.
  577.   // Note: This was previously done prior to caling GetValidDropEffect and
  578.   // DoEnter. The SDK documentation states that IDropTargetHelper.DragEnter
  579.   // should be called last in IDropTarget.DragEnter (presumably after dwEffect
  580.   // has been modified), but Microsoft's own demo application calls it as the
  581.   // very first thing (same for all other IDropTargetHelper methods).
  582.   if ShowImage then
  583.   begin
  584.     // Attempt to create Drag Drop helper object.
  585.     // At present this is only supported on Windows 2000. If the object can't be
  586.     // created, we fall back to the old image list based method (which only
  587.     // works on Win9x).
  588.     CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER,
  589.       IDropTargetHelper, FDropTargetHelper);
  590.  
  591.     if (FDropTargetHelper <> nil) then
  592.     begin
  593.       // If the call to DragEnter fails (which it will do if the drop source
  594.       // doesn't support IDropSourceHelper or hasn't specified a drag image),
  595.       // we release the drop target helper and fall back to imagelist based
  596.       // drag images.
  597.       if (DropTargetHelper.DragEnter(FTarget.Handle, DataObj, pt, dwEffect) <> S_OK) then
  598.         FDropTargetHelper := nil;
  599.     end;
  600.  
  601.     if (FDropTargetHelper = nil) then
  602.     begin
  603.       FDragImageHandle := ImageList_GetDragImage(nil, @FImageHotSpot);
  604.       if (FDragImageHandle <> 0) then
  605.       begin
  606.         // Currently we will just replace any 'embedded' cursor with our
  607.         // blank (transparent) image otherwise we sometimes get 2 cursors ...
  608.         ImageList_SetDragCursorImage(FImages.Handle, 0, FImageHotSpot.x, FImageHotSpot.y);
  609.         with ClientPtToWindowPt(FTarget.Handle, pt) do
  610.           ImageList_DragEnter(FTarget.handle, x, y);
  611.       end;
  612.     end;
  613.   end else
  614.     FDragImageHandle := 0;
  615. end;
  616.  
  617. procedure TCustomDropTarget.DoEnter(ShiftState: TShiftState; Point: TPoint; var Effect: Longint);
  618. begin
  619.   if Assigned(FOnEnter) then
  620.     FOnEnter(Self, ShiftState, Point, Effect);
  621. end;
  622.  
  623. function TCustomDropTarget.DragOver(grfKeyState: Longint; pt: TPoint;
  624.   var dwEffect: Longint): HResult;
  625. var
  626.   ShiftState: TShiftState;
  627.   IsScrolling: boolean;
  628. begin
  629.   // Refuse drop if we dermined in DragEnter that a drop weren't possible,
  630.   // but still handle drag images provided we have a valid target.
  631.   if (FTarget = nil) then
  632.   begin
  633.     dwEffect := DROPEFFECT_NONE;
  634.     Result := E_UNEXPECTED;
  635.     exit;
  636.   end;
  637.  
  638.   pt := FTarget.ScreenToClient(pt);
  639.  
  640.   if (FDataObject <> nil) then
  641.   begin
  642.  
  643.     ShiftState := KeysToShiftStatePlus(grfKeyState);
  644.  
  645.     // Create a default drop effect based on the shift state and allowed
  646.     // drop effects (or an OnGetDropEffect event if implemented).
  647.     dwEffect := GetValidDropEffect(ShiftState, pt, dwEffect);
  648.  
  649.     // Generate an OnDragOver event
  650.     DoDragOver(ShiftState, pt, dwEffect);
  651.  
  652.     // Note: Auto scroll is detected by the GetValidDropEffect method, but can
  653.     // also be started by the user via the OnDragOver or OnGetDropEffect events.
  654.     // Auto scroll is initiated by specifying the DROPEFFECT_SCROLL value as
  655.     // part of the drop effect.
  656.  
  657.     // Start the auto scroll timer if auto scroll were requested. Do *not* rely
  658.     // on any other mechanisms to detect auto scroll since the user can only
  659.     // specify auto scroll with the DROPEFFECT_SCROLL value.
  660.     IsScrolling := (dwEffect and DROPEFFECT_SCROLL <> 0);
  661.     if (IsScrolling) and (not FScrollTimer.Enabled) then
  662.     begin
  663.       FScrollTimer.Interval := DragDropScrollDelay; // hardcoded to 100 in previous versions.
  664.       FScrollTimer.Enabled := True;
  665.     end;
  666.  
  667.     Result := S_OK;
  668.   end else
  669.   begin
  670.     // Even though this isn't an error condition per se, we must return
  671.     // an error code (e.g. E_UNEXPECTED) in order for the cursor to change
  672.     // to DROPEFFECT_NONE.
  673.     IsScrolling := False;
  674.     Result := DV_E_CLIPFORMAT;
  675.   end;
  676.  
  677.   // Move drag image
  678.   if (DropTargetHelper <> nil) then
  679.   begin
  680.     OleCheck(DropTargetHelper.DragOver(pt, dwEffect));
  681.   end else
  682.   if (FDragImageHandle <> 0) then
  683.   begin
  684.     if (not IsScrolling) and ((FLastPoint.x <> pt.x) or (FLastPoint.y <> pt.y)) then
  685.       with ClientPtToWindowPt(FTarget.Handle, pt) do
  686.         ImageList_DragMove(x, y);
  687.   end;
  688.  
  689.   FLastPoint := pt;
  690. end;
  691.  
  692. procedure TCustomDropTarget.DoDragOver(ShiftState: TShiftState; Point: TPoint; var Effect: Longint);
  693. begin
  694.   if Assigned(FOnDragOver) then
  695.     FOnDragOver(Self, ShiftState, Point, Effect);
  696. end;
  697.  
  698. function TCustomDropTarget.DragLeave: HResult;
  699. begin
  700.   ClearData;
  701.   FScrollTimer.Enabled := False;
  702.  
  703.   FDataObject := nil;
  704.  
  705.   if (DropTargetHelper <> nil) then
  706.   begin
  707.     DropTargetHelper.DragLeave;
  708.   end else
  709.     if (FDragImageHandle <> 0) then
  710.       ImageList_DragLeave(FTarget.Handle);
  711.  
  712.   // Generate an OnLeave event.
  713.   // Protect resources against exceptions in event handler.
  714.   try
  715.     DoLeave;
  716.   finally
  717.     FTarget := nil;
  718.     FDropTargetHelper := nil;
  719.   end;
  720.  
  721.   Result := S_OK;
  722. end;
  723.  
  724. procedure TCustomDropTarget.DoLeave;
  725. begin
  726.   if Assigned(FOnLeave) then
  727.     FOnLeave(Self);
  728. end;
  729.  
  730. function TCustomDropTarget.Drop(const dataObj: IDataObject; grfKeyState: Longint;
  731.   pt: TPoint; var dwEffect: Longint): HResult;
  732. var
  733.   ShiftState: TShiftState;
  734.   ClientPt: TPoint;
  735. begin
  736.   FScrollTimer.Enabled := False;
  737.  
  738.   // Protect resources against exceptions in OnDrop event handler.
  739.   try
  740.     // Refuse drop if we have lost the data object somehow.
  741.     // This can happen if the drop is rejected in one of the other IDropTarget
  742.     // methods (e.g. DragOver).
  743.     if (FDataObject = nil) then
  744.     begin
  745.       dwEffect := DROPEFFECT_NONE;
  746.       Result := E_UNEXPECTED;
  747.     end else
  748.     begin
  749.  
  750.       ShiftState := KeysToShiftStatePlus(grfKeyState);
  751.  
  752.       // Create a default drop effect based on the shift state and allowed
  753.       // drop effects (or an OnGetDropEffect event if implemented).
  754.       if (FTarget <> nil) then
  755.         ClientPt := FTarget.ScreenToClient(pt)
  756.       else
  757.         ClientPt := pt;
  758.       dwEffect := GetValidDropEffect(ShiftState, ClientPt, dwEffect);
  759.  
  760.       // Get data from source and generate an OnDrop event unless we failed to
  761.       // get data.
  762.       if (FGetDataOnEnter) or (GetData(dwEffect)) then
  763.         DoDrop(ShiftState, ClientPt, dwEffect)
  764.       else
  765.         dwEffect := DROPEFFECT_NONE;
  766.       Result := S_OK;
  767.     end;
  768.  
  769.     if (DropTargetHelper <> nil) then
  770.     begin
  771.       DropTargetHelper.Drop(DataObj, pt, dwEffect);
  772.     end else
  773.       if (FDragImageHandle <> 0) and (FTarget <> nil) then
  774.         ImageList_DragLeave(FTarget.Handle);
  775.   finally
  776.     // clean up!
  777.     ClearData;
  778.     FDataObject := nil;
  779.     FDropTargetHelper := nil;
  780.     FTarget := nil;
  781.   end;
  782. end;
  783.  
  784. procedure TCustomDropTarget.DoDrop(ShiftState: TShiftState; Point: TPoint; var Effect: Longint);
  785. begin
  786.   if Assigned(FOnDrop) then
  787.     FOnDrop(Self, ShiftState, Point, Effect);
  788.  
  789.   (*
  790.   Optimized move (from MSDN):
  791.  
  792.   Scenario: A file is moved from the file system to a namespace extension using
  793.   an optimized move.
  794.  
  795.   In a conventional move operation, the target makes a copy of the data and the
  796.   source deletes the original. This procedure can be inefficient because it
  797.   requires two copies of the data. With large objects such as databases, a
  798.   conventional move operation might not even be practical.
  799.  
  800.   With an optimized move, the target uses its understanding of how the data is
  801.   stored to handle the entire move operation. There is never a second copy of
  802.   the data, and there is no need for the source to delete the original data.
  803.   Shell data is well suited to optimized moves because the target can handle the
  804.   entire operation using the shell API. A typical example is moving files. Once
  805.   the target has the path of a file to be moved, it can use SHFileOperation to
  806.   move it. There is no need for the source to delete the original file.
  807.  
  808.   Note The shell normally uses an optimized move to move files. To handle shell
  809.   data transfer properly, your application must be capable of detecting and
  810.   handling an optimized move.
  811.  
  812.   Optimized moves are handled in the following way:
  813.  
  814.   1) The source calls DoDragDrop with the dwEffect parameter set to
  815.      DROPEFFECT_MOVE to indicate that the source objects can be moved.
  816.   2) The target receives the DROPEFFECT_MOVE value through one of its
  817.      IDropTarget methods, indicating that a move is allowed.
  818.   3) The target either copies the object (unoptimized move) or moves the object
  819.      (optimized move).
  820.   4) The target then tells the source whether it needs to delete the original
  821.      data.
  822.      An optimized move is the default operation, with the data deleted by the
  823.      target. To inform the source that an optimized move was performed:
  824.      - The target sets the pdwEffect value it received through its
  825.        IDropTarget::Drop method to some value other than DROPEFFECT_MOVE. It is
  826.        typically set to either DROPEFFECT_NONE or DROPEFFECT_COPY. The value
  827.        will be returned to the source by DoDragDrop.
  828.      - The target also calls the data object's IDataObject::SetData method and
  829.        passes it a CFSTR_PERFORMEDDROPEFFECT format identifier set to
  830.        DROPEFFECT_NONE. This method call is necessary because some drop targets
  831.        might not set the pdwEffect parameter of DoDragDrop properly. The
  832.        CFSTR_PERFORMEDDROPEFFECT format is the reliable way to indicate that an
  833.        optimized move has taken place.
  834.      If the target did an unoptimized move, the data must be deleted by the
  835.      source. To inform the source that an unoptimized move was performed:
  836.      - The target sets the pdwEffect value it received through its
  837.        IDropTarget::Drop method to DROPEFFECT_MOVE. The value will be returned
  838.        to the source by DoDragDrop.
  839.      - The target also calls the data object's IDataObject::SetData method and
  840.        passes it a CFSTR_PERFORMEDDROPEFFECT format identifier set to
  841.        DROPEFFECT_MOVE. This method call is necessary because some drop targets
  842.        might not set the pdwEffect parameter of DoDragDrop properly. The
  843.        CFSTR_PERFORMEDDROPEFFECT format is the reliable way to indicate that an
  844.        unoptimized move has taken place.
  845.   5) The source inspects the two values that can be returned by the target. If
  846.      both are set to DROPEFFECT_MOVE, it completes the unoptimized move by
  847.      deleting the original data. Otherwise, the target did an optimized move and
  848.      the original data has been deleted.
  849.   *)
  850.  
  851.   // TODO : Why isn't this code in the Drop method?
  852.   // Report performed drop effect back to data originator.
  853.   if (Effect <> DROPEFFECT_NONE) then
  854.   begin
  855.     // If the transfer was an optimized move operation (target deletes data),
  856.     // we convert the move operation to a copy operation to prevent that the
  857.     // source deletes the data.
  858.     if (FOptimizedMove) and (Effect = DROPEFFECT_MOVE) then
  859.       Effect := DROPEFFECT_COPY;
  860.     SetPerformedDropEffect(Effect);
  861.   end;
  862. end;
  863.  
  864. type
  865.   TDropTargetTransferThread = class(TThread)
  866.   private
  867.     FCustomDropTarget: TCustomDropTarget;
  868.     FDataObject: IDataObject;
  869.     FEffect: Longint;
  870.     FMarshalStream: pointer;
  871.   protected
  872.     procedure Execute; override;
  873.     property MarshalStream: pointer read FMarshalStream write FMarshalStream;
  874.   public
  875.     constructor Create(ACustomDropTarget: TCustomDropTarget;
  876.       const ADataObject: IDataObject; AEffect: Longint);
  877.     property CustomDropTarget: TCustomDropTarget read FCustomDropTarget;
  878.     property DataObject: IDataObject read FDataObject;
  879.     property Effect: Longint read FEffect;
  880.   end;
  881.  
  882. constructor TDropTargetTransferThread.Create(ACustomDropTarget: TCustomDropTarget;
  883.   const ADataObject: IDataObject; AEffect: longInt);
  884. begin
  885.   inherited Create(True);
  886.   FreeOnTerminate := True;
  887.   FCustomDropTarget := ACustomDropTarget;
  888.   OnTerminate := FCustomDropTarget.DoEndAsyncTransfer;
  889.   FEffect := AEffect;
  890.   OleCheck(CoMarshalInterThreadInterfaceInStream(IDataObject, ADataObject,
  891.     IStream(FMarshalStream)));
  892. end;
  893.  
  894. procedure TDropTargetTransferThread.Execute;
  895. var
  896.   Res: HResult;
  897. begin
  898.   CoInitialize(nil);
  899.   try
  900.     try
  901.       OleCheck(CoGetInterfaceAndReleaseStream(IStream(MarshalStream),
  902.         IDataObject, FDataObject));
  903.       MarshalStream := nil;
  904.       CustomDropTarget.FDataObject := DataObject;
  905.       CustomDropTarget.DoGetData;
  906.       Res := S_OK;
  907.     except
  908.       Res := E_UNEXPECTED;
  909.     end;
  910.     (FDataObject as IAsyncOperation).EndOperation(Res, nil, Effect);
  911.   finally
  912.     FDataObject := nil;
  913.     CoUninitialize;
  914.   end;
  915. end;
  916.  
  917. procedure TCustomDropTarget.DoEndAsyncTransfer(Sender: TObject);
  918. begin
  919.   // Reset async transfer flag once transfer completes and...
  920.   FIsAsync := False;
  921.  
  922.   // ...Fire event.
  923.   if Assigned(FOnEndAsyncTransfer) then
  924.     FOnEndAsyncTransfer(Self);
  925. end;
  926.  
  927. function TCustomDropTarget.GetData(Effect: longInt): boolean;
  928. var
  929.   DoAsync: LongBool;
  930.   AsyncOperation: IAsyncOperation;
  931. //  h: HResult;
  932. begin
  933.   ClearData;
  934.  
  935.   // Determine if drop source supports and has enabled asynchronous data
  936.   // transfer.
  937. (*
  938.   h := DataObject.QueryInterface(IAsyncOperation, AsyncOperation);
  939.   h := DataObject.QueryInterface(IDropSource, AsyncOperation);
  940.   OutputDebugString(PChar(SysErrorMessage(h)));
  941. *)
  942.   if not(AllowAsyncTransfer and
  943.     Succeeded(DataObject.QueryInterface(IAsyncOperation, AsyncOperation)) and
  944.     Succeeded(AsyncOperation.GetAsyncMode(DoAsync))) then
  945.     DoAsync := False;
  946.  
  947.   // Start an async data transfer...
  948.   if (DoAsync) then
  949.   begin
  950.     // Fire event.
  951.     if Assigned(FOnStartAsyncTransfer) then
  952.       FOnStartAsyncTransfer(Self);
  953.     FIsAsync := True;
  954.     // Notify drop source that an async data transfer is starting.
  955.     AsyncOperation.StartOperation(nil);
  956.     // Create the data transfer thread and launch it.
  957.     with TDropTargetTransferThread.Create(Self, DataObject, Effect) do
  958.       Resume;
  959.  
  960.     Result := True;
  961.   end else
  962.     Result := DoGetData;
  963. end;
  964.  
  965. procedure TCustomDropTarget.Notification(AComponent: TComponent;
  966.   Operation: TOperation);
  967. begin
  968.   inherited Notification(AComponent, Operation);
  969.   if (Operation = opRemove) and (AComponent is TWinControl) then
  970.   begin
  971.     if (csDesigning in ComponentState) and (AComponent = FTarget) then
  972.       FTarget := nil;
  973.     if (FTargets.IndexOf(TWinControl(AComponent)) <> -1) then
  974.       DoUnregister(TWinControl(AComponent));
  975.   end;
  976. end;
  977.  
  978. type
  979.   TWinControlProxy = class(TWinControl)
  980.   protected
  981.     procedure DestroyWnd; override;
  982.     procedure CreateWnd; override;
  983.   end;
  984.  
  985. procedure TWinControlProxy.CreateWnd;
  986. begin
  987.   inherited CreateWnd;
  988.   OleCheck(RegisterDragDrop(Parent.Handle, TCustomDropTarget(Owner)));
  989.   Visible := False;
  990. end;
  991.  
  992. procedure TWinControlProxy.DestroyWnd;
  993. begin
  994.   if (Parent.HandleAllocated) then
  995.     RevokeDragDrop(Parent.Handle);
  996.   // Control must be visible in order to guarantee that CreateWnd is called when
  997.   // parent control recreates window handle.
  998.   Visible := True;
  999.   inherited DestroyWnd;
  1000. end;
  1001.  
  1002. procedure TCustomDropTarget.Register(ATarget: TWinControl);
  1003.  
  1004.   function Contains(Parent, Child: TWinControl): boolean;
  1005.   var
  1006.     i: integer;
  1007.   begin
  1008.     if (Child.Parent <> Parent) then
  1009.     begin
  1010.       Result := False;
  1011.       for i := 0 to Parent.ControlCount-1 do
  1012.         if (Parent.Controls[i] is TWinControl) and
  1013.           Contains(TWinControl(Parent.Controls[i]), Child) then
  1014.         begin
  1015.           Result := True;
  1016.           break;
  1017.         end;
  1018.     end else
  1019.       Result := True;
  1020.   end;
  1021.  
  1022. var
  1023.   i: integer;
  1024.   Inserted: boolean;
  1025. begin
  1026.   // Don't register if the target is already registered.
  1027.   // TODO -cImprovement : Maybe we should unregister and reregister the target if it has already been registered (in case the handle has changed)...
  1028.   if (FTargets.IndexOf(ATarget) <> -1) then
  1029.     exit;
  1030.  
  1031.   // Unregister previous target unless MultiTarget is enabled (for backwards
  1032.   // compatibility).
  1033.   if (not FMultiTarget) and not(csLoading in ComponentState) then
  1034.     Unregister;
  1035.  
  1036.   if (ATarget = nil) then
  1037.     exit;
  1038.  
  1039.   // Insert the target in Z order, Topmost last.
  1040.   // Note: The target is added to the target list even though the drop target
  1041.   // registration may fail below. This is done because we would like
  1042.   // the target to be unregistered (RevokeDragDrop) even if we failed to
  1043.   // register it.
  1044.   Inserted := False;
  1045.   for i := FTargets.Count-1 downto 0 do
  1046.     if Contains(FTargets[i], ATarget) then
  1047.     begin
  1048.       FTargets.Insert(i+1, ATarget);
  1049.       Inserted := True;
  1050.       break;
  1051.     end;
  1052.   if (not Inserted) then
  1053.   begin
  1054.     FTargets.Add(ATarget);
  1055.     // ATarget.FreeNotification(Self);
  1056.   end;
  1057.  
  1058.  
  1059.   // If the target is a TRichEdit control, we disable the rich edit control's
  1060.   // built-in drag/drop support.
  1061.   if (ATarget is TCustomRichEdit) then
  1062.     RevokeDragDrop(ATarget.Handle);
  1063.  
  1064.   // Create a child control to monitor the target window handle.
  1065.   // The child control will perform the drop target registration for us.
  1066.   with TWinControlProxy.Create(Self) do
  1067.     Parent := ATarget;
  1068. end;
  1069.  
  1070. {$ifdef VER12_PLUS}
  1071. procedure TCustomDropTarget.Unregister(ATarget: TWinControl);
  1072. begin
  1073.   // Unregister a single targets (or all targets if ATarget is nil).
  1074.   DoUnregister(ATarget);
  1075. end;
  1076. {$else}
  1077. procedure TCustomDropTarget.Unregister;
  1078. begin
  1079.   // Unregister all targets (for backward compatibility).
  1080.   DoUnregister(nil);
  1081. end;
  1082. {$endif}
  1083.  
  1084. procedure TCustomDropTarget.DoUnregister(ATarget: TWinControl);
  1085. var
  1086.   i                     : integer;
  1087. begin
  1088.   if (ATarget = nil) then
  1089.   begin
  1090.     for i := FTargets.Count-1 downto 0 do
  1091.       DoUnregister(FTargets[i]);
  1092.     exit;
  1093.   end;
  1094.  
  1095.   i := FTargets.IndexOf(ATarget);
  1096.   if (i = -1) then
  1097.     exit;
  1098.  
  1099.   if (ATarget = FTarget) then
  1100.     FTarget := nil;
  1101.     // raise Exception.Create(sUnregisterActiveTarget);
  1102.  
  1103.   FTargets.Delete(i);
  1104.  
  1105. (* Handled by proxy
  1106.   if (ATarget.HandleAllocated) then
  1107.     // Ignore failed unregistrations - nothing to do about it anyway
  1108.     RevokeDragDrop(ATarget.Handle);
  1109. *)
  1110.  
  1111.   // Delete target proxy.
  1112.   // The target proxy willl unregister the drop target for us when it is
  1113.   // destroyed.
  1114.   for i := ATarget.ControlCount-1 downto 0 do
  1115.     if (ATarget.Controls[i] is TWinControlProxy) and
  1116.       (TWinControlProxy(ATarget.Controls[i]).Owner = Self) then
  1117.     with TWinControlProxy(ATarget.Controls[i]) do
  1118.     begin
  1119.       Parent := nil;
  1120.       Free;
  1121.       break;
  1122.     end;
  1123. end;
  1124.  
  1125. function TCustomDropTarget.FindTarget(p: TPoint): TWinControl;
  1126. (*
  1127. var
  1128.   i: integer;
  1129.   r: TRect;
  1130.   Parent: TWinControl;
  1131. *)
  1132. begin
  1133.  
  1134.   Result := FindVCLWindow(p);
  1135.   while (Result <> nil) and (Targets.IndexOf(Result) = -1) do
  1136.   begin
  1137.     Result := Result.Parent;
  1138.   end;
  1139. (*
  1140.   // Search list in Z order. Top to bottom.
  1141.   for i := Targets.Count-1 downto 0 do
  1142.   begin
  1143.     Result := Targets[i];
  1144.  
  1145.     // If the control or any of its parent aren't visible, we can't drop on it.
  1146.     Parent := Result;
  1147.     while (Parent <> nil) do
  1148.     begin
  1149.       if (not Parent.Showing) then
  1150.         break;
  1151.       Parent := Parent.Parent;
  1152.     end;
  1153.     if (Parent <> nil) then
  1154.       continue;
  1155.  
  1156.     GetWindowRect(Result.Handle, r);
  1157.     if PtInRect(r, p) then
  1158.       exit;
  1159.   end;
  1160.   Result := nil;
  1161. *)
  1162. end;
  1163.  
  1164. function TCustomDropTarget.FindNearestTarget(p: TPoint): TWinControl;
  1165. var
  1166.   i                     : integer;
  1167.   r                     : TRect;
  1168.   pc                    : TPoint;
  1169.   Control               : TWinControl;
  1170.   Dist                  ,
  1171.   BestDist              : integer;
  1172.  
  1173.   function Distance(r: TRect; p: TPoint): integer;
  1174.   var
  1175.     dx                  ,
  1176.     dy                  : integer;
  1177.   begin
  1178.     if (p.x < r.Left) then
  1179.       dx := r.Left - p.x
  1180.     else if (p.x > r.Right) then
  1181.       dx := r.Right - p.x
  1182.     else
  1183.       dx := 0;
  1184.     if (p.y < r.Top) then
  1185.       dy := r.Top - p.y
  1186.     else if (p.y > r.Bottom) then
  1187.       dy := r.Bottom - p.y
  1188.     else
  1189.       dy := 0;
  1190.     Result := dx*dx + dy*dy;
  1191.   end;
  1192.  
  1193. begin
  1194.   Result := nil;
  1195.   BestDist := high(integer);
  1196.   for i := 0 to Targets.Count-1 do
  1197.   begin
  1198.     Control := Targets[i];
  1199.     r := Control.ClientRect;
  1200.     inc(r.Right);
  1201.     inc(r.Bottom);
  1202.     pc := Control.ScreenToClient(p);
  1203.     if (PtInRect(r, p)) then
  1204.     begin
  1205.       Result := Control;
  1206.       exit;
  1207.     end;
  1208.     Dist := Distance(r, pc);
  1209.     if (Dist < BestDist) then
  1210.     begin
  1211.       Result := Control;
  1212.       BestDist := Dist;
  1213.     end;
  1214.   end;
  1215. end;
  1216.  
  1217. function TCustomDropTarget.GetTarget: TWinControl;
  1218. begin
  1219.   Result := FTarget;
  1220.   if (Result = nil) and not(csDesigning in ComponentState) then
  1221.   begin
  1222.     if (FTargets.Count > 0) then
  1223.       Result := TWinControl(FTargets[0])
  1224.     else
  1225.       Result := nil;
  1226.   end;
  1227. end;
  1228.  
  1229. procedure TCustomDropTarget.SetTarget(const Value: TWinControl);
  1230. begin
  1231.   if (FTarget = Value) then
  1232.     exit;
  1233.  
  1234.   if (csDesigning in ComponentState) then
  1235.     FTarget := Value
  1236.   else
  1237.   begin
  1238.     // If MultiTarget isn't enabled, Register will automatically unregister do
  1239.     // no need to do it here.
  1240.     if (FMultiTarget) and not(csLoading in ComponentState) then
  1241.       Unregister;
  1242.     Register(Value);
  1243.   end;
  1244. end;
  1245.  
  1246. procedure TCustomDropTarget.SetDataObject(Value: IDataObject);
  1247. begin
  1248.   FDataObject := Value;
  1249. end;
  1250.  
  1251. procedure TCustomDropTarget.SetShowImage(Show: boolean);
  1252. begin
  1253.   FShowImage := Show;
  1254.   if (DropTargetHelper <> nil) then
  1255.     DropTargetHelper.Show(Show)
  1256.   else
  1257.     if (FDataObject <> nil) then
  1258.       ImageList_DragShowNolock(FShowImage);
  1259. end;
  1260.  
  1261. function TCustomDropTarget.GetValidDropEffect(ShiftState: TShiftState;
  1262.   pt: TPoint; dwEffect: LongInt): LongInt;
  1263. begin
  1264.   // dwEffect 'in' parameter = set of drop effects allowed by drop source.
  1265.   // Now filter out the effects disallowed by target...
  1266.   Result := dwEffect AND DragTypesToDropEffect(FDragTypes);
  1267.  
  1268.   Result := ShiftStateToDropEffect(ShiftState, Result, True);
  1269.  
  1270.   // Add Scroll effect if necessary...
  1271.   if (FAutoScroll) and (FScrollBars <> []) then
  1272.   begin
  1273.     // If the cursor is inside the no-scroll zone, clear the drag scroll flag,
  1274.     // otherwise set it.
  1275.     if (PtInRect(FNoScrollZone, pt)) then
  1276.       Result := Result AND NOT integer(DROPEFFECT_SCROLL)
  1277.     else
  1278.       Result := Result OR integer(DROPEFFECT_SCROLL);
  1279.   end;
  1280.  
  1281.   // 'Default' behaviour can be overriden by assigning OnGetDropEffect.
  1282.   if Assigned(FOnGetDropEffect) then
  1283.     FOnGetDropEffect(Self, ShiftState, pt, Result);
  1284. end;
  1285.  
  1286. function TCustomDropTarget.GetPreferredDropEffect: LongInt;
  1287. begin
  1288.   with TPreferredDropEffectClipboardFormat.Create do
  1289.     try
  1290.       if GetData(DataObject) then
  1291.         Result := Value
  1292.       else
  1293.         Result := DROPEFFECT_NONE;
  1294.     finally
  1295.       Free;
  1296.     end;
  1297. end;
  1298.  
  1299. function TCustomDropTarget.SetPasteSucceded(Effect: LongInt): boolean;
  1300. var
  1301.   Medium: TStgMedium;
  1302. begin
  1303.   with TPasteSuccededClipboardFormat.Create do
  1304.     try
  1305.       Value := Effect;
  1306.       Result := SetData(DataObject, FormatEtc, Medium);
  1307.     finally
  1308.       Free;
  1309.     end;
  1310. end;
  1311.  
  1312. function TCustomDropTarget.SetPerformedDropEffect(Effect: longInt): boolean;
  1313. var
  1314.   Medium: TStgMedium;
  1315. begin
  1316.   with TPerformedDropEffectClipboardFormat.Create do
  1317.     try
  1318.       Value := Effect;
  1319.       Result := SetData(DataObject, FormatEtc, Medium);
  1320.     finally
  1321.       Free;
  1322.     end;
  1323. end;
  1324.  
  1325. (*
  1326. The basic procedure for a delete-on-paste operation is as follows (from MSDN):
  1327.  
  1328. 1) The source marks the screen display of the selected data.
  1329. 2) The source creates a data object. It indicates a cut operation by adding the
  1330.    CFSTR_PREFERREDDROPEFFECT format with a data value of DROPEFFECT_MOVE.
  1331. 3) The source places the data object on the Clipboard using OleSetClipboard.
  1332. 4) The target retrieves the data object from the Clipboard using
  1333.    OleGetClipboard.
  1334. 5) The target extracts the CFSTR_PREFERREDDROPEFFECT data. If it is set to only
  1335.    DROPEFFECT_MOVE, the target can either do an optimized move or simply copy
  1336.    the data.
  1337. 6) If the target does not do an optimized move, it calls the
  1338.    IDataObject::SetData method with the CFSTR_PERFORMEDDROPEFFECT format set
  1339.    to DROPEFFECT_MOVE.
  1340. 7) When the paste is complete, the target calls the IDataObject::SetData method
  1341.    with the CFSTR_PASTESUCCEEDED format set to DROPEFFECT_MOVE.
  1342. 8) When the source's IDataObject::SetData method is called with the
  1343.   CFSTR_PASTESUCCEEDED format set to DROPEFFECT_MOVE, it must check to see if it
  1344.   also received the CFSTR_PERFORMEDDROPEFFECT format set to DROPEFFECT_MOVE. If
  1345.   both formats are sent by the target, the source will have to delete the data.
  1346.   If only the CFSTR_PASTESUCCEEDED format is received, the source can simply
  1347.   remove the data from its display. If the transfer fails, the source updates
  1348.   the display to its original appearance.
  1349. *)
  1350. function TCustomDropTarget.PasteFromClipboard: longint;
  1351. var
  1352.   Effect: longInt;
  1353. begin
  1354.   // Get an IDataObject interface to the clipboard.
  1355.   // Temporarily pretend that the IDataObject has been dropped on the target.
  1356.   OleCheck(OleGetClipboard(FDataObject));
  1357.   try
  1358.     Effect := GetPreferredDropEffect;
  1359.     // Get data from the IDataObject.
  1360.     if (GetData(Effect)) then
  1361.       Result := Effect
  1362.     else
  1363.       Result := DROPEFFECT_NONE;
  1364.  
  1365.     DoOnPaste(Result);
  1366.   finally
  1367.     // Clean up
  1368.     FDataObject := nil;
  1369.   end;
  1370. end;
  1371.  
  1372. procedure TCustomDropTarget.DoOnPaste(var Effect: longint);
  1373. begin
  1374.   // Generate an OnDrop event
  1375.   DoDrop([], Point(0,0), Effect);
  1376.  
  1377.   // Report performed drop effect back to data originator.
  1378.   if (Effect <> DROPEFFECT_NONE) then
  1379.     // Delete on paste:
  1380.     // We now set the CF_PASTESUCCEDED format to indicate to the source
  1381.     // that we are using the "delete on paste" protocol and that the
  1382.     // paste has completed.
  1383.     SetPasteSucceded(Effect);
  1384. end;
  1385.  
  1386. procedure TCustomDropTarget.Assign(Source: TPersistent);
  1387. begin
  1388.   if (Source is TClipboard) then
  1389.     PasteFromClipboard
  1390.   else if (Source.GetInterface(IDataObject, FDataObject)) then
  1391.   begin
  1392.     try
  1393.       // Get data from the IDataObject
  1394.       if (not GetData(DROPEFFECT_COPY)) then
  1395.         inherited Assign(Source);
  1396.     finally
  1397.       // Clean up
  1398.       FDataObject := nil;
  1399.     end;
  1400.   end else
  1401.     inherited Assign(Source);
  1402. end;
  1403.  
  1404. procedure TCustomDropTarget.DoAutoScroll(Sender: TObject);
  1405. var
  1406.   Scroll: TScrolDirections;
  1407.   Interval: integer;
  1408. begin
  1409.   // Disable timer until we are ready to auto-repeat the scroll.
  1410.   // If no scroll is performed, the scroll stops here.
  1411.   FScrollTimer.Enabled := False;;
  1412.  
  1413.   Interval := DragDropScrollInterval;
  1414.   Scroll := [];
  1415.  
  1416.   // Only scroll if the pointer is outside the non-scroll area
  1417.   if (not PtInRect(FNoScrollZone, FLastPoint)) then
  1418.   begin
  1419.     with FLastPoint do
  1420.     begin
  1421.       // Determine which way to scroll.
  1422.       if (Y < FNoScrollZone.Top) then
  1423.         include(Scroll, sdUp)
  1424.       else if (Y > FNoScrollZone.Bottom) then
  1425.         include(Scroll, sdDown);
  1426.  
  1427.       if (X < FNoScrollZone.Left) then
  1428.         include(Scroll, sdLeft)
  1429.       else if (X > FNoScrollZone.Right) then
  1430.         include(Scroll, sdRight);
  1431.     end;
  1432.   end;
  1433.  
  1434.   DoScroll(FLastPoint, Scroll, Interval);
  1435.  
  1436.   // Note: Once the OnScroll event has been fired and the user has had a
  1437.   // chance of overriding the auto scroll logic, we should *only* use to Scroll
  1438.   // variable to determine if and how to scroll. Do not use FScrollBars past
  1439.   // this point.
  1440.  
  1441.   // Only scroll if the pointer is outside the non-scroll area
  1442.   if (Scroll <> []) then
  1443.   begin
  1444.     // Remove drag image before scrolling
  1445.     if (FDragImageHandle <> 0) then
  1446.       ImageList_DragLeave(FTarget.Handle);
  1447.     try
  1448.       if (sdUp in Scroll) then
  1449.         FTarget.Perform(WM_VSCROLL,SB_LINEUP, 0)
  1450.       else if (sdDown in Scroll) then
  1451.         FTarget.Perform(WM_VSCROLL,SB_LINEDOWN, 0);
  1452.  
  1453.       if (sdLeft in Scroll) then
  1454.         FTarget.Perform(WM_HSCROLL,SB_LINEUP, 0)
  1455.       else if (sdRight in Scroll) then
  1456.         FTarget.Perform(WM_HSCROLL,SB_LINEDOWN, 0);
  1457.     finally
  1458.       // Restore drag image
  1459.       if (FDragImageHandle <> 0) then
  1460.         with ClientPtToWindowPt(FTarget.Handle, FLastPoint) do
  1461.           ImageList_DragEnter(FTarget.Handle, x, y);
  1462.     end;
  1463.  
  1464.     // Reset scroll timer interval once timer has fired once.
  1465.     FScrollTimer.Interval := Interval;
  1466.     FScrollTimer.Enabled := True;
  1467.   end;
  1468. end;
  1469.  
  1470. procedure TCustomDropTarget.DoScroll(Point: TPoint;
  1471.   var Scroll: TScrolDirections; var Interval: integer);
  1472. begin
  1473.   if Assigned(FOnScroll) then
  1474.     FOnScroll(Self, FLastPoint, Scroll, Interval);
  1475. end;
  1476.  
  1477.  
  1478. ////////////////////////////////////////////////////////////////////////////////
  1479. //
  1480. //              TDropDummy
  1481. //
  1482. ////////////////////////////////////////////////////////////////////////////////
  1483. function TDropDummy.HasValidFormats(ADataObject: IDataObject): boolean;
  1484. begin
  1485.   Result := False;
  1486. end;
  1487.  
  1488. procedure TDropDummy.ClearData;
  1489. begin
  1490.   // Abstract method override - doesn't do anything as you can see.
  1491. end;
  1492.  
  1493. function TDropDummy.DoGetData: boolean;
  1494. begin
  1495.   Result := False;
  1496. end;
  1497.  
  1498.  
  1499. ////////////////////////////////////////////////////////////////////////////////
  1500. //
  1501. //              TCustomDropMultiTarget
  1502. //
  1503. ////////////////////////////////////////////////////////////////////////////////
  1504. constructor TCustomDropMultiTarget.Create(AOwner: TComponent);
  1505. begin
  1506.   inherited Create(AOwner);
  1507.   DragTypes := [dtLink, dtCopy];
  1508.   GetDataOnEnter := False;
  1509.   FDataFormats := TDataFormats.Create;
  1510. end;
  1511.  
  1512. destructor TCustomDropMultiTarget.Destroy;
  1513. var
  1514.   i                     : integer;
  1515. begin
  1516.   // Delete all target formats owned by the object.
  1517.   for i := FDataFormats.Count-1 downto 0 do
  1518.     FDataFormats[i].Free;
  1519.   FDataFormats.Free;
  1520.   inherited Destroy;
  1521. end;
  1522.  
  1523. function TCustomDropMultiTarget.HasValidFormats(ADataObject: IDataObject): boolean;
  1524. var
  1525.   GetNum                ,
  1526.   GotNum                : longInt;
  1527.   FormatEnumerator      : IEnumFormatEtc;
  1528.   i                     : integer;
  1529.   SourceFormatEtc       : TFormatEtc;
  1530. begin
  1531.   Result := False;
  1532.  
  1533.   if (ADataObject.EnumFormatEtc(DATADIR_GET, FormatEnumerator) <> S_OK) or
  1534.     (FormatEnumerator.Reset <> S_OK) then
  1535.     exit;
  1536.  
  1537.   GetNum := 1; // Get one format at a time.
  1538.  
  1539.   // Enumerate all data formats offered by the drop source.
  1540.   // Note: Depends on order of evaluation.
  1541.   while (not Result) and
  1542.     (FormatEnumerator.Next(GetNum, SourceFormatEtc, @GotNum) = S_OK) and
  1543.     (GetNum = GotNum) do
  1544.   begin
  1545.     // Determine if any of the associated clipboard formats can
  1546.     // read the current data format.
  1547.     for i := 0 to FDataFormats.Count-1 do
  1548.       if (FDataFormats[i].AcceptFormat(SourceFormatEtc)) and
  1549.         (FDataFormats[i].HasValidFormats(ADataObject)) then
  1550.       begin
  1551.         Result := True;
  1552.         DoAcceptFormat(FDataFormats[i], Result);
  1553.         if (Result) then
  1554.           break;
  1555.       end;
  1556.   end;
  1557. end;
  1558.  
  1559. procedure TCustomDropMultiTarget.ClearData;
  1560. var
  1561.   i                     : integer;
  1562. begin
  1563.   if (AsyncTransfer) then
  1564.     raise Exception.Create(sAsyncBusy);
  1565.   for i := 0 to DataFormats.Count-1 do
  1566.     DataFormats[i].Clear;
  1567. end;
  1568.  
  1569. function TCustomDropMultiTarget.DoGetData: boolean;
  1570. var
  1571.   i: integer;
  1572.   Accept: boolean;
  1573. begin
  1574.   Result := False;
  1575.  
  1576.   // Get data for all target formats
  1577.   for i := 0 to DataFormats.Count-1 do
  1578.   begin
  1579.     // This isn't strictly nescessary and adds overhead, but it reduces
  1580.     // unnescessary calls to DoAcceptData (format is asked if it can accept data
  1581.     // even though no data is available to the format).
  1582.     if not(FDataFormats[i].HasValidFormats(DataObject)) then
  1583.       continue;
  1584.  
  1585.     // Only get data from accepted formats.
  1586.     // TDropComboTarget uses the DoAcceptFormat method to filter formats and to
  1587.     // allow the user to disable formats via an event.
  1588.     Accept := True;
  1589.     DoAcceptFormat(DataFormats[i], Accept);
  1590.     if (not Accept) then
  1591.       Continue;
  1592.  
  1593.     Result := DataFormats[i].GetData(DataObject) or Result;
  1594.   end;
  1595. end;
  1596.  
  1597. procedure TCustomDropMultiTarget.DoAcceptFormat(const DataFormat: TCustomDataFormat;
  1598.   var Accept: boolean);
  1599. begin
  1600.   if Assigned(FOnAcceptFormat) then
  1601.     FOnAcceptFormat(Self, DataFormat, Accept);
  1602. end;
  1603.  
  1604. end.
  1605.  
  1606.