DropTarget.pas 51 KB

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