| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302 |
- unit DropSource;
- // -----------------------------------------------------------------------------
- // Project: Drag and Drop Component Suite
- // Module: DropSource
- // Description: Implements Dragging & Dropping of data
- // FROM your application to another.
- // Version: 4.0
- // Date: 18-MAY-2001
- // Target: Win32, Delphi 5-6
- // Authors: Anders Melander, anders@melander.dk, http://www.melander.dk
- // Copyright © 1997-2001 Angus Johnson & Anders Melander
- // -----------------------------------------------------------------------------
- // General changes:
- // - Some component glyphs has changed.
- //
- // TDropSource changes:
- // - CutToClipboard and CopyToClipboard now uses OleSetClipboard.
- // This means that descendant classes no longer needs to override the
- // CutOrCopyToClipboard method.
- // - New OnGetData event.
- // - Changed to use new V4 architecture:
- // * All clipboard format support has been removed from TDropSource, it has
- // been renamed to TCustomDropSource and the old TDropSource has been
- // modified to descend from TCustomDropSource and has moved to the
- // DropSource3 unit. TDropSource is now supported for backwards
- // compatibility only and will be removed in a future version.
- // * A new TCustomDropMultiSource, derived from TCustomDropSource, uses the
- // new architecture (with TClipboardFormat and TDataFormat) and is the new
- // base class for all the drop source components.
- // - TInterfacedComponent moved to DragDrop unit.
- // -----------------------------------------------------------------------------
- // TODO -oanme -cCheckItOut : OleQueryLinkFromData
- // TODO -oanme -cDocumentation : CutToClipboard and CopyToClipboard alters the value of PreferredDropEffect.
- // TODO -oanme -cDocumentation : Clipboard must be flushed or emptied manually after CutToClipboard and CopyToClipboard. Automatic flush is not guaranteed.
- // TODO -oanme -cDocumentation : Delete-on-paste. Why and How.
- // TODO -oanme -cDocumentation : Optimized move. Why and How.
- // 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.
- // TODO -oanme -cDocumentation : DragDetectPlus. Why and How.
- // -----------------------------------------------------------------------------
- interface
- uses
- DragDrop,
- DragDropFormats,
- ActiveX,
- Controls,
- Windows,
- Classes;
- {$include DragDrop.inc}
- type
- TDragResult = (drDropCopy, drDropMove, drDropLink, drCancel,
- drOutMemory, drAsync, drUnknown);
- TDropEvent = procedure(Sender: TObject; DragType: TDragType;
- var ContinueDrop: Boolean) of object;
- //: TAfterDropEvent is fired after the target has finished processing a
- // successfull drop.
- // The Optimized parameter is True if the target either performed an operation
- // other than a move or performed an "optimized move". In either cases, the
- // source isn't required to delete the source data.
- // If the Optimized parameter is False, the target performed an "unoptimized
- // move" operation and the source is required to delete the source data to
- // complete the move operation.
- TAfterDropEvent = procedure(Sender: TObject; DragResult: TDragResult;
- Optimized: Boolean) of object;
- TFeedbackEvent = procedure(Sender: TObject; Effect: LongInt;
- var UseDefaultCursors: Boolean) of object;
- //: The TDropDataEvent event is fired when the target requests data from the
- // drop source or offers data to the drop source.
- // The Handled flag should be set if the event handler satisfied the request.
- TDropDataEvent = procedure(Sender: TObject; const FormatEtc: TFormatEtc;
- out Medium: TStgMedium; var Handled: Boolean) of object;
- //: TPasteEvent is fired when the target sends a "Paste Succeeded" value
- // back to the drop source after a clipboard transfer.
- // The DeleteOnPaste parameter is True if the source is required to delete
- // the source data. This will only occur after a CutToClipboard operation
- // (corresponds to a move drag/drop).
- TPasteEvent = procedure(Sender: TObject; Action: TDragResult;
- DeleteOnPaste: boolean) of object;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // TCustomDropSource
- //
- ////////////////////////////////////////////////////////////////////////////////
- // Abstract base class for all Drop Source components.
- // Implements the IDropSource and IDataObject interfaces.
- ////////////////////////////////////////////////////////////////////////////////
- TCustomDropSource = class(TDragDropComponent, IDropSource, IDataObject,
- IAsyncOperation)
- private
- FDragTypes: TDragTypes;
- FFeedbackEffect: LongInt;
- // Events...
- FOnDrop: TDropEvent;
- FOnAfterDrop: TAfterDropEvent;
- FOnFeedback: TFeedBackEvent;
- FOnGetData: TDropDataEvent;
- FOnSetData: TDropDataEvent;
- FOnPaste: TPasteEvent;
- // Drag images...
- FImages: TImageList;
- FShowImage: boolean;
- FImageIndex: integer;
- FImageHotSpot: TPoint;
- FDragSourceHelper: IDragSourceHelper;
- // Async transfer...
- FAllowAsync: boolean;
- FRequestAsync: boolean;
- FIsAsync: boolean;
- protected
- property FeedbackEffect: LongInt read FFeedbackEffect write FFeedbackEffect;
- // IDropSource implementation
- function QueryContinueDrag(fEscapePressed: bool;
- grfKeyState: LongInt): HRESULT; stdcall;
- function GiveFeedback(dwEffect: LongInt): HRESULT; stdcall;
- // IDataObject implementation
- function GetData(const FormatEtcIn: TFormatEtc;
- out Medium: TStgMedium):HRESULT; stdcall;
- function GetDataHere(const FormatEtc: TFormatEtc;
- out Medium: TStgMedium):HRESULT; stdcall;
- function QueryGetData(const FormatEtc: TFormatEtc): HRESULT; stdcall;
- function GetCanonicalFormatEtc(const FormatEtc: TFormatEtc;
- out FormatEtcout: TFormatEtc): HRESULT; stdcall;
- function SetData(const FormatEtc: TFormatEtc; var Medium: TStgMedium;
- fRelease: Bool): HRESULT; stdcall;
- function EnumFormatEtc(dwDirection: LongInt;
- out EnumFormatEtc: IEnumFormatEtc): HRESULT; stdcall;
- function dAdvise(const FormatEtc: TFormatEtc; advf: LongInt;
- const advsink: IAdviseSink; out dwConnection: LongInt): HRESULT; stdcall;
- function dUnadvise(dwConnection: LongInt): HRESULT; stdcall;
- function EnumdAdvise(out EnumAdvise: IEnumStatData): HRESULT; stdcall;
- // IAsyncOperation implementation
- function EndOperation(hResult: HRESULT; const pbcReserved: IBindCtx;
- dwEffects: Cardinal): HRESULT; stdcall;
- function GetAsyncMode(out fDoOpAsync: LongBool): HRESULT; stdcall;
- function InOperation(out pfInAsyncOp: LongBool): HRESULT; stdcall;
- function SetAsyncMode(fDoOpAsync: LongBool): HRESULT; stdcall;
- function StartOperation(const pbcReserved: IBindCtx): HRESULT; stdcall;
- // Abstract methods
- function DoGetData(const FormatEtcIn: TFormatEtc;
- out Medium: TStgMedium): HRESULT; virtual; abstract;
- function DoSetData(const FormatEtc: TFormatEtc;
- var Medium: TStgMedium): HRESULT; virtual;
- function HasFormat(const FormatEtc: TFormatEtc): boolean; virtual; abstract;
- function GetEnumFormatEtc(dwDirection: LongInt): IEnumFormatEtc; virtual; abstract;
- // Data format event sink
- procedure DataChanging(Sender: TObject); virtual;
- // Clipboard
- function CutOrCopyToClipboard: boolean; virtual;
- procedure DoOnPaste(Action: TDragResult; DeleteOnPaste: boolean); virtual;
- // Property access
- procedure SetShowImage(Value: boolean);
- procedure SetImages(const Value: TImageList);
- procedure SetImageIndex(const Value: integer);
- procedure SetPoint(Index: integer; Value: integer);
- function GetPoint(Index: integer): integer;
- function GetPerformedDropEffect: longInt; virtual;
- function GetLogicalPerformedDropEffect: longInt; virtual;
- procedure SetPerformedDropEffect(const Value: longInt); virtual;
- function GetPreferredDropEffect: longInt; virtual;
- procedure SetPreferredDropEffect(const Value: longInt); virtual;
- function GetInShellDragLoop: boolean; virtual;
- function GetTargetCLSID: TCLSID; virtual;
- procedure SetInShellDragLoop(const Value: boolean); virtual;
- function GetLiveDataOnClipboard: boolean;
- procedure SetAllowAsync(const Value: boolean);
- // Component management
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- property DragSourceHelper: IDragSourceHelper read FDragSourceHelper;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- function Execute: TDragResult; virtual;
- function CutToClipboard: boolean; virtual;
- function CopyToClipboard: boolean; virtual;
- procedure FlushClipboard; virtual;
- procedure EmptyClipboard; virtual;
- property PreferredDropEffect: longInt read GetPreferredDropEffect
- write SetPreferredDropEffect;
- property PerformedDropEffect: longInt read GetPerformedDropEffect
- write SetPerformedDropEffect;
- property LogicalPerformedDropEffect: longInt read GetLogicalPerformedDropEffect;
- property InShellDragLoop: boolean read GetInShellDragLoop
- write SetInShellDragLoop;
- property TargetCLSID: TCLSID read GetTargetCLSID;
- property LiveDataOnClipboard: boolean read GetLiveDataOnClipboard;
- property AsyncTransfer: boolean read FIsAsync;
- published
- property DragTypes: TDragTypes read FDragTypes write FDragTypes;
- // Events
- property OnFeedback: TFeedbackEvent read FOnFeedback write FOnFeedback;
- property OnDrop: TDropEvent read FOnDrop write FOnDrop;
- property OnAfterDrop: TAfterDropEvent read FOnAfterDrop write FOnAfterDrop;
- property OnGetData: TDropDataEvent read FOnGetData write FOnGetData;
- property OnSetData: TDropDataEvent read FOnSetData write FOnSetData;
- property OnPaste: TPasteEvent read FOnPaste write FOnPaste;
- // Drag Images...
- property Images: TImageList read FImages write SetImages;
- property ImageIndex: integer read FImageIndex write SetImageIndex;
- property ShowImage: boolean read FShowImage write SetShowImage;
- property ImageHotSpotX: integer index 1 read GetPoint write SetPoint;
- property ImageHotSpotY: integer index 2 read GetPoint write SetPoint;
- // Async transfer...
- property AllowAsyncTransfer: boolean read FAllowAsync write SetAllowAsync;
- end;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // TCustomDropMultiSource
- //
- ////////////////////////////////////////////////////////////////////////////////
- // Drop target base class which can accept multiple formats.
- ////////////////////////////////////////////////////////////////////////////////
- TCustomDropMultiSource = class(TCustomDropSource)
- private
- FFeedbackDataFormat: TFeedbackDataFormat;
- FRawDataFormat: TRawDataFormat;
- protected
- function DoGetData(const FormatEtcIn: TFormatEtc;
- out Medium: TStgMedium):HRESULT; override;
- function DoSetData(const FormatEtc: TFormatEtc;
- var Medium: TStgMedium): HRESULT; override;
- function HasFormat(const FormatEtc: TFormatEtc): boolean; override;
- function GetEnumFormatEtc(dwDirection: LongInt): IEnumFormatEtc; override;
- function GetPerformedDropEffect: longInt; override;
- function GetLogicalPerformedDropEffect: longInt; override;
- function GetPreferredDropEffect: longInt; override;
- procedure SetPerformedDropEffect(const Value: longInt); override;
- procedure SetPreferredDropEffect(const Value: longInt); override;
- function GetInShellDragLoop: boolean; override;
- procedure SetInShellDragLoop(const Value: boolean); override;
- function GetTargetCLSID: TCLSID; override;
- procedure DoOnSetData(DataFormat: TCustomDataFormat;
- ClipboardFormat: TClipboardFormat);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- property DataFormats;
- // TODO : Add support for delayed rendering with OnRenderData event.
- published
- end;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // TDropEmptySource
- //
- ////////////////////////////////////////////////////////////////////////////////
- // Do-nothing source for use with TDataFormatAdapter and such
- ////////////////////////////////////////////////////////////////////////////////
- TDropEmptySource = class(TCustomDropMultiSource);
- ////////////////////////////////////////////////////////////////////////////////
- //
- // TDropSourceThread
- //
- ////////////////////////////////////////////////////////////////////////////////
- // Executes a drop source operation from a thread.
- // TDropSourceThread is an alternative to the Windows 2000 Asynchronous Data
- // Transfer support.
- ////////////////////////////////////////////////////////////////////////////////
- type
- TDropSourceThread = class(TThread)
- private
- FDropSource: TCustomDropSource;
- FDragResult: TDragResult;
- protected
- procedure Execute; override;
- public
- constructor Create(ADropSource: TCustomDropSource; AFreeOnTerminate: Boolean);
- property DragResult: TDragResult read FDragResult;
- property Terminated;
- end;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Utility functions
- //
- ////////////////////////////////////////////////////////////////////////////////
- function DropEffectToDragResult(DropEffect: longInt): TDragResult;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Component registration
- //
- ////////////////////////////////////////////////////////////////////////////////
- procedure Register;
- (*******************************************************************************
- **
- ** IMPLEMENTATION
- **
- *******************************************************************************)
- implementation
- uses
- CommCtrl,
- ComObj,
- Graphics;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Component registration
- //
- ////////////////////////////////////////////////////////////////////////////////
- procedure Register;
- begin
- RegisterComponents(DragDropComponentPalettePage, [TDropEmptySource]);
- end;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Utility functions
- //
- ////////////////////////////////////////////////////////////////////////////////
- function DropEffectToDragResult(DropEffect: longInt): TDragResult;
- begin
- case DropEffect of
- DROPEFFECT_NONE:
- Result := drCancel;
- DROPEFFECT_COPY:
- Result := drDropCopy;
- DROPEFFECT_MOVE:
- Result := drDropMove;
- DROPEFFECT_LINK:
- Result := drDropLink;
- else
- Result := drUnknown; // This is probably an error condition
- end;
- end;
- // -----------------------------------------------------------------------------
- // TCustomDropSource
- // -----------------------------------------------------------------------------
- constructor TCustomDropSource.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- DragTypes := [dtCopy]; //default to Copy.
- // Note: Normally we would call _AddRef or coLockObjectExternal(Self) here to
- // make sure that the component wasn't deleted prematurely (e.g. after a call
- // to RegisterDragDrop), but since our ancestor class TInterfacedComponent
- // disables reference counting, we do not need to do so.
- FImageHotSpot := Point(16,16);
- FImages := nil;
- end;
- destructor TCustomDropSource.Destroy;
- begin
- // TODO -oanme -cImprovement : Maybe FlushClipboard would be more appropiate?
- EmptyClipboard;
- inherited Destroy;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.GetCanonicalFormatEtc(const FormatEtc: TFormatEtc;
- out FormatEtcout: TFormatEtc): HRESULT;
- begin
- Result := DATA_S_SAMEFORMATETC;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.SetData(const FormatEtc: TFormatEtc;
- var Medium: TStgMedium; fRelease: Bool): HRESULT;
- begin
- // Warning: Ordinarily it would be much more efficient to just call
- // HasFormat(FormatEtc) to determine if we support the given format, but
- // because we have to able to accept *all* data formats, even unknown ones, in
- // order to support the Windows 2000 drag helper functionality, we can't
- // reject any formats here. Instead we pass the request on to DoSetData and
- // let it worry about the details.
- // if (HasFormat(FormatEtc)) then
- // begin
- try
- Result := DoSetData(FormatEtc, Medium);
- finally
- if (fRelease) then
- ReleaseStgMedium(Medium);
- end;
- // end else
- // Result:= DV_E_FORMATETC;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.DAdvise(const FormatEtc: TFormatEtc; advf: LongInt;
- const advSink: IAdviseSink; out dwConnection: LongInt): HRESULT;
- begin
- Result := OLE_E_ADVISENOTSUPPORTED;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.DUnadvise(dwConnection: LongInt): HRESULT;
- begin
- Result := OLE_E_ADVISENOTSUPPORTED;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.EnumDAdvise(out EnumAdvise: IEnumStatData): HRESULT;
- begin
- Result := OLE_E_ADVISENOTSUPPORTED;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.GetData(const FormatEtcIn: TFormatEtc;
- out Medium: TStgMedium):HRESULT; stdcall;
- var
- Handled: boolean;
- begin
- Handled := False;
- if (Assigned(FOnGetData)) then
- // Fire event to ask user for data.
- FOnGetData(Self, FormatEtcIn, Medium, Handled);
- // If user provided data, there is no need to call descendant for it.
- if (Handled) then
- Result := S_OK
- else if (HasFormat(FormatEtcIn)) then
- // Call descendant class to get data.
- Result := DoGetData(FormatEtcIn, Medium)
- else
- Result:= DV_E_FORMATETC;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.GetDataHere(const FormatEtc: TFormatEtc;
- out Medium: TStgMedium):HRESULT; stdcall;
- begin
- Result := E_NOTIMPL;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.QueryGetData(const FormatEtc: TFormatEtc): HRESULT; stdcall;
- begin
- if (HasFormat(FormatEtc)) then
- Result:= S_OK
- else
- Result:= DV_E_FORMATETC;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.EnumFormatEtc(dwDirection: LongInt;
- out EnumFormatEtc:IEnumFormatEtc): HRESULT; stdcall;
- begin
- EnumFormatEtc := GetEnumFormatEtc(dwDirection);
- if (EnumFormatEtc <> nil) then
- Result := S_OK
- else
- Result := E_NOTIMPL;
- end;
- // -----------------------------------------------------------------------------
- // Implements IDropSource.QueryContinueDrag
- function TCustomDropSource.QueryContinueDrag(fEscapePressed: bool;
- grfKeyState: LongInt): HRESULT; stdcall;
- var
- ContinueDrop : Boolean;
- DragType : TDragType;
- begin
- if FEscapePressed then
- Result := DRAGDROP_S_CANCEL
- // Allow drag and drop with either mouse buttons.
- else if (grfKeyState and (MK_LBUTTON or MK_RBUTTON) = 0) then
- begin
- ContinueDrop := DropEffectToDragType(FeedbackEffect, DragType) and
- (DragType in DragTypes);
- InShellDragLoop := False;
- // If a valid drop then do OnDrop event if assigned...
- if ContinueDrop and Assigned(OnDrop) then
- OnDrop(Self, DragType, ContinueDrop);
- if ContinueDrop then
- Result := DRAGDROP_S_DROP
- else
- Result := DRAGDROP_S_CANCEL;
- end else
- Result := S_OK;
- end;
- // -----------------------------------------------------------------------------
- // Implements IDropSource.GiveFeedback
- function TCustomDropSource.GiveFeedback(dwEffect: LongInt): HRESULT; stdcall;
- var
- UseDefaultCursors: Boolean;
- begin
- UseDefaultCursors := True;
- FeedbackEffect := dwEffect;
- if Assigned(OnFeedback) then
- OnFeedback(Self, dwEffect, UseDefaultCursors);
- if UseDefaultCursors then
- Result := DRAGDROP_S_USEDEFAULTCURSORS
- else
- Result := S_OK;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.DoSetData(const FormatEtc: TFormatEtc;
- var Medium: TStgMedium): HRESULT;
- var
- Handled: boolean;
- begin
- Result := E_NOTIMPL;
- if (Assigned(FOnSetData)) then
- begin
- Handled := False;
- // Fire event to ask user to handle data.
- FOnSetData(Self, FormatEtc, Medium, Handled);
- if (Handled) then
- Result := S_OK;
- end;
- end;
- // -----------------------------------------------------------------------------
- procedure TCustomDropSource.SetAllowAsync(const Value: boolean);
- begin
- if (FAllowAsync <> Value) then
- begin
- FAllowAsync := Value;
- if (not FAllowAsync) then
- begin
- FRequestAsync := False;
- FIsAsync := False;
- end;
- end;
- end;
- function TCustomDropSource.GetAsyncMode(out fDoOpAsync: LongBool): HRESULT;
- begin
- fDoOpAsync := FRequestAsync;
- Result := S_OK;
- end;
- function TCustomDropSource.SetAsyncMode(fDoOpAsync: LongBool): HRESULT;
- begin
- if (FAllowAsync) then
- begin
- FRequestAsync := fDoOpAsync;
- Result := S_OK;
- end else
- Result := E_NOTIMPL;
- end;
- function TCustomDropSource.InOperation(out pfInAsyncOp: LongBool): HRESULT;
- begin
- pfInAsyncOp := FIsAsync;
- Result := S_OK;
- end;
- function TCustomDropSource.StartOperation(const pbcReserved: IBindCtx): HRESULT;
- begin
- if (FRequestAsync) then
- begin
- FIsAsync := True;
- Result := S_OK;
- end else
- Result := E_NOTIMPL;
- end;
- function TCustomDropSource.EndOperation(hResult: HRESULT;
- const pbcReserved: IBindCtx; dwEffects: Cardinal): HRESULT;
- var
- DropResult: TDragResult;
- begin
- if (FIsAsync) then
- begin
- FIsAsync := False;
- if (Assigned(FOnAfterDrop)) then
- begin
- if (Succeeded(hResult)) then
- DropResult := DropEffectToDragResult(dwEffects and DragTypesToDropEffect(FDragTypes))
- else
- DropResult := drUnknown;
- FOnAfterDrop(Self, DropResult,
- (DropResult <> drDropMove) or (PerformedDropEffect <> DROPEFFECT_MOVE));
- end;
- Result := S_OK;
- end else
- Result := E_FAIL;
- end;
- function TCustomDropSource.Execute: TDragResult;
- function GetRGBColor(Value: TColor): DWORD;
- begin
- Result := ColorToRGB(Value);
- case Result of
- clNone: Result := CLR_NONE;
- clDefault: Result := CLR_DEFAULT;
- end;
- end;
- var
- DropResult: HRESULT;
- AllowedEffects,
- DropEffect: longint;
- IsDraggingImage: boolean;
- shDragImage: TSHDRAGIMAGE;
- shDragBitmap: TBitmap;
- begin
- shDragBitmap := nil;
- AllowedEffects := DragTypesToDropEffect(FDragTypes);
- // Reset the "Performed Drop Effect" value. If it is supported by the target,
- // the target will set it to the desired value when the drop occurs.
- PerformedDropEffect := -1;
- if (FShowImage) then
- begin
- // Attempt to create Drag Drop helper object.
- // At present this is only supported on Windows 2000. If the object can't be
- // created, we fall back to the old image list based method (which only
- // works within the application).
- CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER,
- IDragSourceHelper, FDragSourceHelper);
- // Display drag image.
- if (FDragSourceHelper <> nil) then
- begin
- IsDraggingImage := True;
- shDragBitmap := TBitmap.Create;
- shDragBitmap.PixelFormat := pfDevice;
- FImages.GetBitmap(ImageIndex, shDragBitmap);
- shDragImage.hbmpDragImage := shDragBitmap.Handle;
- shDragImage.sizeDragImage.cx := shDragBitmap.Width;
- shDragImage.sizeDragImage.cy := shDragBitmap.Height;
- shDragImage.crColorKey := GetRGBColor(FImages.BkColor);
- shDragImage.ptOffset.x := ImageHotSpotX;
- shDragImage.ptOffset.y := ImageHotSpotY;
- if Failed(FDragSourceHelper.InitializeFromBitmap(shDragImage, Self)) then
- begin
- FDragSourceHelper := nil;
- shDragBitmap.Free;
- shDragBitmap := nil;
- end;
- end else
- IsDraggingImage := False;
- // Fall back to image list drag image if platform doesn't support
- // IDragSourceHelper or if we "just" failed to initialize properly.
- if (FDragSourceHelper = nil) then
- begin
- IsDraggingImage := ImageList_BeginDrag(FImages.Handle, FImageIndex,
- FImageHotSpot.X, FImageHotSpot.Y);
- end;
- end else
- IsDraggingImage := False;
- if (AllowAsyncTransfer) then
- SetAsyncMode(True);
- try
- InShellDragLoop := True;
- try
- DropResult := DoDragDrop(Self, Self, AllowedEffects, DropEffect);
- finally
- // InShellDragLoop is also reset in TCustomDropSource.QueryContinueDrag.
- // This is just to make absolutely sure that it is reset (actually no big
- // deal if it isn't).
- InShellDragLoop := False;
- end;
- finally
- if IsDraggingImage then
- begin
- if (FDragSourceHelper <> nil) then
- begin
- FDragSourceHelper := nil;
- shDragBitmap.Free;
- end else
- ImageList_EndDrag;
- end;
- end;
- case DropResult of
- DRAGDROP_S_DROP:
- (*
- ** Special handling of "optimized move".
- ** If PerformedDropEffect has been set by the target to DROPEFFECT_MOVE
- ** and the drop effect returned from DoDragDrop is different from
- ** DROPEFFECT_MOVE, then an optimized move was performed.
- ** Note: This is different from how MSDN states that an optimized move is
- ** signalled, but matches how Windows 2000 signals an optimized move.
- **
- ** On Windows 2000 an optimized move is signalled by:
- ** 1) Returning DRAGDROP_S_DROP from DoDragDrop.
- ** 2) Setting drop effect to DROPEFFECT_NONE.
- ** 3) Setting the "Performed Dropeffect" format to DROPEFFECT_MOVE.
- **
- ** On previous version of Windows, an optimized move is signalled by:
- ** 1) Returning DRAGDROP_S_DROP from DoDragDrop.
- ** 2) Setting drop effect to DROPEFFECT_MOVE.
- ** 3) Setting the "Performed Dropeffect" format to DROPEFFECT_NONE.
- **
- ** The documentation states that an optimized move is signalled by:
- ** 1) Returning DRAGDROP_S_DROP from DoDragDrop.
- ** 2) Setting drop effect to DROPEFFECT_NONE or DROPEFFECT_COPY.
- ** 3) Setting the "Performed Dropeffect" format to DROPEFFECT_NONE.
- *)
- if (LogicalPerformedDropEffect = DROPEFFECT_MOVE) or
- ((DropEffect <> DROPEFFECT_MOVE) and (PerformedDropEffect = DROPEFFECT_MOVE)) then
- Result := drDropMove
- else
- Result := DropEffectToDragResult(DropEffect and AllowedEffects);
- DRAGDROP_S_CANCEL:
- Result := drCancel;
- E_OUTOFMEMORY:
- Result := drOutMemory;
- else
- // This should never happen!
- Result := drUnknown;
- end;
- // Reset PerformedDropEffect if the target didn't set it.
- if (PerformedDropEffect = -1) then
- PerformedDropEffect := DROPEFFECT_NONE;
- // Fire OnAfterDrop event unless we are in the middle of an async data
- // transfer.
- if (not AsyncTransfer) and (Assigned(FOnAfterDrop)) then
- FOnAfterDrop(Self, Result,
- (Result = drDropMove) and
- ((DropEffect <> DROPEFFECT_MOVE) or (PerformedDropEffect <> DROPEFFECT_MOVE)));
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.GetPerformedDropEffect: longInt;
- begin
- Result := DROPEFFECT_NONE;
- end;
- function TCustomDropSource.GetLogicalPerformedDropEffect: longInt;
- begin
- Result := DROPEFFECT_NONE;
- end;
- procedure TCustomDropSource.SetPerformedDropEffect(const Value: longInt);
- begin
- // Not implemented in base class
- end;
- function TCustomDropSource.GetPreferredDropEffect: longInt;
- begin
- Result := DROPEFFECT_NONE;
- end;
- procedure TCustomDropSource.SetPreferredDropEffect(const Value: longInt);
- begin
- // Not implemented in base class
- end;
- function TCustomDropSource.GetInShellDragLoop: boolean;
- begin
- Result := False;
- end;
- function TCustomDropSource.GetTargetCLSID: TCLSID;
- begin
- Result := GUID_NULL;
- end;
- procedure TCustomDropSource.SetInShellDragLoop(const Value: boolean);
- begin
- // Not implemented in base class
- end;
- procedure TCustomDropSource.DataChanging(Sender: TObject);
- begin
- // Data is changing - Flush clipboard to freeze the contents
- FlushClipboard;
- end;
- procedure TCustomDropSource.FlushClipboard;
- begin
- // If we have live data on the clipboard...
- if (LiveDataOnClipboard) then
- // ...we force the clipboard to make a static copy of the data
- // before the data changes.
- OleCheck(OleFlushClipboard);
- end;
- procedure TCustomDropSource.EmptyClipboard;
- begin
- // If we have live data on the clipboard...
- if (LiveDataOnClipboard) then
- // ...we empty the clipboard.
- OleCheck(OleSetClipboard(nil));
- end;
- function TCustomDropSource.CutToClipboard: boolean;
- begin
- PreferredDropEffect := DROPEFFECT_MOVE;
- // Copy data to clipboard
- Result := CutOrCopyToClipboard;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.CopyToClipboard: boolean;
- begin
- PreferredDropEffect := DROPEFFECT_COPY;
- // Copy data to clipboard
- Result := CutOrCopyToClipboard;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.CutOrCopyToClipboard: boolean;
- begin
- Result := (OleSetClipboard(Self as IDataObject) = S_OK);
- end;
- procedure TCustomDropSource.DoOnPaste(Action: TDragResult; DeleteOnPaste: boolean);
- begin
- if (Assigned(FOnPaste)) then
- FOnPaste(Self, Action, DeleteOnPaste);
- end;
- function TCustomDropSource.GetLiveDataOnClipboard: boolean;
- begin
- Result := (OleIsCurrentClipboard(Self as IDataObject) = S_OK);
- end;
- // -----------------------------------------------------------------------------
- procedure TCustomDropSource.SetImages(const Value: TImageList);
- begin
- if (FImages = Value) then
- exit;
- FImages := Value;
- if (csLoading in ComponentState) then
- exit;
- { DONE -oanme : Shouldn't FShowImage and FImageIndex only be reset if FImages = nil? }
- if (FImages = nil) or (FImageIndex >= FImages.Count) then
- FImageIndex := 0;
- FShowImage := FShowImage and (FImages <> nil) and (FImages.Count > 0);
- end;
- // -----------------------------------------------------------------------------
- procedure TCustomDropSource.SetImageIndex(const Value: integer);
- begin
- if (csLoading in ComponentState) then
- begin
- FImageIndex := Value;
- exit;
- end;
- if (Value < 0) or (FImages.Count = 0) or (FImages = nil) then
- begin
- FImageIndex := 0;
- FShowImage := False;
- end else
- if (Value < FImages.Count) then
- FImageIndex := Value;
- end;
- // -----------------------------------------------------------------------------
- procedure TCustomDropSource.SetPoint(Index: integer; Value: integer);
- begin
- if (Index = 1) then
- FImageHotSpot.x := Value
- else
- FImageHotSpot.y := Value;
- end;
- // -----------------------------------------------------------------------------
- function TCustomDropSource.GetPoint(Index: integer): integer;
- begin
- if (Index = 1) then
- Result := FImageHotSpot.x
- else
- Result := FImageHotSpot.y;
- end;
- // -----------------------------------------------------------------------------
- procedure TCustomDropSource.SetShowImage(Value: boolean);
- begin
- FShowImage := Value;
- if (csLoading in ComponentState) then
- exit;
- if (FImages = nil) then
- FShowImage := False;
- end;
- // -----------------------------------------------------------------------------
- procedure TCustomDropSource.Notification(AComponent: TComponent;
- Operation: TOperation);
- begin
- inherited Notification(AComponent, Operation);
- if (Operation = opRemove) and (AComponent = FImages) then
- Images := nil;
- end;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // TEnumFormatEtc
- //
- ////////////////////////////////////////////////////////////////////////////////
- // Format enumerator used by TCustomDropMultiTarget.
- ////////////////////////////////////////////////////////////////////////////////
- type
- TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc)
- private
- FFormats : TClipboardFormats;
- FIndex : integer;
- protected
- constructor CreateClone(AFormats: TClipboardFormats; AIndex: Integer);
- public
- constructor Create(AFormats: TDataFormats; Direction: TDataDirection);
- { IEnumFormatEtc implentation }
- function Next(Celt: LongInt; out Elt; pCeltFetched: pLongInt): HRESULT; stdcall;
- function Skip(Celt: LongInt): HRESULT; stdcall;
- function Reset: HRESULT; stdcall;
- function Clone(out Enum: IEnumFormatEtc): HRESULT; stdcall;
- end;
- constructor TEnumFormatEtc.Create(AFormats: TDataFormats; Direction: TDataDirection);
- var
- i, j : integer;
- begin
- inherited Create;
- FFormats := TClipboardFormats.Create(nil, False);
- FIndex := 0;
- for i := 0 to AFormats.Count-1 do
- for j := 0 to AFormats[i].CompatibleFormats.Count-1 do
- if (Direction in AFormats[i].CompatibleFormats[j].DataDirections) and
- (not FFormats.Contain(TClipboardFormatClass(AFormats[i].CompatibleFormats[j].ClassType))) then
- FFormats.Add(AFormats[i].CompatibleFormats[j]);
- end;
- constructor TEnumFormatEtc.CreateClone(AFormats: TClipboardFormats; AIndex: Integer);
- var
- i : integer;
- begin
- inherited Create;
- FFormats := TClipboardFormats.Create(nil, False);
- FIndex := AIndex;
- for i := 0 to AFormats.Count-1 do
- FFormats.Add(AFormats[i]);
- end;
- function TEnumFormatEtc.Next(Celt: LongInt; out Elt;
- pCeltFetched: pLongInt): HRESULT;
- var
- i : integer;
- FormatEtc : PFormatEtc;
- begin
- i := 0;
- FormatEtc := PFormatEtc(@Elt);
- while (i < Celt) and (FIndex < FFormats.Count) do
- begin
- FormatEtc^ := FFormats[FIndex].FormatEtc;
- Inc(FormatEtc);
- Inc(i);
- Inc(FIndex);
- end;
- if (pCeltFetched <> nil) then
- pCeltFetched^ := i;
- if (i = Celt) then
- Result := S_OK
- else
- Result := S_FALSE;
- end;
- function TEnumFormatEtc.Skip(Celt: LongInt): HRESULT;
- begin
- if (FIndex + Celt <= FFormats.Count) then
- begin
- inc(FIndex, Celt);
- Result := S_OK;
- end else
- begin
- FIndex := FFormats.Count;
- Result := S_FALSE;
- end;
- end;
- function TEnumFormatEtc.Reset: HRESULT;
- begin
- FIndex := 0;
- Result := S_OK;
- end;
- function TEnumFormatEtc.Clone(out Enum: IEnumFormatEtc): HRESULT;
- begin
- Enum := TEnumFormatEtc.CreateClone(FFormats, FIndex);
- Result := S_OK;
- end;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // TCustomDropMultiSource
- //
- ////////////////////////////////////////////////////////////////////////////////
- type
- TSourceDataFormats = class(TDataFormats)
- public
- function Add(DataFormat: TCustomDataFormat): integer; override;
- end;
- function TSourceDataFormats.Add(DataFormat: TCustomDataFormat): integer;
- begin
- Result := inherited Add(DataFormat);
- // Set up change notification so drop source can flush clipboard if data changes.
- DataFormat.OnChanging := TCustomDropMultiSource(DataFormat.Owner).DataChanging;
- end;
- constructor TCustomDropMultiSource.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FDataFormats := TSourceDataFormats.Create;
- FFeedbackDataFormat := TFeedbackDataFormat.Create(Self);
- FRawDataFormat := TRawDataFormat.Create(Self);
- end;
- destructor TCustomDropMultiSource.Destroy;
- var
- i : integer;
- begin
- EmptyClipboard;
- // Delete all target formats owned by the object
- for i := FDataFormats.Count-1 downto 0 do
- FDataFormats[i].Free;
- FDataFormats.Free;
- inherited Destroy;
- end;
- function TCustomDropMultiSource.DoGetData(const FormatEtcIn: TFormatEtc;
- out Medium: TStgMedium): HRESULT;
- var
- i, j: integer;
- DF: TCustomDataFormat;
- CF: TClipboardFormat;
- begin
- // TODO : Add support for delayed rendering with OnRenderData event.
- Medium.tymed := 0;
- Medium.UnkForRelease := nil;
- Medium.hGlobal := 0;
- Result := DV_E_FORMATETC;
- (*
- ** Loop through all data formats associated with this drop source to find one
- ** which can offer the clipboard format requested by the target.
- *)
- for i := 0 to DataFormats.Count-1 do
- begin
- DF := DataFormats[i];
- // Ignore empty data formats.
- if (not DF.HasData) then
- continue;
- (*
- ** Loop through all the data format's supported clipboard formats to find
- ** one which contains data and can provide it in the format requested by the
- ** target.
- *)
- for j := 0 to DF.CompatibleFormats.Count-1 do
- begin
- CF := DF.CompatibleFormats[j];
- (*
- ** 1) Determine if the clipboard format supports the format requested by
- ** the target.
- ** 2) Transfer data from the data format object to the clipboard format
- ** object.
- ** 3) Determine if the clipboard format object now has data to offer.
- ** 4) Transfer the data from the clipboard format object to the medium.
- *)
- if (CF.AcceptFormat(FormatEtcIn)) and
- (DataFormats[i].AssignTo(CF)) and
- (CF.HasData) and
- (CF.SetDataToMedium(FormatEtcIn, Medium)) then
- begin
- // Once data has been sucessfully transfered to the medium, we clear
- // the data in the TClipboardFormat object in order to conserve
- // resources.
- CF.Clear;
- Result := S_OK;
- exit;
- end;
- end;
- end;
- end;
- function TCustomDropMultiSource.DoSetData(const FormatEtc: TFormatEtc;
- var Medium: TStgMedium): HRESULT;
- var
- i, j : integer;
- GenericClipboardFormat: TRawClipboardFormat;
- begin
- Result := E_NOTIMPL;
- // Get data for requested source format.
- for i := 0 to DataFormats.Count-1 do
- for j := 0 to DataFormats[i].CompatibleFormats.Count-1 do
- if (DataFormats[i].CompatibleFormats[j].AcceptFormat(FormatEtc)) and
- (DataFormats[i].CompatibleFormats[j].GetDataFromMedium(Self, Medium)) and
- (DataFormats[i].Assign(DataFormats[i].CompatibleFormats[j])) then
- begin
- DoOnSetData(DataFormats[i], DataFormats[i].CompatibleFormats[j]);
- // Once data has been sucessfully transfered to the medium, we clear
- // the data in the TClipboardFormat object in order to conserve
- // resources.
- DataFormats[i].CompatibleFormats[j].Clear;
- Result := S_OK;
- exit;
- end;
- // The requested data format wasn't supported by any of the registered
- // clipboard formats, but in order to support the Windows 2000 drag drop helper
- // object we have to accept any data which is written to the IDataObject.
- // To do this we create a new clipboard format object, initialize it with the
- // format information passed to us and copy the data.
- GenericClipboardFormat := TRawClipboardFormat.CreateFormatEtc(FormatEtc);
- FRawDataFormat.CompatibleFormats.Add(GenericClipboardFormat);
- if (GenericClipboardFormat.GetDataFromMedium(Self, Medium)) and
- (FRawDataFormat.Assign(GenericClipboardFormat)) then
- Result := S_OK;
- end;
- function TCustomDropMultiSource.GetEnumFormatEtc(dwDirection: Integer): IEnumFormatEtc;
- begin
- if (dwDirection = DATADIR_GET) then
- Result := TEnumFormatEtc.Create(FDataFormats, ddRead)
- else if (dwDirection = DATADIR_SET) then
- Result := TEnumFormatEtc.Create(FDataFormats, ddWrite)
- else
- Result := nil;
- end;
- function TCustomDropMultiSource.HasFormat(const FormatEtc: TFormatEtc): boolean;
- var
- i ,
- j : integer;
- begin
- Result := False;
- for i := 0 to DataFormats.Count-1 do
- for j := 0 to DataFormats[i].CompatibleFormats.Count-1 do
- if (DataFormats[i].CompatibleFormats[j].AcceptFormat(FormatEtc)) then
- begin
- Result := True;
- exit;
- end;
- end;
- function TCustomDropMultiSource.GetPerformedDropEffect: longInt;
- begin
- Result := FFeedbackDataFormat.PerformedDropEffect;
- end;
- function TCustomDropMultiSource.GetLogicalPerformedDropEffect: longInt;
- begin
- Result := FFeedbackDataFormat.LogicalPerformedDropEffect;
- end;
- function TCustomDropMultiSource.GetPreferredDropEffect: longInt;
- begin
- Result := FFeedbackDataFormat.PreferredDropEffect;
- end;
- procedure TCustomDropMultiSource.SetPerformedDropEffect(const Value: longInt);
- begin
- FFeedbackDataFormat.PerformedDropEffect := Value;
- end;
- procedure TCustomDropMultiSource.SetPreferredDropEffect(const Value: longInt);
- begin
- FFeedbackDataFormat.PreferredDropEffect := Value;
- end;
- function TCustomDropMultiSource.GetInShellDragLoop: boolean;
- begin
- Result := FFeedbackDataFormat.InShellDragLoop;
- end;
- procedure TCustomDropMultiSource.SetInShellDragLoop(const Value: boolean);
- begin
- FFeedbackDataFormat.InShellDragLoop := Value;
- end;
- function TCustomDropMultiSource.GetTargetCLSID: TCLSID;
- begin
- Result := FFeedbackDataFormat.TargetCLSID;
- end;
- procedure TCustomDropMultiSource.DoOnSetData(DataFormat: TCustomDataFormat;
- ClipboardFormat: TClipboardFormat);
- var
- DropEffect : longInt;
- begin
- if (ClipboardFormat is TPasteSuccededClipboardFormat) then
- begin
- DropEffect := TPasteSuccededClipboardFormat(ClipboardFormat).Value;
- DoOnPaste(DropEffectToDragResult(DropEffect),
- (DropEffect = DROPEFFECT_MOVE) and (PerformedDropEffect = DROPEFFECT_MOVE));
- end;
- end;
- ////////////////////////////////////////////////////////////////////////////////
- //
- // TDropSourceThread
- //
- ////////////////////////////////////////////////////////////////////////////////
- constructor TDropSourceThread.Create(ADropSource: TCustomDropSource;
- AFreeOnTerminate: Boolean);
- begin
- inherited Create(True);
- FreeOnTerminate := AFreeOnTerminate;
- FDropSource := ADropSource;
- FDragResult := drAsync;
- end;
- procedure TDropSourceThread.Execute;
- var
- pt: TPoint;
- hwndAttach: HWND;
- dwAttachThreadID, dwCurrentThreadID : DWORD;
- begin
- (*
- ** See Microsoft Knowledgebase Article Q139408 for an explanation of the
- ** AttachThreadInput stuff.
- ** http://support.microsoft.com/support/kb/articles/Q139/4/08.asp
- *)
- // Get handle of window under mouse-cursor.
- GetCursorPos(pt);
- hwndAttach := WindowFromPoint(pt);
- ASSERT(hwndAttach<>0, 'Can''t find window with drag-object');
- // Get thread IDs.
- dwAttachThreadID := GetWindowThreadProcessId(hwndAttach, nil);
- dwCurrentThreadID := GetCurrentThreadId();
- // Attach input queues if necessary.
- if (dwAttachThreadID <> dwCurrentThreadID) then
- AttachThreadInput(dwAttachThreadID, dwCurrentThreadID, True);
- try
- // Initialize OLE for this thread.
- OleInitialize(nil);
- try
- // Start drag & drop.
- FDragResult := FDropSource.Execute;
- finally
- OleUninitialize;
- end;
- finally
- // Restore input queue settings.
- if (dwAttachThreadID <> dwCurrentThreadID) then
- AttachThreadInput(dwAttachThreadID, dwCurrentThreadID, False);
- // Set Terminated flag so owner knows that drag has finished.
- Terminate;
- end;
- end;
- end.
|