unit DragDropURL;
{
  Description
  ===========
    TDragDropURL is a component for simple OLE drag-and-drop operations
    with URLs. The component is a child-class from TDragDrop. 


  Disclaimer
  ==========
    The author disclaims all warranties, expressed or implied, including,
    without limitation, the warranties of merchantability and of fitness
    for any purpose. The author assumes no liability for damages, direct or
    consequential, which may result from the use of this component/unit.


  Restrictions on Using the Unit / Component
  ==========================================
    This unit/component is copyright 1998 by Dieter Steinwedel. ALL RIGHTS
    ARE RESERVED BY DIETER STEINWEDEL. You are allowed to use it freely
    subject to the following restrictions:

    • You are not allowed delete or alter the author's name and
      copyright in any manner

    • You are not allowed to publish a copy, modified version or
      compilation neither for payment in any kind nor freely

    • You are allowed to create a link to the download in the WWW

    • These restrictions and terms apply to you as long as until
      I alter them. Changes can found on my homepage


  Contact
  =======
    homepage: http://godard.oec.uni-osnabrueck.de/student_home/dsteinwe/delphi/DietersDelphiSite.htm
}

{$ALIGN ON}
{$ASSERTIONS OFF}
{$BOOLEVAL OFF}
{$DENYPACKAGEUNIT OFF}
{$EXTENDEDSYNTAX ON}
{$HINTS ON}
{$IMPORTEDDATA ON}
{$LONGSTRINGS ON}
{$OPTIMIZATION ON}
{$TYPEDADDRESS OFF}
{$TYPEINFO OFF}
{$WARNINGS ON}

interface

uses DragDrop, Windows, Classes, SysUtils, ActiveX, PIDL, ShlObj;

type
  TDataObjectURL = class(TDataObject)
  private
     URLStream:TMemoryStream;
     FGDStream:TMemoryStream;
  public
     constructor Create(ScrapFileName, URL:string; Scrap:boolean);
     destructor Destroy; override;
     function RenderData(FormatEtc:TFormatEtc;
        var StgMedium: TStgMedium):HResult; override;
  end;

  TDropTargetURL = class(TDropTarget)
  protected
     procedure AcceptDataObject(DataObj: IDataObject; var Accept:boolean); override;
  public
     constructor Create(AOwner: TDragDrop);
     destructor Destroy; override;
     procedure RenderDropped(DataObj: IDataObject; grfKeyState: Longint;
        pt: TPoint; var dwEffect: longint); override;
  end;

  TDragDropURL = class(TDragDrop)
  private
     FURL:String;
     FScrapFileName:string;
  protected
     function CreateDataObject:TDataObject; override;
  public
     constructor Create(AOwner: TComponent); override; 
     destructor Destroy; override;
     property URL: String read FURL write FURL;
     property ScrapFileName: string read FScrapFileName write FScrapFileName;
  end;

procedure Register;

implementation

// TDataObjectURL -------------------------------------------------------------

const HLineSize=24;

constructor TDataObjectURL.Create(ScrapFileName, URL:string; Scrap:boolean);
var FE:TFormatEtc;
    SM:TStgMedium;
    pc:array[0..255] of char;
    FDescriptor:TFILEGROUPDESCRIPTOR;
