Subversion Repositories decoder

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
unit DragDrop;
2
// -----------------------------------------------------------------------------
3
// Project:         Drag and Drop Component Suite
4
// Module:          DragDrop
5
// Description:     Implements base classes and utility functions.
6
// Version:         4.0
7
// Date:            18-MAY-2001
8
// Target:          Win32, Delphi 5-6
9
// Authors:         Anders Melander, anders@melander.dk, http://www.melander.dk
10
// Copyright        © 1997-2001 Angus Johnson & Anders Melander
11
// -----------------------------------------------------------------------------
12
// TODO -oanme -cPortability : Replace all public use of HWND with THandle. BCB's HWND <> Delphi's HWND.
13
{$include DragDrop.inc}
14
 
15
interface
16
 
17
uses
18
  Classes,
19
  Windows,
20
  ActiveX;
21
 
22
{$IFDEF BCB}
23
{$HPPEMIT '#ifndef NO_WIN32_LEAN_AND_MEAN'}
24
{$HPPEMIT '"Error: The NO_WIN32_LEAN_AND_MEAN symbol must be defined in your projects conditional defines"'}
25
{$HPPEMIT '#endif'}
26
{$ENDIF}
27
 
28
 
29
const
30
  DROPEFFECT_NONE   = ActiveX.DROPEFFECT_NONE;
31
  DROPEFFECT_COPY   = ActiveX.DROPEFFECT_COPY;
32
  DROPEFFECT_MOVE   = ActiveX.DROPEFFECT_MOVE;
33
  DROPEFFECT_LINK   = ActiveX.DROPEFFECT_LINK;
34
  DROPEFFECT_SCROLL = ActiveX.DROPEFFECT_SCROLL;
35
 
36
type
37
  // TDragType enumerates the three possible drag/drop operations.
38
  TDragType = (dtCopy, dtMove, dtLink);
39
  TDragTypes = set of TDragType;
40
 
41
type
42
  // TDataDirection is used by the clipboard format registration to specify
43
  // if the clipboard format should be listed in get (read) format enumerations,
44
  // set (write) format enumerations or both.
45
  // ddRead : Destination (IDropTarget) can read data from IDataObject.
46
  // ddWrite : Destination (IDropTarget) can write data to IDataObject.
47
  TDataDirection = (ddRead, ddWrite);
48
  TDataDirections = set of TDataDirection;
49
 
50
const
51
  ddReadWrite = [ddRead, ddWrite];
52
 
53
type
54
  // TConversionScope is used by the clipboard format registration to specify
55
  // if a clipboard format conversion is supported by the drop source, the drop
56
  // target or both.
57
  // ddSource : Conversion is valid for drop source (IDropSource).
58
  // ddTarget : Conversion is valid for drop target (IDropTarget).
59
  TConversionScope = (csSource, csTarget);
60
  TConversionScopes = set of TConversionScope;
61
 
62
const
63
  csSourceTarget = [csSource, csTarget];
64
 
65
// C++ Builder's declaration of IEnumFORMATETC is incorrect, so we must generate
66
// the typedef for C++ Builder.
67
{$IFDEF BCB}
68
{$HPPEMIT 'typedef System::DelphiInterface<IEnumFORMATETC> _di_IEnumFORMATETC;' }
69
{$ENDIF}
70
 
71
////////////////////////////////////////////////////////////////////////////////
72
//
73
//              TInterfacedComponent
74
//
75
////////////////////////////////////////////////////////////////////////////////
76
// Top level base class for the drag/drop component hierachy.
77
// Implements the IUnknown interface.
78
// Corresponds to TInterfacedObject (see VCL on-line help), but descends from
79
// TComponent instead of TObject.
80
// Reference counting is disabled (_AddRef and _Release methods does nothing)
81
// since the component life span is controlled by the component owner.
82
////////////////////////////////////////////////////////////////////////////////
83
type
84
  TInterfacedComponent = class(TComponent, IUnknown)
85
  protected
86
    function QueryInterface(const IID: TGuid; out Obj): HRESULT;
87
      {$IFDEF VER13_PLUS} override; {$ELSE}
88
      {$IFDEF VER12_PLUS} reintroduce; {$ENDIF}{$ENDIF} stdcall;
89
    function _AddRef: Integer; stdcall;
90
    function _Release: Integer; stdcall;
91
  end;
92
 
93
////////////////////////////////////////////////////////////////////////////////
94
//
95
//              TClipboardFormat
96
//
97
////////////////////////////////////////////////////////////////////////////////
98
// Abstract base class. Extracts or injects data of a specific low level format
99
// from or to an IDataObject.
100
////////////////////////////////////////////////////////////////////////////////
101
type
102
  TCustomDataFormat = class;
103
 
104
  TClipboardFormat = class(TObject)
105
  private
106
    FDataDirections: TDataDirections;
107
    FDataFormat: TCustomDataFormat;
108
  protected
109
    FFormatEtc: TFormatEtc;
110
    constructor CreateFormat(Atymed: Longint); virtual;
111
    constructor CreateFormatEtc(const AFormatEtc: TFormatEtc); virtual;
112
    { Extracts data from the specified medium }
113
    function DoGetData(ADataObject: IDataObject; const AMedium: TStgMedium): boolean; virtual;
114
    { Transfer data to the specified medium }
115
    function DoSetData(const FormatEtcIn: TFormatEtc;
116
      var AMedium: TStgMedium): boolean; virtual;
117
    function GetClipboardFormat: TClipFormat; virtual;
118
    procedure SetClipboardFormat(Value: TClipFormat); virtual;
119
    function GetClipboardFormatName: string; virtual;
120
    procedure SetClipboardFormatName(const Value: string); virtual;
121
    procedure SetFormatEtc(const Value: TFormatEtc);
122
  public
123
    constructor Create; virtual; abstract;
124
    destructor Destroy; override;
125
    { Determines if the object can read from the specified data object }
126
    function HasValidFormats(ADataObject: IDataObject): boolean; virtual;
127
    { Determines if the object can read the specified format }
128
    function AcceptFormat(const AFormatEtc: TFormatEtc): boolean; virtual;
129
    { Extracts data from the specified IDataObject }
130
    function GetData(ADataObject: IDataObject): boolean; virtual;
131
    { Extracts data from the specified IDataObject via the specified medium }
132
    function GetDataFromMedium(ADataObject: IDataObject;
133
      var AMedium: TStgMedium): boolean; virtual;
134
    { Transfers data to the specified IDataObject }
135
    function SetData(ADataObject: IDataObject; const FormatEtcIn: TFormatEtc;
136
      var AMedium: TStgMedium): boolean; virtual;
137
    { Transfers data to the specified medium }
138
    function SetDataToMedium(const FormatEtcIn: TFormatEtc;
139
      var AMedium: TStgMedium): boolean;
140
    { Copies data from the specified source format to the object }
141
    function Assign(Source: TCustomDataFormat): boolean; virtual;
142
    { Copies data from the object to the specified target format }
143
    function AssignTo(Dest: TCustomDataFormat): boolean; virtual;
144
    { Clears the objects data }
145
    procedure Clear; virtual; abstract;
146
    { Returns true if object can supply data }
147
    function HasData: boolean; virtual;
148
    { Unregisters the clipboard format and all mappings involving it from the global database }
149
    class procedure UnregisterClipboardFormat;
150
    { Returns the clipboard format value }
151
    property ClipboardFormat: TClipFormat read GetClipboardFormat
152
      write SetClipboardFormat;
153
    { Returns the clipboard format name }
154
    property ClipboardFormatName: string read GetClipboardFormatName
155
      write SetClipboardFormatName;
156
    { Provides access to the objects format specification }
157
    property FormatEtc: TFormatEtc read FFormatEtc;
158
    { Specifies whether the format can read and write data }
159
    property DataDirections: TDataDirections read FDataDirections
160
      write FDataDirections;
161
    { Specifies the data format which owns and controls this clipboard format }
162
    property DataFormat: TCustomDataFormat read FDataFormat write FDataFormat;
163
  end;
164
 
165
  TClipboardFormatClass = class of TClipboardFormat;
166
 
167
  // TClipboardFormats
168
  // List of TClipboardFormat objects.
169
  TClipboardFormats = class(TObject)
170
  private
171
    FList: TList;
172
    FOwnsObjects: boolean;
173
    FDataFormat: TCustomDataFormat;
174
  protected
175
    function GetFormat(Index: integer): TClipboardFormat;
176
    function GetCount: integer;
177
  public
178
    constructor Create(ADataFormat: TCustomDataFormat; AOwnsObjects: boolean);
179
    destructor Destroy; override;
180
    procedure Clear;
181
    function Add(ClipboardFormat: TClipboardFormat): integer;
182
    function Contain(ClipboardFormatClass: TClipboardFormatClass): boolean;
183
    function FindFormat(ClipboardFormatClass: TClipboardFormatClass): TClipboardFormat;
184
    property Formats[Index: integer]: TClipboardFormat read GetFormat; default;
185
    property Count: integer read GetCount;
186
    property DataFormat: TCustomDataFormat read FDataFormat;
187
  end;
188
 
189
////////////////////////////////////////////////////////////////////////////////
190
//
191
//              TDragDropComponent
192
//
193
////////////////////////////////////////////////////////////////////////////////
194
// Base class for drag/drop components.
195
////////////////////////////////////////////////////////////////////////////////
196
  TDataFormats = class;
197
 
198
  TDragDropComponent = class(TInterfacedComponent)
199
  private
200
  protected
201
    FDataFormats: TDataFormats;
202
    //: Only used by TCustomDropMultiSource and TCustomDropMultiTarget and
203
    // their descendants.
204
    property DataFormats: TDataFormats read FDataFormats;
205
  public
206
  end;
207
 
208
////////////////////////////////////////////////////////////////////////////////
209
//
210
//              TCustomFormat
211
//
212
////////////////////////////////////////////////////////////////////////////////
213
// Abstract base class.
214
// Renders the data of one or more TClipboardFormat objects to or from a
215
// specific high level data format.
216
////////////////////////////////////////////////////////////////////////////////
217
  TCustomDataFormat = class(TObject)
