wideman-one 
Last edit: 05-03-17 Graham Wideman

Delphi

GWDropTarget unit and GWDropDemo code
Article created: 98-06-26

Introduction

This page describes the main features of the GWDropTarget unit, and the GWDropDemo application code.

GWDropDemo Application Architecture, How GWDropTarget Fits In

The diagram below shows the basic model.

GWDropDemo_Arch.GIF (20338 bytes)

The basic idea is that main apparatus that deals with COM is located in GWDropTarget.PAS, and can be used as-is in a variety of applications.  The piece that's specific to a particular app can inherit the basics and override selected functions for that app's needs.   This is demonstrated in the GWDropDemo application, which uses that GWDropTarget.PAS unit, and places all the app-specific drop functionality in DropDemoAppInterface.PAS.

The second piece to notice is that GWDropTarget.PAS contains two classes, not just the TGWDropTarget object that implements the IDropTarget interface. Why? Because extra functionality is needed to deal with the fact that once the IDropTarget object is handed over to the system, the system has some control over destroying it.  We therefore need someplace else to stick functionality that has a different lifetime.  This helper (TGWDropAppInterface) or its descendant turns out to be a convenient place to delegate the app-specific details of drag-time behavior, and dealing with the interface to the app's data, and the app's user-interface.

Finally, TGWDropTarget inherits from TGWMonInterfacedObject and consequently can cooperate with the monitoring apparatus in GWMonOLE.pas. This can be omitted if not needed.

Details

TGWDropAppInterface

Initialization and state
constructor 
Create(AOwner: TForm;
UseMonitor: Boolean);
destructor Destroy;
  • Create in FormCreate, Destroy in FormDestroy. 
  • AOwner supplies reference to app's form so that descendants can interact with form's user interface. 
  • UseMonitor tells whether to activate monitoring by creating an fm_OLEMonitor form. Create assigns AOwner as owner of the monitor form so that it gets destroyed automatically on destroying the owner form. 
  • Note that the fm_OLEMonitor form defaults to hidden, so you must call Show to see it.
  • In TGWDropAppInterface descendants, as usual override Create (and call inherited Create!) to add initializations for added member variables.
function 
DropTarget_Create(
  AWinControl: TWinControl;
  Reg: Boolean
): HResult;
Call this to create the actual TGWDropTarget object. Pass AWinControl as the control that will receive drops. Set Reg true if you want to lock and register the IDropTarget immediately. This is usually the desired option, but not for the GWDropDemo app.
property 
DropTarget: TGWDropTarget;
Use this to access DropTarget functions directly.
function 
DropTarget_Exists: Boolean;
Call this function to find out if the DropTarget exists (before calling DropTarget functions).
procedure DropTarget_Forget;
Don't call this. TGWDropTarget.Destroy calls this to let TGWDropAppInterface know that DropTarget is being destroyed.
function 
DropTarget_LifeState: Tgwdt_ls;
For diagnostics purposes this indicates the state of the DropTarget object.
gwdt_ls_start:       object doesn't exist
gwdt_ls_exists:     object exists 
gwdt_ls_locked:   object has been locked
gwdt_ls_regd:       object has been RegisterDragDrop'ed, ready to accept drops
IDragDrop delegation functions. Override these to provide app-specific drag behavior. Without writing any code, the default implementation, together with the default helper functions, will at least allow your app to visually appear to accept drops, even though it does nothing with the data.
function DragEnter(
  const dataObj: IDataObject; 
    grfKeyState: Longint;
             pt: TPoint; 
   var dwEffect: Longint
): HResult; virtual;
Called once by the system when user drags into target area.
Decide whether to accept the drop, usually by using dataObj to look at formats that source app is offering.  Save conclusion for use by DragOver.
Decide on drop effect desired based on shift/ctrl key state.
Provide target window visual hit feedback if desired.
function DragOver(
    grfKeyState: Longint; 
             pt: TPoint;
   var dwEffect: Longint
): HResult; virtual;
After an initial DragEnter, this is called repeatedly by the system as user drags within target area. Return indication of drop acceptance and drop effect.
function DragLeave: 
  HResult; virtual;
System calls this once to indicate that "Elvis has left the building" (er, I mean the user has dragged outside of target area.) Opportunity to undo visual hit feedback.
function Drop(
  const dataObj: IDataObject; 
    grfKeyState: Longint; 
             pt: TPoint;
   var dwEffect: Longint
): HResult; virtual
System calls this once to indicate that user finished dropping (mouse-up) in target area. Use dataObj to get data. Return indication of drop effect (eg: DROPEFFECT_MOVE tells source app to delete its copy of the data).
Drop Helper functions
function Drop_StdEffect(
  grfKeyState: Longint
): Longint; virtual;
Translates state of shift/ctrl modifier keys into standard copy/move effects. Returns a dwEffect value.
function Drop_Logic(
        phase: Tddp; 
      dataObj: IDataObject; 
  grfKeyState: Longint;
           pt: TPoint
): Longint; virtual;
The default DragEnter/Over/Drop functions call Drop_Logic.  Override this if you prefer to centralize all your drop logic in one place and avoid overriding the individual Drag functions.  Works for simpler cases.   Phase indicates which drag routine is calling.
procedure Drop_Feedback(
     phase: Tddp; 
        pt: TPoint; 
  dwEffect: longint
); virtual;
Dumb default visual hit feedback. Temporarily changes control's background color during hit.

TGWDropTarget Notes

You should not have to modify TGWDropTarget or work with its functions and variables directly, though of course you may be interested in seeing how it works by looking at the source code.

One noteworthy feature is how TGWDropTarget resolves the potential Destroy-loop problem.  The nature of the problem is this:

Possible Strategies:

This I have attempted to do in the TGWDropTarget code. 

Drop Effect (and Internet Explorer) Confusion

98-10-02: IE seems to create a lot of confusion for people wanting to write apps that accept URLs dragged from IE  (This discussion applies to IE 3.02, and presumably other versions too) .  The basic problem is this:  IE offers only a drop effect of DROPEFFECT_LINK, and wants the receiving app to respond with a request for the same. This means that apps that only look at the key-shift state to create their request will only be able to accept drops from IE when the user presses  <Ctrl-Shift> -- not entirely obvious to users.

Instead, drop-accepting apps apps should be careful to choose an effect from among those drop source offers, modified by what shift keys the user presses. This is incorporate in GWDropDemo in V1.12 and later.

Note that Netscape (at least as of V4.04) does not precipitate this confusion since it offers several drop effect options.


Go to:  Drop Demo Intro Page, or  wideman-one