Subversion Repositories decoder

Rev

Blame | Last modification | View Log | RSS feed

  1. unit DropSource;
  2.  
  3. // -----------------------------------------------------------------------------
  4. // Project:         Drag and Drop Component Suite
  5. // Module:          DropSource
  6. // Description:     Implements Dragging & Dropping of data
  7. //                  FROM your application to another.
  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. //
  17. // TDropSource changes:
  18. // - CutToClipboard and CopyToClipboard now uses OleSetClipboard.
  19. //   This means that descendant classes no longer needs to override the
  20. //   CutOrCopyToClipboard method.
  21. // - New OnGetData event.
  22. // - Changed to use new V4 architecture:
  23. //   * All clipboard format support has been removed from TDropSource, it has
  24. //     been renamed to TCustomDropSource and the old TDropSource has been
  25. //     modified to descend from TCustomDropSource and has moved to the
  26. //     DropSource3 unit. TDropSource is now supported for backwards
  27. //     compatibility only and will be removed in a future version.
  28. //   * A new TCustomDropMultiSource, derived from TCustomDropSource, uses the
  29. //     new architecture (with TClipboardFormat and TDataFormat) and is the new
  30. //     base class for all the drop source components.
  31. // - TInterfacedComponent moved to DragDrop unit.
  32. // -----------------------------------------------------------------------------
  33. // TODO -oanme -cCheckItOut : OleQueryLinkFromData
  34. // TODO -oanme -cDocumentation : CutToClipboard and CopyToClipboard alters the value of PreferredDropEffect.
  35. // TODO -oanme -cDocumentation : Clipboard must be flushed or emptied manually after CutToClipboard and CopyToClipboard. Automatic flush is not guaranteed.
  36. // TODO -oanme -cDocumentation : Delete-on-paste. Why and How.
  37. // TODO -oanme -cDocumentation : Optimized move. Why and How.
  38. // TODO -oanme -cDocumentation : OnPaste event is only fired if target sets the "Paste Succeeded" clipboard format. Explorer does this for delete-on-paste move operations.
  39. // TODO -oanme -cDocumentation : DragDetectPlus. Why and How.
  40. // -----------------------------------------------------------------------------
  41.  
  42. interface
  43.  
  44. uses
  45.   DragDrop,
  46.   DragDropFormats,
  47.   ActiveX,
  48.   Controls,
  49.   Windows,
  50.   Classes;
  51.  
  52. {$include DragDrop.inc}
  53.  
  54. type
  55.   TDragResult = (drDropCopy, drDropMove, drDropLink, drCancel,
  56.     drOutMemory, drAsync, drUnknown);
  57.  
  58.   TDropEvent = procedure(Sender: TObject; DragType: TDragType;
  59.     var ContinueDrop: Boolean) of object;
  60.  
  61.   //: TAfterDropEvent is fired after the target has finished processing a
  62.   // successfull drop.
  63.   // The Optimized parameter is True if the target either performed an operation
  64.   // other than a move or performed an "optimized move". In either cases, the
  65.   // source isn't required to delete the source data.
  66.   // If the Optimized parameter is False, the target performed an "unoptimized
  67.   // move" operation and the source is required to delete the source data to
  68.   // complete the move operation.
  69.   TAfterDropEvent = procedure(Sender: TObject; DragResult: TDragResult;
  70.     Optimized: Boolean) of object;
  71.  
  72.   TFeedbackEvent = procedure(Sender: TObject; Effect: LongInt;
  73.     var UseDefaultCursors: Boolean) of object;
  74.  
  75.   //: The TDropDataEvent event is fired when the target requests data from the
  76.   // drop source or offers data to the drop source.
  77.   // The Handled flag should be set if the event handler satisfied the request.
  78.   TDropDataEvent = procedure(Sender: TObject; const FormatEtc: TFormatEtc;
  79.     out Medium: TStgMedium; var Handled: Boolean) of object;
  80.  
  81.   //: TPasteEvent is fired when the target sends a "Paste Succeeded" value
  82.   // back to the drop source after a clipboard transfer.
  83.   // The DeleteOnPaste parameter is True if the source is required to delete
  84.   // the source data. This will only occur after a CutToClipboard operation
  85.   // (corresponds to a move drag/drop).
  86.   TPasteEvent = procedure(Sender: TObject; Action: TDragResult;
  87.     DeleteOnPaste: boolean) of object;
  88.  
  89.  
  90. ////////////////////////////////////////////////////////////////////////////////
  91. //
  92. //              TCustomDropSource
  93. //
  94. ////////////////////////////////////////////////////////////////////////////////
  95. // Abstract base class for all Drop Source components.
  96. // Implements the IDropSource and IDataObject interfaces.
  97. ////////////////////////////////////////////////////////////////////////////////
  98.   TCustomDropSource = class(TDragDropComponent, IDropSource, IDataObject,
  99.     IAsyncOperation)
  100.   private
  101.     FDragTypes: TDragTypes;
  102.     FFeedbackEffect: LongInt;
  103.     // Events...
  104.     FOnDrop: TDropEvent;
  105.     FOnAfterDrop: TAfterDropEvent;
  106.     FOnFeedback: TFeedBackEvent;
  107.     FOnGetData: TDropDataEvent;
  108.     FOnSetData: TDropDataEvent;
  109.     FOnPaste: TPasteEvent;
  110.     // Drag images...
  111.     FImages: TImageList;
  112.     FShowImage: boolean;
  113.     FImageIndex: integer;
  114.     FImageHotSpot: TPoint;
  115.     FDragSourceHelper: IDragSourceHelper;
  116.     // Async transfer...
  117.     FAllowAsync: boolean;
  118.     FRequestAsync: boolean;
  119.     FIsAsync: boolean;
  120.  
  121.   protected
  122.     property FeedbackEffect: LongInt read FFeedbackEffect write FFeedbackEffect;
  123.  
  124.     // IDropSource implementation
  125.     function QueryContinueDrag(fEscapePressed: bool;
  126.       grfKeyState: LongInt): HRESULT; stdcall;
  127.     function GiveFeedback(dwEffect: LongInt): HRESULT; stdcall;
  128.  
  129.     // IDataObject implementation
  130.     function GetData(const FormatEtcIn: TFormatEtc;
  131.       out Medium: TStgMedium):HRESULT; stdcall;
  132.     function GetDataHere(const FormatEtc: TFormatEtc;
  133.       out Medium: TStgMedium):HRESULT; stdcall;
  134.     function QueryGetData(const FormatEtc: TFormatEtc): HRESULT; stdcall;
  135.     function GetCanonicalFormatEtc(const FormatEtc: TFormatEtc;
  136.       out FormatEtcout: TFormatEtc): HRESULT; stdcall;
  137.     function SetData(const FormatEtc: TFormatEtc; var Medium: TStgMedium;
  138.       fRelease: Bool): HRESULT; stdcall;
  139.     function EnumFormatEtc(dwDirection: LongInt;
  140.       out EnumFormatEtc: IEnumFormatEtc): HRESULT; stdcall;
  141.     function dAdvise(const FormatEtc: TFormatEtc; advf: LongInt;
  142.       const advsink: IAdviseSink; out dwConnection: LongInt): HRESULT; stdcall;
  143.     function dUnadvise(dwConnection: LongInt): HRESULT; stdcall;
  144.     function EnumdAdvise(out EnumAdvise: IEnumStatData): HRESULT; stdcall;
  145.  
  146.     // IAsyncOperation implementation
  147.     function EndOperation(hResult: HRESULT; const pbcReserved: IBindCtx;
  148.       dwEffects: Cardinal): HRESULT; stdcall;
  149.     function GetAsyncMode(out fDoOpAsync: LongBool): HRESULT; stdcall;
  150.     function InOperation(out pfInAsyncOp: LongBool): HRESULT; stdcall;
  151.     function SetAsyncMode(fDoOpAsync: LongBool): HRESULT; stdcall;
  152.     function StartOperation(const pbcReserved: IBindCtx): HRESULT; stdcall;
  153.  
  154.     // Abstract methods
  155.     function DoGetData(const FormatEtcIn: TFormatEtc;
  156.       out Medium: TStgMedium): HRESULT; virtual; abstract;
  157.     function DoSetData(const FormatEtc: TFormatEtc;
  158.       var Medium: TStgMedium): HRESULT; virtual;
  159.     function HasFormat(const FormatEtc: TFormatEtc): boolean; virtual; abstract;
  160.     function GetEnumFormatEtc(dwDirection: LongInt): IEnumFormatEtc; virtual; abstract;
  161.  
  162.     // Data format event sink
  163.     procedure DataChanging(Sender: TObject); virtual;
  164.  
  165.     // Clipboard
  166.     function CutOrCopyToClipboard: boolean; virtual;
  167.     procedure DoOnPaste(Action: TDragResult; DeleteOnPaste: boolean); virtual;
  168.  
  169.     // Property access
  170.     procedure SetShowImage(Value: boolean);
  171.     procedure SetImages(const Value: TImageList);
  172.     procedure SetImageIndex(const Value: integer);
  173.     procedure SetPoint(Index: integer; Value: integer);
  174.     function GetPoint(Index: integer): integer;
  175.     function GetPerformedDropEffect: longInt; virtual;
  176.     function GetLogicalPerformedDropEffect: longInt; virtual;
  177.     procedure SetPerformedDropEffect(const Value: longInt); virtual;
  178.     function GetPreferredDropEffect: longInt; virtual;
  179.     procedure SetPreferredDropEffect(const Value: longInt); virtual;
  180.     function GetInShellDragLoop: boolean; virtual;
  181.     function GetTargetCLSID: TCLSID; virtual;
  182.     procedure SetInShellDragLoop(const Value: boolean); virtual;
  183.     function GetLiveDataOnClipboard: boolean;
  184.     procedure SetAllowAsync(const Value: boolean);
  185.  
  186.     // Component management
  187.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  188.  
  189.     property DragSourceHelper: IDragSourceHelper read FDragSourceHelper;
  190.   public
  191.     constructor Create(AOwner: TComponent); override;
  192.     destructor Destroy; override;
  193.     function Execute: TDragResult; virtual;
  194.     function CutToClipboard: boolean; virtual;
  195.     function CopyToClipboard: boolean; virtual;
  196.     procedure FlushClipboard; virtual;
  197.     procedure EmptyClipboard; virtual;
  198.  
  199.     property PreferredDropEffect: longInt read GetPreferredDropEffect
  200.       write SetPreferredDropEffect;
  201.     property PerformedDropEffect: longInt read GetPerformedDropEffect
  202.       write SetPerformedDropEffect;
  203.     property LogicalPerformedDropEffect: longInt read GetLogicalPerformedDropEffect;
  204.     property InShellDragLoop: boolean read GetInShellDragLoop
  205.       write SetInShellDragLoop;
  206.     property TargetCLSID: TCLSID read GetTargetCLSID;
  207.     property LiveDataOnClipboard: boolean read GetLiveDataOnClipboard;
  208.     property AsyncTransfer: boolean read FIsAsync;
  209.  
  210.   published
  211.     property DragTypes: TDragTypes read FDragTypes write FDragTypes;
  212.     // Events
  213.     property OnFeedback: TFeedbackEvent read FOnFeedback write FOnFeedback;
  214.     property OnDrop: TDropEvent read FOnDrop write FOnDrop;
  215.     property OnAfterDrop: TAfterDropEvent read FOnAfterDrop write FOnAfterDrop;
  216.     property OnGetData: TDropDataEvent read FOnGetData write FOnGetData;
  217.     property OnSetData: TDropDataEvent read FOnSetData write FOnSetData;
  218.     property OnPaste: TPasteEvent read FOnPaste write FOnPaste;
  219.  
  220.     // Drag Images...
  221.     property Images: TImageList read FImages write SetImages;
  222.     property ImageIndex: integer read FImageIndex write SetImageIndex;
  223.     property ShowImage: boolean read FShowImage write SetShowImage;
  224.     property ImageHotSpotX: integer index 1 read GetPoint write SetPoint;
  225.     property ImageHotSpotY: integer index 2 read GetPoint write SetPoint;
  226.     // Async transfer...
  227.     property AllowAsyncTransfer: boolean read FAllowAsync write SetAllowAsync;
  228.   end;
  229.  
  230.  
  231. ////////////////////////////////////////////////////////////////////////////////
  232. //
  233. //              TCustomDropMultiSource
  234. //
  235. ////////////////////////////////////////////////////////////////////////////////
  236. // Drop target base class which can accept multiple formats.
  237. ////////////////////////////////////////////////////////////////////////////////
  238.   TCustomDropMultiSource = class(TCustomDropSource)
  239.   private
  240.     FFeedbackDataFormat: TFeedbackDataFormat;
  241.     FRawDataFormat: TRawDataFormat;
  242.  
  243.   protected
  244.     function DoGetData(const FormatEtcIn: TFormatEtc;
  245.       out Medium: TStgMedium):HRESULT; override;
  246.     function DoSetData(const FormatEtc: TFormatEtc;
  247.       var Medium: TStgMedium): HRESULT; override;
  248.     function HasFormat(const FormatEtc: TFormatEtc): boolean; override;
  249.     function GetEnumFormatEtc(dwDirection: LongInt): IEnumFormatEtc; override;
  250.  
  251.     function GetPerformedDropEffect: longInt; override;
  252.     function GetLogicalPerformedDropEffect: longInt; override;
  253.     function GetPreferredDropEffect: longInt; override;
  254.     procedure SetPerformedDropEffect(const Value: longInt); override;
  255.     procedure SetPreferredDropEffect(const Value: longInt); override;
  256.     function GetInShellDragLoop: boolean; override;
  257.     procedure SetInShellDragLoop(const Value: boolean); override;
  258.     function GetTargetCLSID: TCLSID; override;
  259.  
  260.     procedure DoOnSetData(DataFormat: TCustomDataFormat;
  261.       ClipboardFormat: TClipboardFormat);
  262.  
  263.   public
  264.     constructor Create(AOwner: TComponent); override;
  265.     destructor Destroy; override;
  266.     property DataFormats;
  267.     // TODO : Add support for delayed rendering with OnRenderData event.
  268.   published
  269.   end;
  270.  
  271. ////////////////////////////////////////////////////////////////////////////////
  272. //
  273. //              TDropEmptySource
  274. //
  275. ////////////////////////////////////////////////////////////////////////////////
  276. // Do-nothing source for use with TDataFormatAdapter and such
  277. ////////////////////////////////////////////////////////////////////////////////
  278.   TDropEmptySource = class(TCustomDropMultiSource);
  279.  
  280.  
  281. ////////////////////////////////////////////////////////////////////////////////
  282. //
  283. //              TDropSourceThread
  284. //
  285. ////////////////////////////////////////////////////////////////////////////////
  286. // Executes a drop source operation from a thread.
  287. // TDropSourceThread is an alternative to the Windows 2000 Asynchronous Data
  288. // Transfer support.
  289. ////////////////////////////////////////////////////////////////////////////////
  290. type
  291.   TDropSourceThread = class(TThread)
  292.   private
  293.     FDropSource: TCustomDropSource;
  294.     FDragResult: TDragResult;
  295.   protected
  296.     procedure Execute; override;
  297.   public
  298.     constructor Create(ADropSource: TCustomDropSource; AFreeOnTerminate: Boolean);
  299.     property DragResult: TDragResult read FDragResult;
  300.     property Terminated;
  301.   end;
  302.  
  303. ////////////////////////////////////////////////////////////////////////////////
  304. //
  305. //              Utility functions
  306. //
  307. ////////////////////////////////////////////////////////////////////////////////
  308.   function DropEffectToDragResult(DropEffect: longInt): TDragResult;
  309.  
  310.  
  311. ////////////////////////////////////////////////////////////////////////////////
  312. //
  313. //              Component registration
  314. //
  315. ////////////////////////////////////////////////////////////////////////////////
  316. procedure Register;
  317.  
  318.  
  319. (*******************************************************************************
  320. **
  321. **                      IMPLEMENTATION
  322. **
  323. *******************************************************************************)
  324. implementation
  325.  
  326. uses
  327.   CommCtrl,
  328.   ComObj,
  329.   Graphics;
  330.  
  331.  
  332. ////////////////////////////////////////////////////////////////////////////////
  333. //
  334. //              Component registration
  335. //
  336. ////////////////////////////////////////////////////////////////////////////////
  337. procedure Register;
  338. begin
  339.   RegisterComponents(DragDropComponentPalettePage, [TDropEmptySource]);
  340. end;
  341.  
  342.  
  343. ////////////////////////////////////////////////////////////////////////////////
  344. //
  345. //              Utility functions
  346. //
  347. ////////////////////////////////////////////////////////////////////////////////
  348. function DropEffectToDragResult(DropEffect: longInt): TDragResult;
  349. begin
  350.   case DropEffect of
  351.     DROPEFFECT_NONE:
  352.       Result := drCancel;
  353.     DROPEFFECT_COPY:
  354.       Result := drDropCopy;
  355.     DROPEFFECT_MOVE:
  356.       Result := drDropMove;
  357.     DROPEFFECT_LINK:
  358.       Result := drDropLink;
  359.   else
  360.     Result := drUnknown; // This is probably an error condition
  361.   end;
  362. end;
  363.  
  364. // -----------------------------------------------------------------------------
  365. //                      TCustomDropSource
  366. // -----------------------------------------------------------------------------
  367.  
  368. constructor TCustomDropSource.Create(AOwner: TComponent);
  369. begin
  370.   inherited Create(AOwner);
  371.   DragTypes := [dtCopy]; //default to Copy.
  372.  
  373.   // Note: Normally we would call _AddRef or coLockObjectExternal(Self) here to
  374.   // make sure that the component wasn't deleted prematurely (e.g. after a call
  375.   // to RegisterDragDrop), but since our ancestor class TInterfacedComponent
  376.   // disables reference counting, we do not need to do so.
  377.  
  378.   FImageHotSpot := Point(16,16);
  379.   FImages := nil;
  380. end;
  381.  
  382. destructor TCustomDropSource.Destroy;
  383. begin
  384.   // TODO -oanme -cImprovement : Maybe FlushClipboard would be more appropiate?
  385.   EmptyClipboard;
  386.   inherited Destroy;
  387. end;
  388.  
  389. // -----------------------------------------------------------------------------
  390.  
  391. function TCustomDropSource.GetCanonicalFormatEtc(const FormatEtc: TFormatEtc;
  392.   out FormatEtcout: TFormatEtc): HRESULT;
  393. begin
  394.   Result := DATA_S_SAMEFORMATETC;
  395. end;
  396. // -----------------------------------------------------------------------------
  397.  
  398. function TCustomDropSource.SetData(const FormatEtc: TFormatEtc;
  399.   var Medium: TStgMedium; fRelease: Bool): HRESULT;
  400. begin
  401.   // Warning: Ordinarily it would be much more efficient to just call
  402.   // HasFormat(FormatEtc) to determine if we support the given format, but
  403.   // because we have to able to accept *all* data formats, even unknown ones, in
  404.   // order to support the Windows 2000 drag helper functionality, we can't
  405.   // reject any formats here. Instead we pass the request on to DoSetData and
  406.   // let it worry about the details.
  407.  
  408.   // if (HasFormat(FormatEtc)) then
  409.   // begin
  410.     try
  411.       Result := DoSetData(FormatEtc, Medium);
  412.     finally
  413.       if (fRelease) then
  414.         ReleaseStgMedium(Medium);
  415.     end;
  416.   // end else
  417.   //   Result:= DV_E_FORMATETC;
  418. end;
  419. // -----------------------------------------------------------------------------
  420.  
  421. function TCustomDropSource.DAdvise(const FormatEtc: TFormatEtc; advf: LongInt;
  422.   const advSink: IAdviseSink; out dwConnection: LongInt): HRESULT;
  423. begin
  424.   Result := OLE_E_ADVISENOTSUPPORTED;
  425. end;
  426. // -----------------------------------------------------------------------------
  427.  
  428. function TCustomDropSource.DUnadvise(dwConnection: LongInt): HRESULT;
  429. begin
  430.   Result := OLE_E_ADVISENOTSUPPORTED;
  431. end;
  432. // -----------------------------------------------------------------------------
  433.  
  434. function TCustomDropSource.EnumDAdvise(out EnumAdvise: IEnumStatData): HRESULT;
  435. begin
  436.   Result := OLE_E_ADVISENOTSUPPORTED;
  437. end;
  438. // -----------------------------------------------------------------------------
  439.  
  440. function TCustomDropSource.GetData(const FormatEtcIn: TFormatEtc;
  441.   out Medium: TStgMedium):HRESULT; stdcall;
  442. var
  443.   Handled: boolean;
  444. begin
  445.   Handled := False;
  446.   if (Assigned(FOnGetData)) then
  447.     // Fire event to ask user for data.
  448.     FOnGetData(Self, FormatEtcIn, Medium, Handled);
  449.  
  450.   // If user provided data, there is no need to call descendant for it.
  451.   if (Handled) then
  452.     Result := S_OK
  453.   else if (HasFormat(FormatEtcIn)) then
  454.     // Call descendant class to get data.
  455.     Result := DoGetData(FormatEtcIn, Medium)
  456.   else
  457.     Result:= DV_E_FORMATETC;
  458. end;
  459. // -----------------------------------------------------------------------------
  460.  
  461. function TCustomDropSource.GetDataHere(const FormatEtc: TFormatEtc;
  462.   out Medium: TStgMedium):HRESULT; stdcall;
  463. begin
  464.   Result := E_NOTIMPL;
  465. end;
  466. // -----------------------------------------------------------------------------
  467.  
  468. function TCustomDropSource.QueryGetData(const FormatEtc: TFormatEtc): HRESULT; stdcall;
  469. begin
  470.   if (HasFormat(FormatEtc)) then
  471.     Result:= S_OK
  472.   else
  473.     Result:= DV_E_FORMATETC;
  474. end;
  475. // -----------------------------------------------------------------------------
  476.  
  477. function TCustomDropSource.EnumFormatEtc(dwDirection: LongInt;
  478.   out EnumFormatEtc:IEnumFormatEtc): HRESULT; stdcall;
  479. begin
  480.   EnumFormatEtc := GetEnumFormatEtc(dwDirection);
  481.   if (EnumFormatEtc <> nil) then
  482.     Result := S_OK
  483.   else
  484.     Result := E_NOTIMPL;
  485. end;
  486. // -----------------------------------------------------------------------------
  487.  
  488. // Implements IDropSource.QueryContinueDrag
  489. function TCustomDropSource.QueryContinueDrag(fEscapePressed: bool;
  490.   grfKeyState: LongInt): HRESULT; stdcall;
  491. var
  492.   ContinueDrop          : Boolean;
  493.   DragType              : TDragType;
  494. begin
  495.   if FEscapePressed then
  496.     Result := DRAGDROP_S_CANCEL
  497.   // Allow drag and drop with either mouse buttons.
  498.   else if (grfKeyState and (MK_LBUTTON or MK_RBUTTON) = 0) then
  499.   begin
  500.     ContinueDrop := DropEffectToDragType(FeedbackEffect, DragType) and
  501.       (DragType in DragTypes);
  502.  
  503.     InShellDragLoop := False;
  504.  
  505.     // If a valid drop then do OnDrop event if assigned...
  506.     if ContinueDrop and Assigned(OnDrop) then
  507.       OnDrop(Self, DragType, ContinueDrop);
  508.  
  509.     if ContinueDrop then
  510.       Result := DRAGDROP_S_DROP
  511.     else
  512.       Result := DRAGDROP_S_CANCEL;
  513.   end else
  514.     Result := S_OK;
  515. end;
  516. // -----------------------------------------------------------------------------
  517.  
  518. // Implements IDropSource.GiveFeedback
  519. function TCustomDropSource.GiveFeedback(dwEffect: LongInt): HRESULT; stdcall;
  520. var
  521.   UseDefaultCursors: Boolean;
  522. begin
  523.   UseDefaultCursors := True;
  524.   FeedbackEffect := dwEffect;
  525.   if Assigned(OnFeedback) then
  526.     OnFeedback(Self, dwEffect, UseDefaultCursors);
  527.   if UseDefaultCursors then
  528.     Result := DRAGDROP_S_USEDEFAULTCURSORS
  529.   else
  530.     Result := S_OK;
  531. end;
  532. // -----------------------------------------------------------------------------
  533.  
  534. function TCustomDropSource.DoSetData(const FormatEtc: TFormatEtc;
  535.   var Medium: TStgMedium): HRESULT;
  536. var
  537.   Handled: boolean;
  538. begin
  539.   Result := E_NOTIMPL;
  540.   if (Assigned(FOnSetData)) then
  541.   begin
  542.     Handled := False;
  543.     // Fire event to ask user to handle data.
  544.     FOnSetData(Self, FormatEtc, Medium, Handled);
  545.     if (Handled) then
  546.       Result := S_OK;
  547.   end;
  548. end;
  549. // -----------------------------------------------------------------------------
  550.  
  551. procedure TCustomDropSource.SetAllowAsync(const Value: boolean);
  552. begin
  553.   if (FAllowAsync <> Value) then
  554.   begin
  555.     FAllowAsync := Value;
  556.     if (not FAllowAsync) then
  557.     begin
  558.       FRequestAsync := False;
  559.       FIsAsync := False;
  560.     end;
  561.   end;
  562. end;
  563.  
  564. function TCustomDropSource.GetAsyncMode(out fDoOpAsync: LongBool): HRESULT;
  565. begin
  566.   fDoOpAsync := FRequestAsync;
  567.   Result := S_OK;
  568. end;
  569.  
  570. function TCustomDropSource.SetAsyncMode(fDoOpAsync: LongBool): HRESULT;
  571. begin
  572.   if (FAllowAsync) then
  573.   begin
  574.     FRequestAsync := fDoOpAsync;
  575.     Result := S_OK;
  576.   end else
  577.     Result := E_NOTIMPL;
  578. end;
  579.  
  580. function TCustomDropSource.InOperation(out pfInAsyncOp: LongBool): HRESULT;
  581. begin
  582.   pfInAsyncOp := FIsAsync;
  583.   Result := S_OK;
  584. end;
  585.  
  586. function TCustomDropSource.StartOperation(const pbcReserved: IBindCtx): HRESULT;
  587. begin
  588.   if (FRequestAsync) then
  589.   begin
  590.     FIsAsync := True;
  591.     Result := S_OK;
  592.   end else
  593.     Result := E_NOTIMPL;
  594. end;
  595.  
  596. function TCustomDropSource.EndOperation(hResult: HRESULT;
  597.   const pbcReserved: IBindCtx; dwEffects: Cardinal): HRESULT;
  598. var
  599.   DropResult: TDragResult;
  600. begin
  601.   if (FIsAsync) then
  602.   begin
  603.     FIsAsync := False;
  604.     if (Assigned(FOnAfterDrop)) then
  605.     begin
  606.       if (Succeeded(hResult)) then
  607.         DropResult := DropEffectToDragResult(dwEffects and DragTypesToDropEffect(FDragTypes))
  608.       else
  609.         DropResult := drUnknown;
  610.       FOnAfterDrop(Self, DropResult,
  611.         (DropResult <> drDropMove) or (PerformedDropEffect <> DROPEFFECT_MOVE));
  612.     end;
  613.     Result := S_OK;
  614.   end else
  615.     Result := E_FAIL;
  616. end;
  617.  
  618. function TCustomDropSource.Execute: TDragResult;
  619.  
  620.   function GetRGBColor(Value: TColor): DWORD;
  621.   begin
  622.     Result := ColorToRGB(Value);
  623.     case Result of
  624.       clNone: Result := CLR_NONE;
  625.       clDefault: Result := CLR_DEFAULT;
  626.     end;
  627.   end;
  628.  
  629. var
  630.   DropResult: HRESULT;
  631.   AllowedEffects,
  632.   DropEffect: longint;
  633.   IsDraggingImage: boolean;
  634.   shDragImage: TSHDRAGIMAGE;
  635.   shDragBitmap: TBitmap;
  636. begin
  637.   shDragBitmap := nil;
  638.  
  639.   AllowedEffects := DragTypesToDropEffect(FDragTypes);
  640.  
  641.   // Reset the "Performed Drop Effect" value. If it is supported by the target,
  642.   // the target will set it to the desired value when the drop occurs.
  643.   PerformedDropEffect := -1;
  644.  
  645.   if (FShowImage) then
  646.   begin
  647.     // Attempt to create Drag Drop helper object.
  648.     // At present this is only supported on Windows 2000. If the object can't be
  649.     // created, we fall back to the old image list based method (which only
  650.     // works within the application).
  651.     CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER,
  652.       IDragSourceHelper, FDragSourceHelper);
  653.  
  654.     // Display drag image.
  655.     if (FDragSourceHelper <> nil) then
  656.     begin
  657.       IsDraggingImage := True;
  658.       shDragBitmap := TBitmap.Create;
  659.       shDragBitmap.PixelFormat := pfDevice;
  660.       FImages.GetBitmap(ImageIndex, shDragBitmap);
  661.       shDragImage.hbmpDragImage := shDragBitmap.Handle;
  662.       shDragImage.sizeDragImage.cx := shDragBitmap.Width;
  663.       shDragImage.sizeDragImage.cy := shDragBitmap.Height;
  664.       shDragImage.crColorKey := GetRGBColor(FImages.BkColor);
  665.       shDragImage.ptOffset.x := ImageHotSpotX;
  666.       shDragImage.ptOffset.y := ImageHotSpotY;
  667.       if Failed(FDragSourceHelper.InitializeFromBitmap(shDragImage, Self)) then
  668.       begin
  669.         FDragSourceHelper := nil;
  670.         shDragBitmap.Free;
  671.         shDragBitmap := nil;
  672.       end;
  673.     end else
  674.       IsDraggingImage := False;
  675.  
  676.     // Fall back to image list drag image if platform doesn't support
  677.     // IDragSourceHelper or if we "just" failed to initialize properly.
  678.     if (FDragSourceHelper = nil) then
  679.     begin
  680.       IsDraggingImage := ImageList_BeginDrag(FImages.Handle, FImageIndex,
  681.         FImageHotSpot.X, FImageHotSpot.Y);
  682.     end;
  683.   end else
  684.     IsDraggingImage := False;
  685.  
  686.   if (AllowAsyncTransfer) then
  687.     SetAsyncMode(True);
  688.  
  689.   try
  690.     InShellDragLoop := True;
  691.     try
  692.       DropResult := DoDragDrop(Self, Self, AllowedEffects, DropEffect);
  693.     finally
  694.       // InShellDragLoop is also reset in TCustomDropSource.QueryContinueDrag.
  695.       // This is just to make absolutely sure that it is reset (actually no big
  696.       // deal if it isn't).
  697.       InShellDragLoop := False;
  698.     end;
  699.  
  700.   finally
  701.     if IsDraggingImage then
  702.     begin
  703.       if (FDragSourceHelper <> nil) then
  704.       begin
  705.         FDragSourceHelper := nil;
  706.         shDragBitmap.Free;
  707.       end else
  708.         ImageList_EndDrag;
  709.     end;
  710.   end;
  711.  
  712.   case DropResult of
  713.     DRAGDROP_S_DROP:
  714.       (*
  715.       ** Special handling of "optimized move".
  716.       ** If PerformedDropEffect has been set by the target to DROPEFFECT_MOVE
  717.       ** and the drop effect returned from DoDragDrop is different from
  718.       ** DROPEFFECT_MOVE, then an optimized move was performed.
  719.       ** Note: This is different from how MSDN states that an optimized move is
  720.       ** signalled, but matches how Windows 2000 signals an optimized move.
  721.       **
  722.       ** On Windows 2000 an optimized move is signalled by:
  723.       ** 1) Returning DRAGDROP_S_DROP from DoDragDrop.
  724.       ** 2) Setting drop effect to DROPEFFECT_NONE.
  725.       ** 3) Setting the "Performed Dropeffect" format to DROPEFFECT_MOVE.
  726.       **
  727.       ** On previous version of Windows, an optimized move is signalled by:
  728.       ** 1) Returning DRAGDROP_S_DROP from DoDragDrop.
  729.       ** 2) Setting drop effect to DROPEFFECT_MOVE.
  730.       ** 3) Setting the "Performed Dropeffect" format to DROPEFFECT_NONE.
  731.       **
  732.       ** The documentation states that an optimized move is signalled by:
  733.       ** 1) Returning DRAGDROP_S_DROP from DoDragDrop.
  734.       ** 2) Setting drop effect to DROPEFFECT_NONE or DROPEFFECT_COPY.
  735.       ** 3) Setting the "Performed Dropeffect" format to DROPEFFECT_NONE.
  736.       *)
  737.       if (LogicalPerformedDropEffect = DROPEFFECT_MOVE) or
  738.         ((DropEffect <> DROPEFFECT_MOVE) and (PerformedDropEffect = DROPEFFECT_MOVE)) then
  739.         Result := drDropMove
  740.       else
  741.         Result := DropEffectToDragResult(DropEffect and AllowedEffects);
  742.     DRAGDROP_S_CANCEL:
  743.       Result := drCancel;
  744.     E_OUTOFMEMORY:
  745.       Result := drOutMemory;
  746.     else
  747.       // This should never happen!
  748.       Result := drUnknown;
  749.   end;
  750.  
  751.   // Reset PerformedDropEffect if the target didn't set it.
  752.   if (PerformedDropEffect = -1) then
  753.     PerformedDropEffect := DROPEFFECT_NONE;
  754.  
  755.   // Fire OnAfterDrop event unless we are in the middle of an async data
  756.   // transfer.
  757.   if (not AsyncTransfer) and (Assigned(FOnAfterDrop)) then
  758.     FOnAfterDrop(Self, Result,
  759.       (Result = drDropMove) and
  760.       ((DropEffect <> DROPEFFECT_MOVE) or (PerformedDropEffect <> DROPEFFECT_MOVE)));
  761.  
  762. end;
  763. // -----------------------------------------------------------------------------
  764.  
  765. function TCustomDropSource.GetPerformedDropEffect: longInt;
  766. begin
  767.   Result := DROPEFFECT_NONE;
  768. end;
  769.  
  770. function TCustomDropSource.GetLogicalPerformedDropEffect: longInt;
  771. begin
  772.   Result := DROPEFFECT_NONE;
  773. end;
  774.  
  775. procedure TCustomDropSource.SetPerformedDropEffect(const Value: longInt);
  776. begin
  777.   // Not implemented in base class
  778. end;
  779.  
  780. function TCustomDropSource.GetPreferredDropEffect: longInt;
  781. begin
  782.   Result := DROPEFFECT_NONE;
  783. end;
  784.  
  785. procedure TCustomDropSource.SetPreferredDropEffect(const Value: longInt);
  786. begin
  787.   // Not implemented in base class
  788. end;
  789.  
  790. function TCustomDropSource.GetInShellDragLoop: boolean;
  791. begin
  792.   Result := False;
  793. end;
  794.  
  795. function TCustomDropSource.GetTargetCLSID: TCLSID;
  796. begin
  797.   Result := GUID_NULL;
  798. end;
  799.  
  800. procedure TCustomDropSource.SetInShellDragLoop(const Value: boolean);
  801. begin
  802.   // Not implemented in base class
  803. end;
  804.  
  805. procedure TCustomDropSource.DataChanging(Sender: TObject);
  806. begin
  807.   // Data is changing - Flush clipboard to freeze the contents
  808.   FlushClipboard;
  809. end;
  810.  
  811. procedure TCustomDropSource.FlushClipboard;
  812. begin
  813.   // If we have live data on the clipboard...
  814.   if (LiveDataOnClipboard) then
  815.     // ...we force the clipboard to make a static copy of the data
  816.     // before the data changes.
  817.     OleCheck(OleFlushClipboard);
  818. end;
  819.  
  820. procedure TCustomDropSource.EmptyClipboard;
  821. begin
  822.   // If we have live data on the clipboard...
  823.   if (LiveDataOnClipboard) then
  824.     // ...we empty the clipboard.
  825.     OleCheck(OleSetClipboard(nil));
  826. end;
  827.  
  828. function TCustomDropSource.CutToClipboard: boolean;
  829. begin
  830.   PreferredDropEffect := DROPEFFECT_MOVE;
  831.   // Copy data to clipboard
  832.   Result := CutOrCopyToClipboard;
  833. end;
  834. // -----------------------------------------------------------------------------
  835.  
  836. function TCustomDropSource.CopyToClipboard: boolean;
  837. begin
  838.   PreferredDropEffect := DROPEFFECT_COPY;
  839.   // Copy data to clipboard
  840.   Result := CutOrCopyToClipboard;
  841. end;
  842. // -----------------------------------------------------------------------------
  843.  
  844. function TCustomDropSource.CutOrCopyToClipboard: boolean;
  845. begin
  846.   Result := (OleSetClipboard(Self as IDataObject) = S_OK);
  847. end;
  848.  
  849. procedure TCustomDropSource.DoOnPaste(Action: TDragResult; DeleteOnPaste: boolean);
  850. begin
  851.   if (Assigned(FOnPaste)) then
  852.     FOnPaste(Self, Action, DeleteOnPaste);
  853. end;
  854.  
  855. function TCustomDropSource.GetLiveDataOnClipboard: boolean;
  856. begin
  857.   Result := (OleIsCurrentClipboard(Self as IDataObject) = S_OK);
  858. end;
  859.  
  860. // -----------------------------------------------------------------------------
  861.  
  862. procedure TCustomDropSource.SetImages(const Value: TImageList);
  863. begin
  864.   if (FImages = Value) then
  865.     exit;
  866.   FImages := Value;
  867.   if (csLoading in ComponentState) then
  868.     exit;
  869.  
  870.   { DONE -oanme : Shouldn't FShowImage and FImageIndex only be reset if FImages = nil? }
  871.   if (FImages = nil) or (FImageIndex >= FImages.Count) then
  872.     FImageIndex := 0;
  873.   FShowImage := FShowImage and (FImages <> nil) and (FImages.Count > 0);
  874. end;
  875. // -----------------------------------------------------------------------------
  876.  
  877. procedure TCustomDropSource.SetImageIndex(const Value: integer);
  878. begin
  879.   if (csLoading in ComponentState) then
  880.   begin
  881.     FImageIndex := Value;
  882.     exit;
  883.   end;
  884.  
  885.   if (Value < 0) or (FImages.Count = 0) or (FImages = nil) then
  886.   begin
  887.     FImageIndex := 0;
  888.     FShowImage := False;
  889.   end else
  890.     if (Value < FImages.Count) then
  891.       FImageIndex := Value;
  892. end;
  893. // -----------------------------------------------------------------------------
  894.  
  895. procedure TCustomDropSource.SetPoint(Index: integer; Value: integer);
  896. begin
  897.   if (Index = 1) then
  898.     FImageHotSpot.x := Value
  899.   else
  900.     FImageHotSpot.y := Value;
  901. end;
  902. // -----------------------------------------------------------------------------
  903.  
  904. function TCustomDropSource.GetPoint(Index: integer): integer;
  905. begin
  906.   if (Index = 1) then
  907.     Result := FImageHotSpot.x
  908.   else
  909.     Result := FImageHotSpot.y;
  910. end;
  911. // -----------------------------------------------------------------------------
  912.  
  913. procedure TCustomDropSource.SetShowImage(Value: boolean);
  914. begin
  915.   FShowImage := Value;
  916.   if (csLoading in ComponentState) then
  917.     exit;
  918.   if (FImages = nil) then
  919.     FShowImage := False;
  920. end;
  921. // -----------------------------------------------------------------------------
  922.  
  923. procedure TCustomDropSource.Notification(AComponent: TComponent;
  924.   Operation: TOperation);
  925. begin
  926.   inherited Notification(AComponent, Operation);
  927.   if (Operation = opRemove) and (AComponent = FImages) then
  928.     Images := nil;
  929. end;
  930.  
  931.  
  932. ////////////////////////////////////////////////////////////////////////////////
  933. //
  934. //              TEnumFormatEtc
  935. //
  936. ////////////////////////////////////////////////////////////////////////////////
  937. // Format enumerator used by TCustomDropMultiTarget.
  938. ////////////////////////////////////////////////////////////////////////////////
  939. type
  940.   TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc)
  941.   private
  942.     FFormats            : TClipboardFormats;
  943.     FIndex              : integer;
  944.   protected
  945.     constructor CreateClone(AFormats: TClipboardFormats; AIndex: Integer);
  946.   public
  947.     constructor Create(AFormats: TDataFormats; Direction: TDataDirection);
  948.     { IEnumFormatEtc implentation }
  949.     function Next(Celt: LongInt; out Elt; pCeltFetched: pLongInt): HRESULT; stdcall;
  950.     function Skip(Celt: LongInt): HRESULT; stdcall;
  951.     function Reset: HRESULT; stdcall;
  952.     function Clone(out Enum: IEnumFormatEtc): HRESULT; stdcall;
  953.   end;
  954.  
  955. constructor TEnumFormatEtc.Create(AFormats: TDataFormats; Direction: TDataDirection);
  956. var
  957.   i, j                  : integer;
  958. begin
  959.   inherited Create;
  960.   FFormats := TClipboardFormats.Create(nil, False);
  961.   FIndex := 0;
  962.   for i := 0 to AFormats.Count-1 do
  963.     for j := 0 to AFormats[i].CompatibleFormats.Count-1 do
  964.       if (Direction in AFormats[i].CompatibleFormats[j].DataDirections) and
  965.         (not FFormats.Contain(TClipboardFormatClass(AFormats[i].CompatibleFormats[j].ClassType))) then
  966.         FFormats.Add(AFormats[i].CompatibleFormats[j]);
  967. end;
  968.  
  969. constructor TEnumFormatEtc.CreateClone(AFormats: TClipboardFormats; AIndex: Integer);
  970. var
  971.   i                     : integer;
  972. begin
  973.   inherited Create;
  974.   FFormats := TClipboardFormats.Create(nil, False);
  975.   FIndex := AIndex;
  976.   for i := 0 to AFormats.Count-1 do
  977.     FFormats.Add(AFormats[i]);
  978. end;
  979.  
  980. function TEnumFormatEtc.Next(Celt: LongInt; out Elt;
  981.   pCeltFetched: pLongInt): HRESULT;
  982. var
  983.   i                     : integer;
  984.   FormatEtc             : PFormatEtc;
  985. begin
  986.   i := 0;
  987.   FormatEtc := PFormatEtc(@Elt);
  988.   while (i < Celt) and (FIndex < FFormats.Count) do
  989.   begin
  990.     FormatEtc^ := FFormats[FIndex].FormatEtc;
  991.     Inc(FormatEtc);
  992.     Inc(i);
  993.     Inc(FIndex);
  994.   end;
  995.  
  996.   if (pCeltFetched <> nil) then
  997.     pCeltFetched^ := i;
  998.  
  999.   if (i = Celt) then
  1000.     Result := S_OK
  1001.   else
  1002.     Result := S_FALSE;
  1003. end;
  1004.  
  1005. function TEnumFormatEtc.Skip(Celt: LongInt): HRESULT;
  1006. begin
  1007.   if (FIndex + Celt <= FFormats.Count) then
  1008.   begin
  1009.     inc(FIndex, Celt);
  1010.     Result := S_OK;
  1011.   end else
  1012.   begin
  1013.     FIndex := FFormats.Count;
  1014.     Result := S_FALSE;
  1015.   end;
  1016. end;
  1017.  
  1018. function TEnumFormatEtc.Reset: HRESULT;
  1019. begin
  1020.   FIndex := 0;
  1021.   Result := S_OK;
  1022. end;
  1023.  
  1024. function TEnumFormatEtc.Clone(out Enum: IEnumFormatEtc): HRESULT;
  1025. begin
  1026.   Enum := TEnumFormatEtc.CreateClone(FFormats, FIndex);
  1027.   Result := S_OK;
  1028. end;
  1029.  
  1030.  
  1031. ////////////////////////////////////////////////////////////////////////////////
  1032. //
  1033. //              TCustomDropMultiSource
  1034. //
  1035. ////////////////////////////////////////////////////////////////////////////////
  1036. type
  1037.   TSourceDataFormats = class(TDataFormats)
  1038.   public
  1039.     function Add(DataFormat: TCustomDataFormat): integer; override;
  1040.   end;
  1041.  
  1042. function TSourceDataFormats.Add(DataFormat: TCustomDataFormat): integer;
  1043. begin
  1044.   Result := inherited Add(DataFormat);
  1045.   // Set up change notification so drop source can flush clipboard if data changes.
  1046.   DataFormat.OnChanging := TCustomDropMultiSource(DataFormat.Owner).DataChanging;
  1047. end;
  1048.  
  1049. constructor TCustomDropMultiSource.Create(AOwner: TComponent);
  1050. begin
  1051.   inherited Create(AOwner);
  1052.   FDataFormats := TSourceDataFormats.Create;
  1053.   FFeedbackDataFormat := TFeedbackDataFormat.Create(Self);
  1054.   FRawDataFormat := TRawDataFormat.Create(Self);
  1055. end;
  1056.  
  1057. destructor TCustomDropMultiSource.Destroy;
  1058. var
  1059.   i                     : integer;
  1060. begin
  1061.   EmptyClipboard;
  1062.   // Delete all target formats owned by the object
  1063.   for i := FDataFormats.Count-1 downto 0 do
  1064.     FDataFormats[i].Free;
  1065.   FDataFormats.Free;
  1066.   inherited Destroy;
  1067. end;
  1068.  
  1069. function TCustomDropMultiSource.DoGetData(const FormatEtcIn: TFormatEtc;
  1070.   out Medium: TStgMedium): HRESULT;
  1071. var
  1072.   i, j: integer;
  1073.   DF: TCustomDataFormat;
  1074.   CF: TClipboardFormat;
  1075. begin
  1076.   // TODO : Add support for delayed rendering with OnRenderData event.
  1077.   Medium.tymed := 0;
  1078.   Medium.UnkForRelease := nil;
  1079.   Medium.hGlobal := 0;
  1080.  
  1081.   Result := DV_E_FORMATETC;
  1082.  
  1083.   (*
  1084.   ** Loop through all data formats associated with this drop source to find one
  1085.   ** which can offer the clipboard format requested by the target.
  1086.   *)
  1087.   for i := 0 to DataFormats.Count-1 do
  1088.   begin
  1089.     DF := DataFormats[i];
  1090.  
  1091.     // Ignore empty data formats.
  1092.     if (not DF.HasData) then
  1093.       continue;
  1094.  
  1095.     (*
  1096.     ** Loop through all the data format's supported clipboard formats to find
  1097.     ** one which contains data and can provide it in the format requested by the
  1098.     ** target.
  1099.     *)
  1100.     for j := 0 to DF.CompatibleFormats.Count-1 do
  1101.     begin
  1102.       CF := DF.CompatibleFormats[j];
  1103.       (*
  1104.       ** 1) Determine if the clipboard format supports the format requested by
  1105.       **    the target.
  1106.       ** 2) Transfer data from the data format object to the clipboard format
  1107.       **    object.
  1108.       ** 3) Determine if the clipboard format object now has data to offer.
  1109.       ** 4) Transfer the data from the clipboard format object to the medium.
  1110.       *)
  1111.       if (CF.AcceptFormat(FormatEtcIn)) and
  1112.         (DataFormats[i].AssignTo(CF)) and
  1113.         (CF.HasData) and
  1114.         (CF.SetDataToMedium(FormatEtcIn, Medium)) then
  1115.       begin
  1116.         // Once data has been sucessfully transfered to the medium, we clear
  1117.         // the data in the TClipboardFormat object in order to conserve
  1118.         // resources.
  1119.         CF.Clear;
  1120.         Result := S_OK;
  1121.         exit;
  1122.       end;
  1123.     end;
  1124.   end;
  1125. end;
  1126.  
  1127. function TCustomDropMultiSource.DoSetData(const FormatEtc: TFormatEtc;
  1128.   var Medium: TStgMedium): HRESULT;
  1129. var
  1130.   i, j                  : integer;
  1131.   GenericClipboardFormat: TRawClipboardFormat;
  1132. begin
  1133.   Result := E_NOTIMPL;
  1134.  
  1135.   // Get data for requested source format.
  1136.   for i := 0 to DataFormats.Count-1 do
  1137.     for j := 0 to DataFormats[i].CompatibleFormats.Count-1 do
  1138.       if (DataFormats[i].CompatibleFormats[j].AcceptFormat(FormatEtc)) and
  1139.         (DataFormats[i].CompatibleFormats[j].GetDataFromMedium(Self, Medium)) and
  1140.         (DataFormats[i].Assign(DataFormats[i].CompatibleFormats[j])) then
  1141.       begin
  1142.         DoOnSetData(DataFormats[i], DataFormats[i].CompatibleFormats[j]);
  1143.         // Once data has been sucessfully transfered to the medium, we clear
  1144.         // the data in the TClipboardFormat object in order to conserve
  1145.         // resources.
  1146.         DataFormats[i].CompatibleFormats[j].Clear;
  1147.         Result := S_OK;
  1148.         exit;
  1149.       end;
  1150.  
  1151.   // The requested data format wasn't supported by any of the registered
  1152.   // clipboard formats, but in order to support the Windows 2000 drag drop helper
  1153.   // object we have to accept any data which is written to the IDataObject.
  1154.   // To do this we create a new clipboard format object, initialize it with the
  1155.   // format information passed to us and copy the data.
  1156.   GenericClipboardFormat := TRawClipboardFormat.CreateFormatEtc(FormatEtc);
  1157.   FRawDataFormat.CompatibleFormats.Add(GenericClipboardFormat);
  1158.   if (GenericClipboardFormat.GetDataFromMedium(Self, Medium)) and
  1159.     (FRawDataFormat.Assign(GenericClipboardFormat)) then
  1160.     Result := S_OK;
  1161. end;
  1162.  
  1163. function TCustomDropMultiSource.GetEnumFormatEtc(dwDirection: Integer): IEnumFormatEtc;
  1164. begin
  1165.   if (dwDirection = DATADIR_GET) then
  1166.     Result := TEnumFormatEtc.Create(FDataFormats, ddRead)
  1167.   else if (dwDirection = DATADIR_SET) then
  1168.     Result := TEnumFormatEtc.Create(FDataFormats, ddWrite)
  1169.   else
  1170.     Result := nil;
  1171. end;
  1172.  
  1173. function TCustomDropMultiSource.HasFormat(const FormatEtc: TFormatEtc): boolean;
  1174. var
  1175.   i                     ,
  1176.   j                     : integer;
  1177. begin
  1178.   Result := False;
  1179.  
  1180.   for i := 0 to DataFormats.Count-1 do
  1181.     for j := 0 to DataFormats[i].CompatibleFormats.Count-1 do
  1182.       if (DataFormats[i].CompatibleFormats[j].AcceptFormat(FormatEtc)) then
  1183.       begin
  1184.         Result := True;
  1185.         exit;
  1186.       end;
  1187. end;
  1188.  
  1189. function TCustomDropMultiSource.GetPerformedDropEffect: longInt;
  1190. begin
  1191.   Result := FFeedbackDataFormat.PerformedDropEffect;
  1192. end;
  1193.  
  1194. function TCustomDropMultiSource.GetLogicalPerformedDropEffect: longInt;
  1195. begin
  1196.   Result := FFeedbackDataFormat.LogicalPerformedDropEffect;
  1197. end;
  1198.  
  1199. function TCustomDropMultiSource.GetPreferredDropEffect: longInt;
  1200. begin
  1201.   Result := FFeedbackDataFormat.PreferredDropEffect;
  1202. end;
  1203.  
  1204. procedure TCustomDropMultiSource.SetPerformedDropEffect(const Value: longInt);
  1205. begin
  1206.   FFeedbackDataFormat.PerformedDropEffect := Value;
  1207. end;
  1208.  
  1209. procedure TCustomDropMultiSource.SetPreferredDropEffect(const Value: longInt);
  1210. begin
  1211.   FFeedbackDataFormat.PreferredDropEffect := Value;
  1212. end;
  1213.  
  1214. function TCustomDropMultiSource.GetInShellDragLoop: boolean;
  1215. begin
  1216.   Result := FFeedbackDataFormat.InShellDragLoop;
  1217. end;
  1218.  
  1219. procedure TCustomDropMultiSource.SetInShellDragLoop(const Value: boolean);
  1220. begin
  1221.   FFeedbackDataFormat.InShellDragLoop := Value;
  1222. end;
  1223.  
  1224. function TCustomDropMultiSource.GetTargetCLSID: TCLSID;
  1225. begin
  1226.   Result := FFeedbackDataFormat.TargetCLSID;
  1227. end;
  1228.  
  1229. procedure TCustomDropMultiSource.DoOnSetData(DataFormat: TCustomDataFormat;
  1230.   ClipboardFormat: TClipboardFormat);
  1231. var
  1232.   DropEffect            : longInt;
  1233. begin
  1234.   if (ClipboardFormat is TPasteSuccededClipboardFormat) then
  1235.   begin
  1236.     DropEffect := TPasteSuccededClipboardFormat(ClipboardFormat).Value;
  1237.     DoOnPaste(DropEffectToDragResult(DropEffect),
  1238.       (DropEffect = DROPEFFECT_MOVE) and (PerformedDropEffect = DROPEFFECT_MOVE));
  1239.   end;
  1240. end;
  1241.  
  1242. ////////////////////////////////////////////////////////////////////////////////
  1243. //
  1244. //              TDropSourceThread
  1245. //
  1246. ////////////////////////////////////////////////////////////////////////////////
  1247. constructor TDropSourceThread.Create(ADropSource: TCustomDropSource;
  1248.   AFreeOnTerminate: Boolean);
  1249. begin
  1250.   inherited Create(True);
  1251.   FreeOnTerminate := AFreeOnTerminate;
  1252.   FDropSource := ADropSource;
  1253.   FDragResult := drAsync;
  1254. end;
  1255.  
  1256. procedure TDropSourceThread.Execute;
  1257. var
  1258.   pt: TPoint;
  1259.   hwndAttach: HWND;
  1260.   dwAttachThreadID, dwCurrentThreadID : DWORD;
  1261. begin
  1262.   (*
  1263.   ** See Microsoft Knowledgebase Article Q139408 for an explanation of the
  1264.   ** AttachThreadInput stuff.
  1265.   **   http://support.microsoft.com/support/kb/articles/Q139/4/08.asp
  1266.   *)
  1267.  
  1268.   // Get handle of window under mouse-cursor.
  1269.   GetCursorPos(pt);
  1270.   hwndAttach := WindowFromPoint(pt);
  1271.   ASSERT(hwndAttach<>0, 'Can''t find window with drag-object');
  1272.  
  1273.   // Get thread IDs.
  1274.   dwAttachThreadID := GetWindowThreadProcessId(hwndAttach, nil);
  1275.   dwCurrentThreadID := GetCurrentThreadId();
  1276.  
  1277.   // Attach input queues if necessary.
  1278.   if (dwAttachThreadID <> dwCurrentThreadID) then
  1279.     AttachThreadInput(dwAttachThreadID, dwCurrentThreadID, True);
  1280.   try
  1281.  
  1282.     // Initialize OLE for this thread.
  1283.     OleInitialize(nil);
  1284.     try
  1285.       // Start drag & drop.
  1286.       FDragResult := FDropSource.Execute;
  1287.     finally
  1288.       OleUninitialize;
  1289.     end;
  1290.  
  1291.   finally
  1292.     // Restore input queue settings.
  1293.     if (dwAttachThreadID <> dwCurrentThreadID) then
  1294.       AttachThreadInput(dwAttachThreadID, dwCurrentThreadID, False);
  1295.     // Set Terminated flag so owner knows that drag has finished.
  1296.     Terminate;
  1297.   end;
  1298. end;
  1299.  
  1300. end.
  1301.  
  1302.