218
  private
219
    FCompatibleFormats  : TClipboardFormats;
220
    FFormatList         : TDataFormats;
221
    FOwner              : TDragDropComponent;
222
    FOnChanging         : TNotifyEvent;
223
  protected
224
    { Determines if the object can accept data from the specified source format }
225
    function SupportsFormat(ClipboardFormat: TClipboardFormat): boolean;
226
    procedure DoOnChanging(Sender: TObject);
227
    procedure Changing; virtual;
228
    property FormatList: TDataFormats read FFormatList;
229
  public
230
    constructor Create(AOwner: TDragDropComponent); virtual;
231
    destructor Destroy; override;
232
    procedure Clear; virtual; abstract;
233
    { Copies data between the specified clipboard format to the object }
234
    function Assign(Source: TClipboardFormat): boolean; virtual;
235
    function AssignTo(Dest: TClipboardFormat): boolean; virtual;
236
    { Extracts data from the specified IDataObject }
237
    function GetData(DataObject: IDataObject): boolean; virtual;
238
    { Determines if the object contains *any* data }
239
    function HasData: boolean; virtual; abstract;
240
    { Determines if the object needs/can use *more* data }
241
    function NeedsData: boolean; virtual;
242
    { Determines if the object can read from the specified data object }
243
    function HasValidFormats(ADataObject: IDataObject): boolean; virtual;
244
    { Determines if the object can read the specified format }
245
    function AcceptFormat(const FormatEtc: TFormatEtc): boolean; virtual;
246
    { Registers the data format in the data format list }
247
    class procedure RegisterDataFormat;
248
    { Registers the specified clipboard format as being compatible with the data format }
249
    class procedure RegisterCompatibleFormat(ClipboardFormatClass: TClipboardFormatClass;
250
      Priority: integer {$ifdef VER12_PLUS} = 0 {$endif};
251
      ConversionScopes: TConversionScopes {$ifdef VER12_PLUS} = csSourceTarget {$endif};
252
      DataDirections: TDataDirections {$ifdef VER12_PLUS} = [ddRead] {$endif});
253
    { Unregisters the specified clipboard format from the compatibility list }
254
    class procedure UnregisterCompatibleFormat(ClipboardFormatClass: TClipboardFormatClass);
255
    { Unregisters data format and all mappings involving it from the global database }
256
    class procedure UnregisterDataFormat;
257
    { List of compatible source formats }
258
    property CompatibleFormats: TClipboardFormats read FCompatibleFormats;
259
    property Owner: TDragDropComponent read FOwner;
260
    property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
261
    // TODO : Add support for delayed rendering with DelayedRender property.
262
  end;
263
 
264
  // TDataFormats
265
  // List of TCustomDataFormat objects.
266
  TDataFormats = class(TObject)
267
  private
268
    FList: TList;
269
  protected
270
    function GetFormat(Index: integer): TCustomDataFormat;
271
    function GetCount: integer;
272
  public
273
    constructor Create;
274
    destructor Destroy; override;
275
    function Add(DataFormat: TCustomDataFormat): integer; virtual;
276
    function IndexOf(DataFormat: TCustomDataFormat): integer; virtual;
277
    procedure Remove(DataFormat: TCustomDataFormat); virtual;
278
    property Formats[Index: integer]: TCustomDataFormat read GetFormat; default;
279
    property Count: integer read GetCount;
280
  end;
281
 
282
  // TDataFormatClasses
283
  // List of TCustomDataFormat classes.
284
  TDataFormatClass = class of TCustomDataFormat;
285
 
286
  TDataFormatClasses = class(TObject)
287
  private
288
    FList: TList;
289
  protected
290
    function GetFormat(Index: integer): TDataFormatClass;
291
    function GetCount: integer;
292
    { Provides singleton access to the global data format database }
293
    class function Instance: TDataFormatClasses;
294
  public
295
    constructor Create;
296
    destructor Destroy; override;
297
    function Add(DataFormat: TDataFormatClass): integer; virtual;
298
    procedure Remove(DataFormat: TDataFormatClass); virtual;
299
    property Formats[Index: integer]: TDataFormatClass read GetFormat; default;
300
    property Count: integer read GetCount;
301
  end;
302
 
303
  // TDataFormatMap
304
  // Format conversion database. Contains mappings between TClipboardFormat
305
  // and TCustomDataFormat.
306
  // Used internally by TCustomDropMultiTarget and TCustomDropMultiSource.
307
  TDataFormatMap = class(TObject)
308
    FList: TList;
309
  protected
310
    function FindMap(DataFormatClass: TDataFormatClass; ClipboardFormatClass: TClipboardFormatClass): integer;
311
    procedure Sort;
312
    { Provides singleton access to the global format map database }
313
    class function Instance: TDataFormatMap;
314
  public
315
    constructor Create;
316
    destructor Destroy; override;
317
    procedure Add(DataFormatClass: TDataFormatClass;
318
      ClipboardFormatClass: TClipboardFormatClass;
319
      Priority: integer {$ifdef VER12_PLUS} = 0 {$endif};
320
      ConversionScopes: TConversionScopes {$ifdef VER12_PLUS} = csSourceTarget {$endif};
321
      DataDirections: TDataDirections {$ifdef VER12_PLUS} = [ddRead] {$endif});
322
    procedure Delete(DataFormatClass: TDataFormatClass;
323
      ClipboardFormatClass: TClipboardFormatClass);
324
    procedure DeleteByClipboardFormat(ClipboardFormatClass: TClipboardFormatClass);
325
    procedure DeleteByDataFormat(DataFormatClass: TDataFormatClass);
326
    procedure GetSourceByDataFormat(DataFormatClass: TDataFormatClass;
327
      ClipboardFormats: TClipboardFormats; ConversionScope: TConversionScope);
328
    function CanMap(DataFormatClass: TDataFormatClass;
329
      ClipboardFormatClass: TClipboardFormatClass): boolean;
330
 
331
    { Registers the specified format mapping }
332
    procedure RegisterFormatMap(DataFormatClass: TDataFormatClass;
333
      ClipboardFormatClass: TClipboardFormatClass;
334
      Priority: integer {$ifdef VER12_PLUS} = 0 {$endif};
335
      ConversionScopes: TConversionScopes {$ifdef VER12_PLUS} = csSourceTarget {$endif};
336
      DataDirections: TDataDirections {$ifdef VER12_PLUS} = [ddRead] {$endif});
337
    { Unregisters the specified format mapping }
338
    procedure UnregisterFormatMap(DataFormatClass: TDataFormatClass;
339
      ClipboardFormatClass: TClipboardFormatClass);
340
  end;
341
 
342
////////////////////////////////////////////////////////////////////////////////
343
//
344
//              TDataFormatAdapter
345
//
346
////////////////////////////////////////////////////////////////////////////////
347
// Helper component used to add additional data formats to a drop source or
348
// target at design time.
349
// Requires that data formats have been registered with
350
// TCustomDataFormat.RegisterDataFormat.
351
////////////////////////////////////////////////////////////////////////////////
352
  TDataFormatAdapter = class(TComponent)
353
  private
354
    FDragDropComponent: TDragDropComponent;
355
    FDataFormat: TCustomDataFormat;
356
    FDataFormatClass: TDataFormatClass;
357
    FEnabled: boolean;
358
    function GetDataFormatName: string;
359
    procedure SetDataFormatName(const Value: string);
360
  protected
361
    procedure SetDataFormatClass(const Value: TDataFormatClass);
362
    procedure SetDragDropComponent(const Value: TDragDropComponent);
363
    function GetEnabled: boolean;
364
    procedure SetEnabled(const Value: boolean);
365
    procedure Notification(AComponent: TComponent;
366
      Operation: TOperation); override;
367
    procedure Loaded; override;
368
  public
369
    destructor Destroy; override;
370
    property DataFormatClass: TDataFormatClass read FDataFormatClass
371
      write SetDataFormatClass;
372
    property DataFormat: TCustomDataFormat read FDataFormat;
373
  published
374
    property DragDropComponent: TDragDropComponent read FDragDropComponent
375
      write SetDragDropComponent;
376
    property DataFormatName: string read GetDataFormatName
377
      write SetDataFormatName;
378
    property Enabled: boolean read GetEnabled write SetEnabled;
379
  end;
380
 
381
////////////////////////////////////////////////////////////////////////////////
382
//
383
//              Drag Drop helper interfaces
384
//
385
////////////////////////////////////////////////////////////////////////////////
386
// Requires Windows 2000 or later.
387
////////////////////////////////////////////////////////////////////////////////
388
type
389
  PSHDRAGIMAGE = ^TSHDRAGIMAGE;
390
  {_$EXTERNALSYM _SHDRAGIMAGE}
391
  _SHDRAGIMAGE = packed record
392
    sizeDragImage: TSize;               { The length and Width of the rendered image }
393
    ptOffset: TPoint;                   { The Offset from the mouse cursor to the upper left corner of the image }
394
    hbmpDragImage: HBitmap;             { The Bitmap containing the rendered drag images }
395
    crColorKey: COLORREF;               { The COLORREF that has been blitted to the background of the images }
396
  end;
397
  TSHDRAGIMAGE = _SHDRAGIMAGE;
398
  {_$EXTERNALSYM SHDRAGIMAGE}
399
  SHDRAGIMAGE = _SHDRAGIMAGE;
400
 
401
const
402
  CLSID_DragDropHelper: TGUID = (
403
    D1:$4657278a; D2:$411b; D3:$11d2; D4:($83,$9a,$00,$c0,$4f,$d9,$18,$d0));
404
  SID_DragDropHelper = '{4657278A-411B-11d2-839A-00C04FD918D0}';
405
 
406
const
407
  IID_IDropTargetHelper: TGUID = (
408
    D1:$4657278b; D2:$411b; D3:$11d2; D4:($83,$9a,$00,$c0,$4f,$d9,$18,$d0));
409
  SID_IDropTargetHelper = '{4657278B-411B-11d2-839A-00C04FD918D0}';
410
 