begin
     inherited Create;
     with FE do
     begin
          cfFormat:=CF_SHELLURL;
          ptd:=nil;
          dwAspect:=DVAspect_Content;
          lindex:=-1;
          tymed:=tymed_HGlobal;
     end;
     SetData(FE,SM,false);
     FE.cfFormat:=cf_Text;
     SetData(FE,SM,false);
     if Scrap then
     begin
          FE.cfFormat:=CF_FILEDESCRIPTOR;
          SetData(FE,SM,false);
          FE.cfFormat:=CF_FILECONTENTS;
          FE.lindex:=0;
          SetData(FE,SM,false);
     end;
     URLStream:=TMemoryStream.Create;
     Fillchar(pc,sizeof(pc),#0);
     pc:='[InternetShortcut]'#13#10'URL=';
     URLStream.Write(pc,HLineSize);
     Fillchar(pc,sizeof(pc),#0);
     strPcopy(pc,URL+#0);
     URLStream.Write(pc,length(URL)+1);
     FGDStream:=TMemoryStream.Create;
     FDescriptor.cItems:=1;
     with FDescriptor.fgd[0] do
     begin
          dwFlags:=FD_LinkUI;
          FillChar(cFileName,sizeof(cFileName),#0);
          if ScrapFileName<>'' then
          begin
               if CompareText(ExtractFileExt(ScrapFileName),'.url')<>0 then
                  ScrapFileName:=ScrapFileName+'.url';
               strPcopy(cFileName,ScrapFileName+#0);
          end
          else cFileName:='URL Link.url';
     end;
     FGDStream.Write(FDescriptor,SizeOf(FDescriptor));
end;

destructor TDataObjectURL.Destroy;
begin
     URLStream.free;
     FGDStream.free;
     inherited Destroy;
end;

function TDataObjectURL.RenderData(FormatEtc:TFormatEtc;
        var StgMedium: TStgMedium):HResult;
var h: HGlobal;
    p:pointer;
begin
     Result:=E_Fail;
     if (FormatEtc.cfFormat=cf_Text) or (FormatEtc.cfFormat=CF_SHELLURL) then
     begin
          h:=GlobalAlloc(GHND or GMEM_SHARE, URLStream.Size-HLineSize);
          if h=0 then
          begin
               Result:=E_OUTOFMEMORY;
               exit;
          end;
          p:=globallock(h);
          URLStream.Seek(HLineSize,0);
          URLStream.Read(p^,URLStream.Size-HLineSize);
          globalunlock(h);
          with StgMedium do
          begin
               tymed:=TYMED_HGLOBAL;
               hGlobal := h;
               unkForRelease := nil;
          end;
          Result:=S_OK;
     end;
     if (FormatEtc.cfFormat=CF_FILECONTENTS) then
     begin
          h:=GlobalAlloc(GHND or GMEM_SHARE, URLStream.Size);
          if h=0 then
          begin
               Result:=E_OUTOFMEMORY;
               exit;
          end;
          p:=globallock(h);
          URLStream.Seek(0,0);
          URLStream.Read(p^,URLStream.Size);
          globalunlock(h);
          with StgMedium do
          begin
               tymed:=TYMED_HGLOBAL;
               hGlobal := h;
               unkForRelease := nil;
          end;
          Result:=S_OK;
     end;
     if (FormatEtc.cfFormat=CF_FILEDESCRIPTOR) then
     begin
          h:=GlobalAlloc(GHND or GMEM_SHARE, FGDStream.Size);
          if h=0 then
          begin
               Result:=E_OUTOFMEMORY;
               exit;
          end;
          p:=globallock(h);
          FGDStream.Seek(0,0);
          FGDStream.Read(p^,FGDStream.Size);
          globalunlock(h);
          with StgMedium do
          begin
               tymed:=TYMED_HGLOBAL;
               hGlobal := h;
               unkForRelease := nil;
          end;
          Result:=S_OK;
     end;
end;

// TDropTargetURL -------------------------------------------------------------

constructor TDropTargetURL.Create(AOwner: TDragDrop);
begin
     inherited Create(AOwner);
end;

destructor TDropTargetURL.Destroy;
begin
     inherited Destroy;
end;

procedure TDropTargetURL.AccepTDataObject(DataObj: IDataObject;
   var Accept:boolean);
var  FE:TFormatEtc;
begin
     with FE do
     begin
          cfFormat:=CF_SHELLURL;
          ptd:=nil;
          dwAspect:=DVASPECT_CONTENT;
          lindex:=-1;
          tymed:=TYMED_HGLOBAL;
     end;
     Accept:=DataObj.QueryGetData(FE)=S_OK;
end;

procedure TDropTargetURL.RenderDropped(DataObj: IDataObject; grfKeyState: Longint;
   pt: TPoint; var dwEffect: longint); 
var FE: TFormatEtc;
    SM: TStgMedium;
    DataPtr: pchar;
begin
     with FE do
     begin
          cfFormat:=CF_SHELLURL;
          ptd:=nil;
          dwAspect:=DVASPECT_CONTENT;
          lindex:=-1;
          tymed:=TYMED_HGLOBAL;
     end;
     if DataObj.GetData(FE,SM)=S_Ok then
     begin
          try
             DataPtr:=GlobalLock(SM.HGlobal);
             TDragDropURL(FOwner).FURL:=StrPas(DataPtr);
          finally
             GlobalUnLock(SM.HGlobal);
             ReleaseStgMedium(SM);
          end;
     end;
end;

// TDragDropURL ---------------------------------------------------------------

constructor TDragDropURL.Create(AOwner: TComponent);
begin
     inherited Create(AOwner);
     FURL:='';
     FScrapFileName:='';
     FDropTarget._Release;
     FDropTarget:=TDropTargetURL.Create(self);
end;

destructor TDragDropURL.Destroy;
begin
     inherited destroy;
end;

function TDragDropURL.CreateDataObject:TDataObject;
begin
     if FURL<>'' then Result:=TDataObjectURL.Create(FScrapFileName,FURL,true)
     else Result:=nil;
end;

// Register Component ----------------------------------------------------------

procedure Register;
begin
  {MP}RegisterComponents({'Shell32'}'DragDrop', [TDragDropURL]);
end;

end.