411
type
412
  {_$HPPEMIT 'typedef DragDrop::DelphiInterface<IDropTargetHelper> _di_IDropTargetHelper;'}
413
  {_$EXTERNALSYM IDropTargetHelper}
414
  IDropTargetHelper = interface(IUnknown)
415
    [SID_IDropTargetHelper]
416
    function DragEnter(hwndTarget: HWND; const DataObj: IDataObject;
417
      var pt: TPoint; dwEffect: Longint): HResult; stdcall;
418
    function DragLeave: HResult; stdcall;
419
    function DragOver(var pt: TPoint; dwEffect: longInt): HResult; stdcall;
420
    function Drop(const DataObj: IDataObject; var pt: TPoint;
421
      dwEffect: longInt): HResult; stdcall;
422
    function Show(Show: BOOL): HResult; stdcall;
423
  end;
424
 
425
const
426
  IID_IDragSourceHelper: TGUID = (
427
    D1:$de5bf786; D2:$477a; D3:$11d2; D4:($83,$9d,$00,$c0,$4f,$d9,$18,$d0));
428
  SID_IDragSourceHelper = '{DE5BF786-477A-11d2-839D-00C04FD918D0}';
429
 
430
type
431
  {_$HPPEMIT 'typedef DragDrop::DelphiInterface<IDragSourceHelper> _di_IDragSourceHelper;'}
432
  {_$EXTERNALSYM IDragSourceHelper}
433
  IDragSourceHelper = interface(IUnknown)
434
    [SID_IDragSourceHelper]
435
    function InitializeFromBitmap(var shdi: TSHDRAGIMAGE;
436
      const DataObj: IDataObject): HResult; stdcall;
437
    function InitializeFromWindow(hwnd: HWND; var pt: TPoint;
438
      const DataObj: IDataObject): HResult; stdcall;
439
  end;
440
 
441
////////////////////////////////////////////////////////////////////////////////
442
//
443
//              Async data transfer interfaces
444
//
445
////////////////////////////////////////////////////////////////////////////////
446
// Requires Windows 2000 or later.
447
////////////////////////////////////////////////////////////////////////////////
448
const
449
  IID_IAsyncOperation: TGUID = (
450
    D1:$3D8B0590; D2:$F691; D3:$11D2; D4:($8E,$A9,$00,$60,$97,$DF,$5B,$D4));
451
  SID_IAsyncOperation = '{3D8B0590-F691-11D2-8EA9-006097DF5BD4}';
452
 
453
type
454
  {_$HPPEMIT 'typedef DragDrop::DelphiInterface<IAsyncOperation> _di_IAsyncOperation;'}
455
  {_$EXTERNALSYM IAsyncOperation}
456
  IAsyncOperation = interface(IUnknown)
457
    [SID_IAsyncOperation]
458
    function SetAsyncMode(fDoOpAsync: BOOL): HResult; stdcall;
459
    function GetAsyncMode(out fDoOpAsync: BOOL): HResult; stdcall;
460
    function StartOperation(const pbcReserved: IBindCtx): HResult; stdcall;
461
    function InOperation(out pfInAsyncOp: BOOL): HResult; stdcall;
462
    function EndOperation(hResult: HRESULT; const pbcReserved: IBindCtx;
463
      dwEffects: DWORD): HResult; stdcall;
464
  end;
465
 
466
////////////////////////////////////////////////////////////////////////////////
467
//
468
//              TRawClipboardFormat & TRawDataFormat
469
//
470
////////////////////////////////////////////////////////////////////////////////
471
// These clipboard and data format classes are special in that they don't
472
// interpret the data in any way.
473
// Their primary purpose is to enable the TCustomDropMultiSource class to accept
474
// and store arbitrary (and unknown) data types. This is a requirement for
475
// drag drop helper object support.
476
////////////////////////////////////////////////////////////////////////////////
477
// The TRawDataFormat class does not perform any storage of data itself. Instead
478
// it relies on the TRawClipboardFormat objects to store data.
479
////////////////////////////////////////////////////////////////////////////////
480
  TRawDataFormat = class(TCustomDataFormat)
481
  private
482
    FMedium: TStgMedium;
483
  protected
484
  public
485
    procedure Clear; override;
486
    function HasData: boolean; override;
487
    function NeedsData: boolean; override;
488
    property Medium: TStgMedium read FMedium write FMedium;
489
  end;
490
 
491
  TRawClipboardFormat = class(TClipboardFormat)
492
  private
493
    FMedium: TStgMedium;
494
  protected
495
    function DoGetData(ADataObject: IDataObject;
496
      const AMedium: TStgMedium): boolean; override;
497
    function DoSetData(const FormatEtcIn: TFormatEtc;
498
      var AMedium: TStgMedium): boolean; override;
499
    procedure SetClipboardFormatName(const Value: string); override;
500
    function GetClipboardFormat: TClipFormat; override;
501
    function GetString: string;
502
    procedure SetString(const Value: string);
503
  public
504
    constructor Create; override;
505
    constructor CreateFormatEtc(const AFormatEtc: TFormatEtc); override;
506
    function Assign(Source: TCustomDataFormat): boolean; override;
507
    function AssignTo(Dest: TCustomDataFormat): boolean; override;
508
    procedure Clear; override;
509
    // Methods to handle the corresponding TRawDataFormat functioinality.
510
    procedure ClearData;
511
    function HasData: boolean; override;
512
    function NeedsData: boolean;
513
 
514
    // All of these should be moved/mirrored in TRawDataFormat:
515
    procedure CopyFromStgMedium(const AMedium: TStgMedium);
516
    procedure CopyToStgMedium(var AMedium: TStgMedium);
517
    property AsString: string read GetString write SetString;
518
    property Medium: TStgMedium read FMedium write FMedium;
519
  end;
520
 
521
////////////////////////////////////////////////////////////////////////////////
522
//
523
//              Utility functions
524
//
525
////////////////////////////////////////////////////////////////////////////////
526
function DropEffectToDragType(DropEffect: longInt; var DragType: TDragType): boolean;
527
function DragTypesToDropEffect(DragTypes: TDragTypes): longint; // V4: New
528
 
529
// Coordinate space conversion.
530
function ClientPtToWindowPt(Handle: THandle; pt: TPoint): TPoint;
531
 
532
// Replacement for KeysToShiftState.
533
function KeysToShiftStatePlus(Keys: Word): TShiftState; // V4: New
534
function ShiftStateToDropEffect(Shift: TShiftState; AllowedEffects: longint;
535
  Fallback: boolean): longint;
536
 
537
// Replacement for the buggy DragDetect API function.
538
function DragDetectPlus(Handle: THandle; p: TPoint): boolean; // V4: New
539
 
540
// Wrapper for urlmon.CopyStgMedium.
541
// Note: Only works with IE4 or later installed.
542
function CopyStgMedium(const SrcMedium: TStgMedium; var DstMedium: TStgMedium): boolean;
543
 
544
// Get the name of a clipboard format as a Delphi string.
545
function GetClipboardFormatNameStr(Value: TClipFormat): string;
546
 
547
// Raise last Windows API error as an exception.
548
procedure _RaiseLastWin32Error;
549
 
550
////////////////////////////////////////////////////////////////////////////////
551
//
552
//              Global variables
553
//
554
////////////////////////////////////////////////////////////////////////////////
555
var
556
  ShellMalloc: IMalloc;
557
 
558
// Name of the IDE component palette page the drag drop components are
559
// registered to
560
var
561
  DragDropComponentPalettePage: string = 'DragDrop';
562
 
563
////////////////////////////////////////////////////////////////////////////////
564
//
565
//              Misc drop target related constants
566
//
567
////////////////////////////////////////////////////////////////////////////////
568
// Drag Drop constants from ActiveX unit
569
var
570
  // Default inset-width of the auto scroll hot zone.
571
  // Specified in pixels.
572
  // Not used! Instead the height of the target control's font is used.
573
  DragDropScrollInset: integer = DD_DEFSCROLLINSET; // 11
574
 
575
  // Default delay after entering the scroll zone, before scrolling starts.
576
  // Specified in milliseconds.
577
  DragDropScrollDelay: integer = DD_DEFSCROLLDELAY; //  50
578
 
579
  // Default scroll interval during auto scroll.
580
  // Specified in milliseconds.
581
  DragDropScrollInterval: integer = DD_DEFSCROLLINTERVAL; // 50
582
 
583
  // Default delay before dragging should start.
584
  // Specified in milliseconds.
585
  DragDropDragDelay: integer = DD_DEFDRAGDELAY; // 200
586
 
587
  // Default minimum distance (radius) before dragging should start.
588
  // Specified in pixels.
589
  // Not used! Instead the SM_CXDRAG and SM_CYDRAG system metrics are used.
590
  DragDropDragMinDistance: integer = DD_DEFDRAGMINDIST; // 2
591
 
592
 
593
////////////////////////////////////////////////////////////////////////////////
594
//
595
//              Misc drag drop API related constants
596
//
597
////////////////////////////////////////////////////////////////////////////////
598
 
599
// The following DVASPECT constants are missing from some versions of Delphi and
600
// C++ Builder.
601
{$ifndef VER135_PLUS}
602
const
603
{$ifndef VER10_PLUS}
604
  DVASPECT_SHORTNAME = 2; // use for CF_HDROP to get short name version of file paths
605
{$endif}
606
  DVASPECT_COPY = 3; // use to indicate format is a "Copy" of the data (FILECONTENTS, FILEDESCRIPTOR, etc)
607
  DVASPECT_LINK = 4; // use to indicate format is a "Shortcut" to the data (FILECONTENTS, FILEDESCRIPTOR, etc)
608
{$endif}
609
 
610
////////////////////////////////////////////////////////////////////////////////
611
//
612
//              Component registration
613
//
614
////////////////////////////////////////////////////////////////////////////////
615
procedure Register;
616
 
617
 
618
(*******************************************************************************
619
**
620
**                      IMPLEMENTATION
621
**
622
*******************************************************************************)
623
implementation
624
 
625
uses
626
{$ifdef DEBUG}
627
  ComObj,
628
{$endif}
629
  DropSource,
630
  DropTarget,
631
  DragDropFormats, // Used by TRawClipboardFormat
632
  Messages,
633
  ShlObj,
634
  MMSystem,
635
  SysUtils;
636
 
637
resourcestring
638
  sImplementationRequired = 'Internal error: %s.%s needs implementation';
639
  sInvalidOwnerType = '%s is not a valid owner for %s. Owner must be derived from %s';
640
  sFormatNameReadOnly = '%s.ClipboardFormat is read-only';
641
  sNoCopyStgMedium = 'A required system function (URLMON.CopyStgMedium) was not available on this system. Operation aborted.';
642
  sBadConstructor = 'The %s class can not be instantiated with the default constructor';
643
  sUnregisteredDataFormat = 'The %s data format has not been registered by any of the used units';
644
 
645
 
646
////////////////////////////////////////////////////////////////////////////////
647
//
648
//              Component registration
649
//
650
////////////////////////////////////////////////////////////////////////////////
651
procedure Register;
652
begin
653
  RegisterComponents(DragDropComponentPalettePage, [TDataFormatAdapter]);
654
end;
655
 
656
 
657
////////////////////////////////////////////////////////////////////////////////
658
//
659
//              TInterfacedComponent
660
//
661
////////////////////////////////////////////////////////////////////////////////
662
function TInterfacedComponent.QueryInterface(const IID: TGuid; out Obj): HRESULT;
663
 
664
{$ifdef DEBUG}
665
  function GuidToString(const IID: TGuid): string;
666
  var
667
    GUID: string;
668
  begin
669
    GUID := ComObj.GUIDToString(IID);
670
    Result := GetRegStringValue('Interface\'+GUID, '');
671
    if (Result = '') then
672
      Result := GUID;
673
  end;
674
{$endif}
675
 
676
begin
677
{$ifdef VER12_PLUS}
678
  if GetInterface(IID, Obj) then
679
    Result := 0
680
  else if (VCLComObject <> nil) then
681
    Result := IVCLComObject(VCLComObject).QueryInterface(IID, Obj)
682
  else
683
    Result := E_NOINTERFACE;
684
{$else}
685
  Result := inherited QueryInterface(IID, Obj);
686
{$endif}
687
{$ifdef DEBUG}
688
  OutputDebugString(PChar(format('%s.QueryInterface(%s): %d (%d)',
689
    [ClassName, GuidToString(IID), Result, ord(pointer(Obj) <> nil)])));
690
{$endif}
691
end;
692
 
693
function TInterfacedComponent._AddRef: Integer;
694
var
695
  Outer: IUnknown;
696
begin
697
  // In case we are the inner object of an aggregation, we attempt to delegate
698
  // the reference counting to the outer object. We assume that the component
699
  // owner is the outer object.
700
  if (Owner <> nil) and (Owner.GetInterface(IUnknown, Outer)) then
701
    Result := Outer._AddRef
702
  else
703
  begin
704
{$ifdef VER12_PLUS}
705
    inherited _AddRef;
706
{$else}
707
    if (VCLComObject <> nil) then
708
      inherited _AddRef;
709
{$endif}
710
    Result := -1;
711
  end;
712
end;
713
 
714
function TInterfacedComponent._Release: Integer;
715
var
716
  Outer: IUnknown;
717
begin
718
  // See _AddRef for comments.
719
  if (Owner <> nil) and (Owner.GetInterface(IUnknown, Outer)) then
720
    Result := Outer._Release
721
  else
722
  begin
723
{$ifdef VER12_PLUS}
724
    inherited _Release;
725
{$else}
726
    if (VCLComObject <> nil) then
727
      inherited _Release;
728
{$endif}
729
    Result := -1;
730
  end;
731
end;
732
 
733
 
734
////////////////////////////////////////////////////////////////////////////////
735
//
736
//              TClipboardFormat
737
//
738
////////////////////////////////////////////////////////////////////////////////
739
destructor TClipboardFormat.Destroy;
740
begin
741
  // Warning: Do not call Clear here. Descendant class has already
742
  // cleaned up and released resources!
743
  inherited Destroy;
744
end;
745
 
746
constructor TClipboardFormat.CreateFormat(Atymed: Longint);
747
begin
748
  inherited Create;
749
  FDataDirections := [ddRead];
750
  FFormatEtc.cfFormat := ClipboardFormat;
751
  FFormatEtc.ptd := nil;
752
  FFormatEtc.dwAspect := DVASPECT_CONTENT;
753
  FFormatEtc.lindex := -1;
754
  FFormatEtc.tymed := Atymed;
755
end;
756
 
757
constructor TClipboardFormat.CreateFormatEtc(const AFormatEtc: TFormatEtc);
758
begin
759
  inherited Create;
760
  FDataDirections := [ddRead];
761
  FFormatEtc := AFormatEtc;
762
end;
763
 
764
function TClipboardFormat.HasValidFormats(ADataObject: IDataObject): boolean;
765
begin
766
  Result := (ADataObject.QueryGetData(FormatEtc) = S_OK);
767
end;
768
 
769
function TClipboardFormat.AcceptFormat(const AFormatEtc: TFormatEtc): boolean;
770
begin
771
  Result := (AFormatEtc.cfFormat = FFormatEtc.cfFormat) and
772
    (AFormatEtc.ptd = nil) and
773
    (AFormatEtc.dwAspect = FFormatEtc.dwAspect) and
774
    (AFormatEtc.tymed AND FFormatEtc.tymed <> 0);
775
end;
776
 
777
function TClipboardFormat.Assign(Source: TCustomDataFormat): boolean;
778
begin
779
  Result := False;
780
end;
781
 
782
function TClipboardFormat.AssignTo(Dest: TCustomDataFormat): boolean;
783
begin
784
  Result := False;
785
end;
786
 
787
function TClipboardFormat.DoGetData(ADataObject: IDataObject; const AMedium: TStgMedium): boolean;
788
begin
789
  Result := False;
790
end;
791
 
792
function TClipboardFormat.DoSetData(const FormatEtcIn: TFormatEtc;
793
  var AMedium: TStgMedium): boolean;
794
begin
795
  Result := False;
796
end;
797
 
798
function TClipboardFormat.GetData(ADataObject: IDataObject): boolean;
799
var
800
  Medium                : TStgMedium;
801
begin
802
  Result := False;
803
 
804
  Clear;
805
  if (ADataObject.GetData(FFormatEtc, Medium) <> S_OK) then
806
    exit;
807
  Result := GetDataFromMedium(ADataObject, Medium);
808
end;
809
 
810
function TClipboardFormat.GetDataFromMedium(ADataObject: IDataObject;
811
  var AMedium: TStgMedium): boolean;
812
begin
813
  Result := False;
814
  try
815
    Clear;
816
    if ((AMedium.tymed AND FFormatEtc.tymed) <> 0) then
817
      Result := DoGetData(ADataObject, AMedium);
818
  finally
819
    ReleaseStgMedium(AMedium);
820
  end;
821
end;
822
 
823
function TClipboardFormat.SetDataToMedium(const FormatEtcIn: TFormatEtc;
824
  var AMedium: TStgMedium): boolean;
825
begin
826
  Result := False;
827
 
828
  FillChar(AMedium, SizeOf(AMedium), 0);
829
 
830
  if (FormatEtcIn.cfFormat <> FFormatEtc.cfFormat) or
831
    (FormatEtcIn.dwAspect <> FFormatEtc.dwAspect) or
832
    (FormatEtcIn.tymed and FFormatEtc.tymed = 0) then
833
    exit;
834
 
835
  // Call descendant to allocate medium and transfer data to it
836
  Result := DoSetData(FormatEtcIn, AMedium);
837
end;
838
 
839
function TClipboardFormat.SetData(ADataObject: IDataObject;
840
  const FormatEtcIn: TFormatEtc; var AMedium: TStgMedium): boolean;
841
begin
842
  // Transfer data to medium
843
  Result := SetDataToMedium(FormatEtcIn, AMedium);
844
 
845
  // Call IDataObject to set data
846
  if (Result) then
847
    Result := (ADataObject.SetData(FormatEtc, AMedium, True) = S_OK);
848
 
849
  // If we didn't succeed in transfering ownership of the data medium to the
850
  // IDataObject, we must deallocate the medium ourselves.
851
  if (not Result) then
852
    ReleaseStgMedium(AMedium);
853
end;
854
 
855
class procedure TClipboardFormat.UnregisterClipboardFormat;
856
begin
857
  TDataFormatMap.Instance.DeleteByClipboardFormat(Self);
858
end;
859
 
860
function TClipboardFormat.GetClipboardFormat: TClipFormat;
861
begin
862
  // This should have been a virtual abstract class method, but this isn't supported by C++ Builder.
863
  raise Exception.CreateFmt(sImplementationRequired, [ClassName, 'GetClipboardFormat']);
864
end;
865
 
866
procedure TClipboardFormat.SetClipboardFormat(Value: TClipFormat);
867
begin
868
  FFormatEtc.cfFormat := Value;
869
end;
870
 
871
function TClipboardFormat.GetClipboardFormatName: string;
872
var
873
  Len                   : integer;
874
begin
875
  SetLength(Result, 255); // 255 is just an artificial limit.
876
  Len := Windows.GetClipboardFormatName(GetClipboardFormat, PChar(Result), 255);
877
  SetLength(Result, Len);
878
end;
879
 
880
procedure TClipboardFormat.SetClipboardFormatName(const Value: string);
881
begin
882
  raise Exception.CreateFmt(sFormatNameReadOnly, [ClassName]);
883
end;
884
 
885
function TClipboardFormat.HasData: boolean;
886
begin
887
  // Descendant classes are not required to override this method, so by default
888
  // we just pretend that data is available. No harm is done by this.
889
  Result := True;
890
end;
891
 
892
procedure TClipboardFormat.SetFormatEtc(const Value: TFormatEtc);
893
begin
894
  FFormatEtc := Value;
895
end;
896
 
897
 
898
////////////////////////////////////////////////////////////////////////////////
899
//
900
//              TClipboardFormats
901
//
902
////////////////////////////////////////////////////////////////////////////////
903
constructor TClipboardFormats.Create(ADataFormat: TCustomDataFormat;
904
  AOwnsObjects: boolean);
905
begin
906
  inherited Create;
907
  FList := TList.Create;
908
  FDataFormat := ADataFormat;
909
  FOwnsObjects := AOwnsObjects;
910
end;
911
 
912
destructor TClipboardFormats.Destroy;
913
begin
914
  Clear;
915
  FList.Free;
916
  inherited Destroy;
917
end;
918
 
919
function TClipboardFormats.Add(ClipboardFormat: TClipboardFormat): integer;
920
begin
921
  Result := FList.Add(ClipboardFormat);
922
  if (FOwnsObjects) and (DataFormat <> nil) then
923
    ClipboardFormat.DataFormat := DataFormat;
924
end;
925
 
926
function TClipboardFormats.FindFormat(ClipboardFormatClass: TClipboardFormatClass): TClipboardFormat;
927
var
928
  i                     : integer;
929
begin
930
  // Search list for an object of the specified type
931
  for i := 0 to Count-1 do
932
    if (Formats[i].InheritsFrom(ClipboardFormatClass)) then
933
    begin
934
      Result := Formats[i];
935
      exit;
936
    end;
937
  Result := nil;
938
end;
939
 
940
function TClipboardFormats.Contain(ClipboardFormatClass: TClipboardFormatClass): boolean;
941
begin
942
  Result := (FindFormat(ClipboardFormatClass) <> nil);
943
end;
944
 
945
function TClipboardFormats.GetCount: integer;
946
begin
947
  Result := FList.Count;
948
end;
949
 
950
function TClipboardFormats.GetFormat(Index: integer): TClipboardFormat;
951
begin
952
  Result := TClipboardFormat(FList[Index]);
953
end;
954
 
955
procedure TClipboardFormats.Clear;
956
var
957
  i                     : integer;
958
  Format                : TObject;
959
begin
960
  if (FOwnsObjects) then
961
    // Empty list and delete all objects in it
962
    for i := Count-1 downto 0 do
963
    begin
964
      Format := Formats[i];
965
      FList.Delete(i);
966
      Format.Free;
967
    end;
968
 
969
  FList.Clear;
970
end;
971
 
972
 
973
////////////////////////////////////////////////////////////////////////////////
974
//
975
//              TCustomDataFormat
976
//
977
////////////////////////////////////////////////////////////////////////////////
978
constructor TCustomDataFormat.Create(AOwner: TDragDropComponent);
979
var
980
  ConversionScope: TConversionScope;
981
begin
982
  if (AOwner <> nil) then
983
  begin
984
    if (AOwner is TCustomDropMultiSource) then
985
      ConversionScope := csSource
986
    else if (AOwner is TCustomDropMultiTarget) then
987
      ConversionScope := csTarget
988
    else
989
      raise Exception.CreateFmt(sInvalidOwnerType, [AOwner.ClassName, ClassName,
990
        'TCustomDropMultiSource or TCustomDropMultiTarget']);
991
    // Add object to owners list of data formats.
992
    FOwner := AOwner;
993
  end else
994
    // TODO : This sucks! All this ConversionScope stuff should be redesigned.
995
    ConversionScope := csTarget;
996
 
997
  FCompatibleFormats := TClipboardFormats.Create(Self, True);
998
  // Populate list with all the clipboard formats that have been registered as
999
  // compatible with this data format.
1000
  TDataFormatMap.Instance.GetSourceByDataFormat(TDataFormatClass(ClassType),
1001
    FCompatibleFormats, ConversionScope);
1002
 
1003
  if (FOwner <> nil) then
1004
    FOwner.DataFormats.Add(Self);
1005
end;
1006
 
1007
destructor TCustomDataFormat.Destroy;
1008
begin
1009
  FCompatibleFormats.Free;
1010
  // Remove object from owners list of target formats
1011
  if (FOwner <> nil) then
1012
    FOwner.DataFormats.Remove(Self);
1013
  inherited Destroy;
1014
end;
1015
 
1016
function TCustomDataFormat.Assign(Source: TClipboardFormat): boolean;
1017
begin
1018
  // Called when derived class(es) couldn't convert from the source format.
1019
  // Try to let source format convert to this format instead.
1020
  Result := Source.AssignTo(Self);
1021
end;
1022
 
1023
function TCustomDataFormat.AssignTo(Dest: TClipboardFormat): boolean;
1024
begin
1025
  // Called when derived class(es) couldn't convert to the destination format.
1026
  // Try to let destination format convert from this format instead.
1027
  Result := Dest.Assign(Self);
1028
end;
1029
 
1030
function TCustomDataFormat.GetData(DataObject: IDataObject): boolean;
1031
var
1032
  i: integer;
1033
begin
1034
  Result := False;
1035
  i := 0;
1036
  // Get data from each of our associated clipboard formats until we don't
1037
  // need anymore data.
1038
  while (NeedsData) and (i < CompatibleFormats.Count) do
1039
  begin
1040
    CompatibleFormats[i].Clear;
1041
 
1042
    if (CompatibleFormats[i].GetData(DataObject)) and
1043
      (CompatibleFormats[i].HasData) then
1044
    begin
1045
      if (Assign(CompatibleFormats[i])) then
1046
      begin
1047
        // Once data has been sucessfully transfered to the TDataFormat object,
1048
        // we clear the data in the TClipboardFormat object in order to conserve
1049
        // resources.
1050
        CompatibleFormats[i].Clear;
1051
        Result := True;
1052
      end;
1053
    end;
1054
 
1055
    inc(i);
1056
  end;
1057
end;
1058
 
1059
function TCustomDataFormat.NeedsData: boolean;
1060
begin
1061
  Result := not HasData;
1062
end;
1063
 
1064
function TCustomDataFormat.HasValidFormats(ADataObject: IDataObject): boolean;
1065
var
1066
  i: integer;
1067
begin
1068
  // Determine if any of the registered clipboard formats can read from the
1069
  // specified data object.
1070
  Result := False;
1071
  for i := 0 to CompatibleFormats.Count-1 do
1072
    if (CompatibleFormats[i].HasValidFormats(ADataObject)) then
1073
    begin
1074
      Result := True;
1075
      break;
1076
    end;
1077
end;
1078
 
1079
function TCustomDataFormat.AcceptFormat(const FormatEtc: TFormatEtc): boolean;
1080
var
1081
  i: integer;
1082
begin
1083
  // Determine if any of the registered clipboard formats can handle the
1084
  // specified clipboard format.
1085
  Result := False;
1086
  for i := 0 to CompatibleFormats.Count-1 do
1087
    if (CompatibleFormats[i].AcceptFormat(FormatEtc)) then
1088
    begin
1089
      Result := True;
1090
      break;
1091
    end;
1092
end;
1093
 
1094
class procedure TCustomDataFormat.RegisterDataFormat;
1095
begin
1096
  TDataFormatClasses.Instance.Add(Self);
1097
end;
1098
 
1099
class procedure TCustomDataFormat.RegisterCompatibleFormat(ClipboardFormatClass: TClipboardFormatClass;
1100
  Priority: integer; ConversionScopes: TConversionScopes;
1101
  DataDirections: TDataDirections);
1102
begin
1103
  // Register format mapping.
1104
  TDataFormatMap.Instance.RegisterFormatMap(Self, ClipboardFormatClass,
1105
    Priority, ConversionScopes, DataDirections);
1106
end;
1107
 
1108
function TCustomDataFormat.SupportsFormat(ClipboardFormat: TClipboardFormat): boolean;
1109
begin
1110
  Result := CompatibleFormats.Contain(TClipboardFormatClass(ClipboardFormat.ClassType));
1111
end;
1112
 
1113
class procedure TCustomDataFormat.UnregisterCompatibleFormat(ClipboardFormatClass: TClipboardFormatClass);
1114
begin
1115
  // Unregister format mapping
1116
  TDataFormatMap.Instance.UnregisterFormatMap(Self, ClipboardFormatClass);
1117
end;
1118
 
1119
class procedure TCustomDataFormat.UnregisterDataFormat;
1120
begin
1121
  TDataFormatMap.Instance.DeleteByDataFormat(Self);
1122
  TDataFormatClasses.Instance.Remove(Self);
1123
end;
1124
 
1125
procedure TCustomDataFormat.DoOnChanging(Sender: TObject);
1126
begin
1127
  Changing;
1128
end;
1129
 
1130
procedure TCustomDataFormat.Changing;
1131
begin
1132
  if (Assigned(OnChanging)) then
1133
    OnChanging(Self);
1134
end;
1135
 
1136
 
1137
////////////////////////////////////////////////////////////////////////////////
1138
//
1139
//              TDataFormats
1140
//
1141
////////////////////////////////////////////////////////////////////////////////
1142
function TDataFormats.Add(DataFormat: TCustomDataFormat): integer;
1143
begin
1144
  Result := FList.IndexOf(DataFormat);
1145
  if (Result = -1) then
1146
    Result := FList.Add(DataFormat);
1147
end;
1148
 
1149
constructor TDataFormats.Create;
1150
begin
1151
  inherited Create;
1152
  FList := TList.Create;
1153
end;
1154
 
1155
destructor TDataFormats.Destroy;
1156
var
1157
  i: integer;
1158
begin
1159
  for i := FList.Count-1 downto 0 do
1160
    Remove(TCustomDataFormat(FList[i]));
1161
  FList.Free;
1162
  inherited Destroy;
1163
end;
1164
 
1165
function TDataFormats.GetCount: integer;
1166
begin
1167
  Result := FList.Count;
1168
end;
1169
 
1170
function TDataFormats.GetFormat(Index: integer): TCustomDataFormat;
1171
begin
1172
  Result := TCustomDataFormat(FList[Index]);
1173
end;
1174
 
1175
function TDataFormats.IndexOf(DataFormat: TCustomDataFormat): integer;
1176
begin
1177
  Result := FList.IndexOf(DataFormat);
1178
end;
1179
 
1180
procedure TDataFormats.Remove(DataFormat: TCustomDataFormat);
1181
begin
1182
  FList.Remove(DataFormat);
1183
end;
1184
 
1185
////////////////////////////////////////////////////////////////////////////////
1186
//
1187
//              TDataFormatClasses
1188
//
1189
////////////////////////////////////////////////////////////////////////////////
1190
function TDataFormatClasses.Add(DataFormat: TDataFormatClass): integer;
1191
begin
1192
  Result := FList.IndexOf(DataFormat);
1193
  if (Result = -1) then
1194
    Result := FList.Add(DataFormat);
1195
end;
1196
 
1197
constructor TDataFormatClasses.Create;
1198
begin
1199
  inherited Create;
1200
  FList := TList.Create;
1201
end;
1202
 
1203
destructor TDataFormatClasses.Destroy;
1204
var
1205
  i: integer;
1206
begin
1207
  for i := FList.Count-1 downto 0 do
1208
    Remove(TDataFormatClass(FList[i]));
1209
  FList.Free;
1210
  inherited Destroy;
1211
end;
1212
 
1213
function TDataFormatClasses.GetCount: integer;
1214
begin
1215
  Result := FList.Count;
1216
end;
1217
 
1218
function TDataFormatClasses.GetFormat(Index: integer): TDataFormatClass;
1219
begin
1220
  Result := TDataFormatClass(FList[Index]);
1221
end;
1222
 
1223
var
1224
  FDataFormatClasses: TDataFormatClasses = nil;
1225
 
1226
class function TDataFormatClasses.Instance: TDataFormatClasses;
1227
begin
1228
  if (FDataFormatClasses = nil) then
1229
    FDataFormatClasses := TDataFormatClasses.Create;
1230
  Result := FDataFormatClasses;
1231
end;
1232
 
1233
procedure TDataFormatClasses.Remove(DataFormat: TDataFormatClass);
1234
begin
1235
  FList.Remove(DataFormat);
1236
end;
1237
 
1238
 
1239
////////////////////////////////////////////////////////////////////////////////
1240
//
1241
//              TDataFormatMap
1242
//
1243
////////////////////////////////////////////////////////////////////////////////
1244
type
1245
  // TTargetFormat / TClipboardFormat association
1246
  TFormatMap = record
1247
    DataFormat: TDataFormatClass;
1248
    ClipboardFormat: TClipboardFormatClass;
1249
    Priority: integer;
1250
    ConversionScopes: TConversionScopes;
1251
    DataDirections: TDataDirections;
1252
  end;
1253
 
1254
  PFormatMap = ^TFormatMap;
1255
 
1256
constructor TDataFormatMap.Create;
1257
begin
1258
  inherited Create;
1259
  FList := TList.Create;
1260
end;
1261
 
1262
destructor TDataFormatMap.Destroy;
1263
var
1264
  i                     : integer;
1265
begin
1266
  // Zap any mapings which hasn't been unregistered
1267
  // yet (actually an error condition)
1268
  for i := FList.Count-1 downto 0 do
1269
    Dispose(FList[i]);
1270
  FList.Free;
1271
  inherited Destroy;
1272
end;
1273
 
1274
procedure TDataFormatMap.Sort;
1275
var
1276
  i                     : integer;
1277
  NewMap                : PFormatMap;
1278
begin
1279
  // Note: We do not use the built-in Sort method of TList because
1280
  // we need to preserve the order in which the mappings were added.
1281
  // New mappings have higher precedence than old mappings (within the
1282
  // same priority).
1283
 
1284
  // Preconditions:
1285
  // 1) The list is already sorted before a new mapping is added.
1286
  // 2) The new mapping is always added to the end of the list.
1287
 
1288
  NewMap := PFormatMap(FList.Last);
1289
 
1290
  // Scan the list for a map with the same TTargetFormat type
1291
  i := FList.Count-2;
1292
  while (i > 0) do
1293
  begin
1294
    if (PFormatMap(FList[i])^.DataFormat = NewMap^.DataFormat) then
1295
    begin
1296
      // Scan the list for a map with lower priority
1297
      repeat
1298
        if (PFormatMap(FList[i])^.Priority < NewMap^.Priority) then
1299
        begin
1300
          // Move the mapping to the new position
1301
          FList.Move(FList.Count-1, i+1);
1302
          exit;
1303
        end;
1304
        dec(i);
1305
      until (i < 0) or (PFormatMap(FList[i])^.DataFormat <> NewMap^.DataFormat);
1306
      // Move the mapping to the new position
1307
      FList.Move(FList.Count-1, i+1);
1308
      exit;
1309
    end;
1310
    dec(i);
1311
  end;
1312
end;
1313
 
1314
procedure TDataFormatMap.Add(DataFormatClass: TDataFormatClass;
1315
  ClipboardFormatClass: TClipboardFormatClass; Priority: integer;
1316
  ConversionScopes: TConversionScopes; DataDirections: TDataDirections);
1317
var
1318
  FormatMap             : PFormatMap;
1319
  OldMap                : integer;
1320
begin
1321
  // Avoid duplicate mappings
1322
  OldMap := FindMap(DataFormatClass, ClipboardFormatClass);
1323
  if (OldMap = -1) then
1324
  begin
1325
    // Add new mapping...
1326
    New(FormatMap);
1327
    FList.Add(FormatMap);
1328
    FormatMap^.ConversionScopes := ConversionScopes;
1329
    FormatMap^.DataDirections := DataDirections;
1330
  end else
1331
  begin
1332
    // Replace old mapping...
1333
    FormatMap := FList[OldMap];
1334
    FList.Move(OldMap, FList.Count-1);
1335
    FormatMap^.ConversionScopes := FormatMap^.ConversionScopes + ConversionScopes;
1336
    FormatMap^.DataDirections := FormatMap^.DataDirections + DataDirections;
1337
  end;
1338
 
1339
  FormatMap^.ClipboardFormat := ClipboardFormatClass;
1340
  FormatMap^.DataFormat := DataFormatClass;
1341
  FormatMap^.Priority := Priority;
1342
  // ...and sort list
1343
  Sort;
1344
end;
1345
 
1346
function TDataFormatMap.CanMap(DataFormatClass: TDataFormatClass;
1347
  ClipboardFormatClass: TClipboardFormatClass): boolean;
1348
begin
1349
  Result := (FindMap(DataFormatClass, ClipboardFormatClass) <> -1);
1350
end;
1351
 
1352
procedure TDataFormatMap.Delete(DataFormatClass: TDataFormatClass;
1353
  ClipboardFormatClass: TClipboardFormatClass);
1354
var
1355
  Index                 : integer;
1356
begin
1357
  Index := FindMap(DataFormatClass, ClipboardFormatClass);
1358
  if (Index <> -1) then
1359
  begin
1360
    Dispose(FList[Index]);
1361
    FList.Delete(Index);
1362
  end;
1363
end;
1364
 
1365
procedure TDataFormatMap.DeleteByClipboardFormat(ClipboardFormatClass: TClipboardFormatClass);
1366
var
1367
  i                     : integer;
1368
begin
1369
  // Delete all mappings associated with the specified clipboard format
1370
  for i := FList.Count-1 downto 0 do
1371
    if (PFormatMap(FList[i])^.ClipboardFormat.InheritsFrom(ClipboardFormatClass)) then
1372
    begin
1373
      Dispose(FList[i]);
1374
      FList.Delete(i);
1375
    end;
1376
end;
1377
 
1378
procedure TDataFormatMap.DeleteByDataFormat(DataFormatClass: TDataFormatClass);
1379
var
1380
  i                     : integer;
1381
begin
1382
  // Delete all mappings associated with the specified target format
1383
  for i := FList.Count-1 downto 0 do
1384
    if (PFormatMap(FList[i])^.DataFormat.InheritsFrom(DataFormatClass)) then
1385
    begin
1386
      Dispose(FList[i]);
1387
      FList.Delete(i);
1388
    end;
1389
end;
1390
 
1391
function TDataFormatMap.FindMap(DataFormatClass: TDataFormatClass;
1392
  ClipboardFormatClass: TClipboardFormatClass): integer;
1393
var
1394
  i                     : integer;
1395
begin
1396
  for i := 0 to FList.Count-1 do
1397
    if (PFormatMap(FList[i])^.DataFormat.InheritsFrom(DataFormatClass)) and
1398
      (PFormatMap(FList[i])^.ClipboardFormat.InheritsFrom(ClipboardFormatClass)) then
1399
    begin
1400
      Result := i;
1401
      exit;
1402
    end;
1403
  Result := -1;
1404
end;
1405
 
1406
procedure TDataFormatMap.GetSourceByDataFormat(DataFormatClass: TDataFormatClass;
1407
  ClipboardFormats: TClipboardFormats; ConversionScope: TConversionScope);
1408
var
1409
  i: integer;
1410
  ClipboardFormat: TClipboardFormat;
1411
begin
1412
  // Clear the list...
1413
  ClipboardFormats.Clear;
1414
  // ...and populate it with *instances* of all the clipbard
1415
  // formats associated with the specified target format and
1416
  // registered with the specified data direction.
1417
  for i := 0 to FList.Count-1 do
1418
    if (ConversionScope in PFormatMap(FList[i])^.ConversionScopes) and
1419
      (PFormatMap(FList[i])^.DataFormat.InheritsFrom(DataFormatClass)) then
1420
    begin
1421
      ClipboardFormat := PFormatMap(FList[i])^.ClipboardFormat.Create;
1422
      ClipboardFormat.DataDirections := PFormatMap(FList[i])^.DataDirections;
1423
      ClipboardFormats.Add(ClipboardFormat);
1424
    end;
1425
end;
1426
 
1427
procedure TDataFormatMap.RegisterFormatMap(DataFormatClass: TDataFormatClass;
1428
  ClipboardFormatClass: TClipboardFormatClass; Priority: integer;
1429
  ConversionScopes: TConversionScopes; DataDirections: TDataDirections);
1430
begin
1431
  Add(DataFormatClass, ClipboardFormatClass, Priority, ConversionScopes,
1432
    DataDirections);
1433
end;
1434
 
1435
procedure TDataFormatMap.UnregisterFormatMap(DataFormatClass: TDataFormatClass;
1436
  ClipboardFormatClass: TClipboardFormatClass);
1437
begin
1438
  Delete(DataFormatClass, ClipboardFormatClass);
1439
end;
1440
 
1441
var
1442
  FDataFormatMap: TDataFormatMap = nil;
1443
 
1444
class function TDataFormatMap.Instance: TDataFormatMap;
1445
begin
1446
  if (FDataFormatMap = nil) then
1447
    FDataFormatMap := TDataFormatMap.Create;
1448
  Result := FDataFormatMap;
1449
end;
1450
 
1451
////////////////////////////////////////////////////////////////////////////////
1452
//
1453
//              TDataFormatAdapter
1454
//
1455
////////////////////////////////////////////////////////////////////////////////
1456
destructor TDataFormatAdapter.Destroy;
1457
begin
1458
  inherited Destroy;
1459
end;
1460
 
1461
function TDataFormatAdapter.GetDataFormatName: string;
1462
begin
1463
  if Assigned(FDataFormatClass) then
1464
    Result := FDataFormatClass.ClassName
1465
  else
1466
    Result := '';
1467
end;
1468
 
1469
function TDataFormatAdapter.GetEnabled: boolean;
1470
begin
1471
  if (csDesigning in ComponentState) then
1472
    Result := FEnabled
1473
  else
1474
    Result := Assigned(FDataFormat) and Assigned(FDataFormatClass);
1475
end;
1476
 
1477
procedure TDataFormatAdapter.Loaded;
1478
begin
1479
  inherited;
1480
  if (FEnabled) then
1481
    Enabled := True;
1482
end;
1483
 
1484
procedure TDataFormatAdapter.Notification(AComponent: TComponent;
1485
  Operation: TOperation);
1486
begin
1487
  if (Operation = opRemove) and (AComponent = FDragDropComponent) then
1488
    DragDropComponent := nil;
1489
  inherited;
1490
end;
1491
 
1492
procedure TDataFormatAdapter.SetDataFormatClass(const Value: TDataFormatClass);
1493
begin
1494
  if (Value <> FDataFormatClass) then
1495
  begin
1496
    if not(csLoading in ComponentState) then
1497
      Enabled := False;
1498
    FDataFormatClass := Value;
1499
  end;
1500
end;
1501
 
1502
procedure TDataFormatAdapter.SetDataFormatName(const Value: string);
1503
var
1504
  i: integer;
1505
  ADataFormatClass: TDataFormatClass;
1506
begin
1507
  ADataFormatClass := nil;
1508
  if (Value <> '') then
1509
  begin
1510
    for i := 0 to TDataFormatClasses.Instance.Count-1 do
1511
      if (AnsiCompareText(TDataFormatClasses.Instance[i].ClassName, Value) = 0) then
1512
      begin
1513
        ADataFormatClass := TDataFormatClasses.Instance[i];
1514
        break;
1515
      end;
1516
    if (ADataFormatClass = nil) then
1517
      raise Exception.CreateFmt(sUnregisteredDataFormat, [Value]);
1518
  end;
1519
  DataFormatClass := ADataFormatClass;
1520
end;
1521
 
1522
procedure TDataFormatAdapter.SetDragDropComponent(const Value: TDragDropComponent);
1523
begin
1524
  if (Value <> FDragDropComponent) then
1525
  begin
1526
    if not(csLoading in ComponentState) then
1527
      Enabled := False;
1528
    if (FDragDropComponent <> nil) then
1529
      FDragDropComponent.RemoveFreeNotification(Self);
1530
    FDragDropComponent := Value;
1531
    if (Value <> nil) then
1532
      Value.FreeNotification(Self);
1533
  end;
1534
end;
1535
 
1536
procedure TDataFormatAdapter.SetEnabled(const Value: boolean);
1537
begin
1538
  if (csLoading in ComponentState) then
1539
  begin
1540
    FEnabled := Value;
1541
  end else
1542
  if (csDesigning in ComponentState) then
1543
  begin
1544
    FEnabled := Value and Assigned(FDragDropComponent) and
1545
      Assigned(FDataFormatClass);
1546
  end else
1547
  if (Value) then
1548
  begin
1549
    if (Assigned(FDragDropComponent)) and (Assigned(FDataFormatClass)) and
1550
      (not Assigned(FDataFormat)) then
1551
      FDataFormat := FDataFormatClass.Create(FDragDropComponent);
1552
  end else
1553
  begin
1554
    if Assigned(FDataFormat) then
1555
    begin
1556
      if Assigned(FDragDropComponent) and
1557
        (FDragDropComponent.DataFormats.IndexOf(FDataFormat) <> -1) then
1558
        FDataFormat.Free;
1559
      FDataFormat := nil;
1560
    end;
1561
  end;
1562
end;
1563
 
1564
 
1565
////////////////////////////////////////////////////////////////////////////////
1566
//
1567
//              TRawClipboardFormat
1568
//
1569
////////////////////////////////////////////////////////////////////////////////
1570
constructor TRawClipboardFormat.Create;
1571
begin
1572
  // Yeah, it's a hack but blame Borland for making TObject.Create public!
1573
  raise Exception.CreateFmt(sBadConstructor, [ClassName]);
1574
end;
1575
 
1576
constructor TRawClipboardFormat.CreateFormatEtc(const AFormatEtc: TFormatEtc);
1577
begin
1578
  inherited CreateFormatEtc(AFormatEtc);
1579
end;
1580
 
1581
procedure TRawClipboardFormat.SetClipboardFormatName(const Value: string);
1582
begin
1583
  ClipboardFormat := RegisterClipboardFormat(PChar(Value));
1584
end;
1585
 
1586
function TRawClipboardFormat.GetClipboardFormat: TClipFormat;
1587
begin
1588
  Result := FFormatEtc.cfFormat;
1589
end;
1590
 
1591
function TRawClipboardFormat.Assign(Source: TCustomDataFormat): boolean;
1592
begin
1593
  if (Source is TRawDataFormat) then
1594
  begin
1595
    Result := True;
1596
  end else
1597
    Result := inherited Assign(Source);
1598
end;
1599
 
1600
function TRawClipboardFormat.AssignTo(Dest: TCustomDataFormat): boolean;
1601
begin
1602
  if (Dest is TRawDataFormat) then
1603
  begin
1604
    Result := True;
1605
  end else
1606
    Result := inherited AssignTo(Dest);
1607
end;
1608
 
1609
procedure TRawClipboardFormat.Clear;
1610
begin
1611
  // Since TRawDataFormat performs storage for TRawDataFormat we only allow
1612
  // TRawDataFormat to clear. To accomplish this TRawDataFormat ignores calls to
1613
  // the clear method and instead introduces the ClearData method.
1614
end;
1615
 
1616
procedure TRawClipboardFormat.ClearData;
1617
begin
1618
  ReleaseStgMedium(FMedium);
1619
  FillChar(FMedium, SizeOf(FMedium), 0);
1620
end;
1621
 
1622
function TRawClipboardFormat.HasData: boolean;
1623
begin
1624
  Result := (FMedium.tymed <> TYMED_NULL);
1625
end;
1626
 
1627
function TRawClipboardFormat.NeedsData: boolean;
1628
begin
1629
  Result := (FMedium.tymed = TYMED_NULL);
1630
end;
1631
 
1632
procedure TRawClipboardFormat.CopyFromStgMedium(const AMedium: TStgMedium);
1633
begin
1634
  CopyStgMedium(AMedium, FMedium);
1635
end;
1636
 
1637
procedure TRawClipboardFormat.CopyToStgMedium(var AMedium: TStgMedium);
1638
begin
1639
  CopyStgMedium(FMedium, AMedium);
1640
end;
1641
 
1642
function TRawClipboardFormat.DoGetData(ADataObject: IDataObject;
1643
  const AMedium: TStgMedium): boolean;
1644
begin
1645
  Result := CopyStgMedium(AMedium, FMedium);
1646
end;
1647
 
1648
function TRawClipboardFormat.DoSetData(const FormatEtcIn: TFormatEtc;
1649
  var AMedium: TStgMedium): boolean;
1650
begin
1651
  Result := CopyStgMedium(FMedium, AMedium);
1652
end;
1653
 
1654
function TRawClipboardFormat.GetString: string;
1655
begin
1656
  with TTextClipboardFormat.Create do
1657
    try
1658
      if GetDataFromMedium(nil, FMedium) then
1659
        Result := Text
1660
      else
1661
        Result := '';
1662
    finally
1663
      Free;
1664
    end;
1665
end;
1666
 
1667
procedure TRawClipboardFormat.SetString(const Value: string);
1668
begin
1669
  with TTextClipboardFormat.Create do
1670
    try
1671
      Text := Value;
1672
      SetDataToMedium(FormatEtc, FMedium);
1673
    finally
1674
      Free;
1675
    end;
1676
end;
1677
 
1678
////////////////////////////////////////////////////////////////////////////////
1679
//
1680
//              TRawDataFormat
1681
//
1682
////////////////////////////////////////////////////////////////////////////////
1683
procedure TRawDataFormat.Clear;
1684
var
1685
 i: integer;
1686
begin
1687
  Changing;
1688
  for i := 0 to CompatibleFormats.Count-1 do
1689
    TRawClipboardFormat(CompatibleFormats[i]).ClearData;
1690
end;
1691
 
1692
function TRawDataFormat.HasData: boolean;
1693
var
1694
 i: integer;
1695
begin
1696
  i := 0;
1697
  Result := False;
1698
  while (not Result) and (i < CompatibleFormats.Count) do
1699
  begin
1700
    Result := TRawClipboardFormat(CompatibleFormats[i]).HasData;
1701
    inc(i);
1702
  end;
1703
end;
1704
 
1705
function TRawDataFormat.NeedsData: boolean;
1706
var
1707
 i: integer;
1708
begin
1709
  i := 0;
1710
  Result := False;
1711
  while (not Result) and (i < CompatibleFormats.Count) do
1712
  begin
1713
    Result := TRawClipboardFormat(CompatibleFormats[i]).NeedsData;
1714
    inc(i);
1715
  end;
1716
end;
1717
 
1718
 
1719
////////////////////////////////////////////////////////////////////////////////
1720
//
1721
//              Utility functions
1722
//
1723
////////////////////////////////////////////////////////////////////////////////
1724
procedure _RaiseLastWin32Error;
1725
begin
1726
{$ifdef VER14_PLUS}
1727
  RaiseLastOSError;
1728
{$else}
1729
  RaiseLastWin32Error;
1730
{$endif}
1731
end;
1732
 
1733
function DropEffectToDragType(DropEffect: longInt; var DragType: TDragType): boolean;
1734
begin
1735
  Result := True;
1736
  if ((DropEffect and DROPEFFECT_COPY) <> 0) then
1737
    DragType := dtCopy
1738
  else
1739
    if ((DropEffect and DROPEFFECT_MOVE) <> 0) then
1740
      DragType := dtMove
1741
    else
1742
      if ((DropEffect and DROPEFFECT_LINK) <> 0) then
1743
        DragType := dtLink
1744
      else
1745
      begin
1746
        DragType := dtCopy;
1747
        Result := False;
1748
      end;
1749
end;
1750
 
1751
function DragTypesToDropEffect(DragTypes: TDragTypes): longint;
1752
begin
1753
  Result := DROPEFFECT_NONE;
1754
  if (dtCopy in DragTypes) then
1755
    Result := Result OR DROPEFFECT_COPY;
1756
  if (dtMove in DragTypes) then
1757
    Result := Result OR DROPEFFECT_MOVE;
1758
  if (dtLink in DragTypes) then
1759
    Result := Result OR DROPEFFECT_LINK;
1760
end;
1761
 
1762
// Replacement for the buggy DragDetect API function.
1763
function DragDetectPlus(Handle: THandle; p: TPoint): boolean;
1764
var
1765
  DragRect: TRect;
1766
  Msg: TMsg;
1767
  StartTime: DWORD;
1768
  OldCapture: HWND;
1769
begin
1770
  Result := False;
1771
  if (not ClientToScreen(Handle, p)) then
1772
    exit;
1773
  // Calculate the drag rect. If the mouse leaves this rect while the
1774
  // mouse button is pressed, a drag is detected.
1775
  DragRect.TopLeft := p;
1776
  DragRect.BottomRight := p;
1777
  InflateRect(DragRect, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
1778
  StartTime := TimeGetTime;
1779
  // Capture the mouse so that we will receive mouse messages even after the
1780
  // mouse leaves the control rect.
1781
  OldCapture := SetCapture(Handle);
1782
  try
1783
    // Abort if we failed to capture the mouse.
1784
    if (GetCapture <> Handle) then
1785
      exit;
1786
    while (not Result) do
1787
    begin
1788
      // Detect if all mouse buttons are up (might mean that we missed a
1789
      // MW_?BUTTONUP message).
1790
      if (GetAsyncKeyState(VK_LBUTTON) AND $8000 = 0) and
1791
        (GetAsyncKeyState(VK_RBUTTON) AND $8000 = 0) then
1792
        break;
1793
 
1794
      if (PeekMessage(Msg, Handle, 0,0, PM_REMOVE)) then
1795
      begin
1796
        case (Msg.message) of
1797
          WM_MOUSEMOVE:
1798
            // Mouse were moved. Check if we are still within the drag rect...
1799
            Result := (not PtInRect(DragRect, Msg.pt)) and
1800
              // ... and that the minimum time has elapsed.
1801
              // Note that we ignore time warp (wrap around) and that Msg.Time
1802
              // might be smaller than StartTime.
1803
              (Msg.time >= StartTime + DWORD(DragDropDragDelay));
1804
          WM_RBUTTONUP,
1805
          WM_LBUTTONUP,
1806
          WM_CANCELMODE:
1807
            // Mouse button were released, escape were pressed or some other
1808
            // operation cancelled our mouse capture.
1809
            break;
1810
          WM_QUIT:
1811
            // Application is shutting down. Get out of here fast.
1812
            exit;
1813
        else
1814
          TranslateMessage(Msg);
1815
          DispatchMessage(Msg);
1816
        end;
1817
      end else
1818
        Sleep(0);
1819
    end;
1820
  finally
1821
    ReleaseCapture;
1822
    // Restore previous capture.
1823
    if (OldCapture <> 0) then
1824
      SetCapture(OldCapture);
1825
  end;
1826
end;
1827
 
1828
function ClientPtToWindowPt(Handle: THandle; pt: TPoint): TPoint;
1829
var
1830
  Rect: TRect;
1831
begin
1832
  ClientToScreen(Handle, pt);
1833
  GetWindowRect(Handle, Rect);
1834
  Result.X := pt.X - Rect.Left;
1835
  Result.Y := pt.Y - Rect.Top;
1836
end;
1837
 
1838
const
1839
  // Note: The definition of MK_ALT is missing from the current Delphi (D5)
1840
  // declarations. Hopefully Delphi 6 will fix this.
1841
  MK_ALT = $20;
1842
 
1843
function KeysToShiftStatePlus(Keys: Word): TShiftState;
1844
begin
1845
  Result := [];
1846
  if (Keys and MK_SHIFT <> 0) then
1847
    Include(Result, ssShift);
1848
  if (Keys and MK_CONTROL <> 0) then
1849
    Include(Result, ssCtrl);
1850
  if (Keys and MK_LBUTTON <> 0) then
1851
    Include(Result, ssLeft);
1852
  if (Keys and MK_RBUTTON <> 0) then
1853
    Include(Result, ssRight);
1854
  if (Keys and MK_MBUTTON <> 0) then
1855
    Include(Result, ssMiddle);
1856
  if (Keys and MK_ALT <> 0) then
1857
    Include(Result, ssMiddle);
1858
end;
1859
 
1860
function ShiftStateToDropEffect(Shift: TShiftState; AllowedEffects: longint;
1861
  Fallback: boolean): longint;
1862
begin
1863
  // As we're only interested in ssShift & ssCtrl here,
1864
  // mouse button states are screened out.
1865
  Shift := Shift * [ssShift, ssCtrl];
1866
 
1867
  Result := DROPEFFECT_NONE;
1868
  if (Shift = [ssShift, ssCtrl]) then
1869
  begin
1870
    if (AllowedEffects AND DROPEFFECT_LINK <> 0) then
1871
      Result := DROPEFFECT_LINK;
1872
  end else
1873
  if (Shift = [ssCtrl]) then
1874
  begin
1875
    if (AllowedEffects AND DROPEFFECT_COPY <> 0) then
1876
      Result := DROPEFFECT_COPY;
1877
  end else
1878
  begin
1879
    if (AllowedEffects AND DROPEFFECT_MOVE <> 0) then
1880
      Result := DROPEFFECT_MOVE;
1881
  end;
1882
 
1883
  // Fall back to defaults if the shift-states specified an
1884
  // unavailable drop effect.
1885
  if (Result = DROPEFFECT_NONE) and (Fallback) then
1886
  begin
1887
    if (AllowedEffects AND DROPEFFECT_COPY <> 0) then
1888
      Result := DROPEFFECT_COPY
1889
    else if (AllowedEffects AND DROPEFFECT_MOVE <> 0) then
1890
      Result := DROPEFFECT_MOVE
1891
    else if (AllowedEffects AND DROPEFFECT_LINK <> 0) then
1892
      Result := DROPEFFECT_LINK;
1893
  end;
1894
end;
1895
 
1896
var
1897
  URLMONDLL: THandle = 0;
1898
  _CopyStgMedium: function(const cstgmedSrc: TStgMedium; var stgmedDest: TStgMedium): HResult; stdcall = nil;
1899
 
1900
function CopyStgMedium(const SrcMedium: TStgMedium; var DstMedium: TStgMedium): boolean;
1901
begin
1902
  // Copy the medium via the URLMON CopyStgMedium function. This should be safe
1903
  // since this function is only called when the drag drop helper object is
1904
  // used and the drag drop helper object is only supported on Windows 2000
1905
  // and later.
1906
  // URLMON.CopyStgMedium requires IE4 or later.
1907
  // An alternative approach would be to use OleDuplicateData, but based on a
1908
  // disassembly of urlmon.dll, CopyStgMedium seems to do a lot more than
1909
  // OleDuplicateData.
1910
  if (URLMONDLL = 0) then
1911
  begin
1912
    URLMONDLL := LoadLibrary('URLMON.DLL');
1913
    if (URLMONDLL <> 0) then
1914
      @_CopyStgMedium := GetProcAddress(URLMONDLL, 'CopyStgMedium');
1915
  end;
1916
 
1917
  if (@_CopyStgMedium = nil) then
1918
    raise Exception.Create(sNoCopyStgMedium);
1919
 
1920
  Result := (_CopyStgMedium(SrcMedium, DstMedium) = S_OK);
1921
end;
1922
 
1923
function GetClipboardFormatNameStr(Value: TClipFormat): string;
1924
var
1925
  len: integer;
1926
begin
1927
  Setlength(Result, 255);
1928
  len := GetClipboardFormatName(Value, PChar(Result), 255);
1929
  SetLength(Result, len);
1930
end;
1931
 
1932
////////////////////////////////////////////////////////////////////////////////
1933
//
1934
//              Initialization/Finalization
1935
//
1936
////////////////////////////////////////////////////////////////////////////////
1937
initialization
1938
  OleInitialize(nil);
1939
  ShGetMalloc(ShellMalloc);
1940
  GetClipboardFormatNameStr(0);
1941
 
1942
finalization
1943
  if (FDataFormatMap <> nil) then
1944
  begin
1945
    FDataFormatMap.Free;
1946
    FDataFormatMap := nil;
1947
  end;
1948
  if (FDataFormatClasses <> nil) then
1949
  begin
1950
    FDataFormatClasses.Free;
1951
    FDataFormatClasses := nil;
1952
  end;
1953
 
1954
  ShellMalloc := nil;
1955
 
1956
  OleUninitialize;
1957
end.
1958