/**************************************************************************
 *  File name  :  dragdrag.c
 *
 *  Description:  This source file contains the direct manipulation routines
 *		  of the Drag/drop sample.
 *
 *		  This source file contains the following functions:
 *
 *		  FreeSourceResources (hab, pDraginfo)
 *		  PreventClose (hwndClient, cDrag)
 *		  RemoveFinalBackslash (p)
 *		  InitDrag(hwndClient, hList, hptrFile, hptrFolder, hptrMulti,
 *			   pContainerName)
 *		  EndConversation (hwndClient, mp1)
 *		  DragLeave (void)
 *		  DropHelp (hwndClient, pDraginfo)
 *		  DragOver (pDraginfo, pCurrentDir)
 *		  Drop (hab, hwndClient, pDraginfo, pCurrentDir)
 *		  EndTarget (hwnd, pParm)
 *		  CheckBusy (void)
 *		  CheckClose (hwnd)
 *		  TargetThread (void)
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  DrgAccessDraginfo
 *		  DrgDeleteDraginfoStrHandles
 *		  DrgQueryDragitemCount
 *		  DrgQueryDragitemPtr
 *		  DrgFreeDraginfo
 *		  WinWindowFromID
 *		  WinSendMsg
 *		  DrgAllocDraginfo
 *		  DrgAddStrHandle
 *		  WinQueryLboxItemText
 *		  DrgSetDragitem
 *		  DrgDrag
 *		  WinDeleteLboxItem
 *		  DrgQueryStrName
 *		  DrgVerifyRMF
 *		  DrgAllocDragtransfer
 *		  DrgSendTransferMsg
 *		  WinStartTimer
 *		  WinGetMsg
 *		  WinStopTimer
 *		  WinDispatchMsg
 *		  DrgFreeDragtransfer
 *		  DosGetNamedSharedMem
 *		  DosSubAllocMem
 *		  DosCreateThread
 *		  DrgQueryDragitem
 *		  WinPostMsg
 *		  DosCopy
 *		  DosDelete
 *		  DosEnterCritSec
 *		  WinGetLastError
 *
 *  Required
 *    Files    :  OS2.H, STRING.H, DRAGDROP.H
 *
 *  Copyright (C) 1991 IBM Corporation
 *
 *	DISCLAIMER OF WARRANTIES.  The following [enclosed] code is
 *	sample code created by IBM Corporation. This sample code is not
 *	part of any standard or IBM product and is provided to you solely
 *	for  the purpose of assisting you in the development of your
 *	applications.  The code is provided "AS IS", without
 *	warranty of any kind.  IBM shall not be liable for any damages
 *	arising out of your use of the sample code, even if they have been
 *	advised of the possibility of such damages.						       *
 *************************************************************************/
/*
 *	  Include the required sections from the PM header file.
 */
#define INCL_DOSFILEMGR
#define INCL_DOSPROCESS
#define INCL_DOSERRORS
#define INCL_SHLERRORS
#define INCL_WIN
#define INCL_WINSTDDRAG

/*
 *		       Include the PM header file.
 */
#include <os2.h>

/*
 *		    Include the C library header files.
 */
#include <string.h>

/*
 *		   Include the application header files.
 */
#include "dragdrop.h"

static PDRAGTARGETTHREAD vpThreadParms = NULL;

/*
 * Variables global to this file
 */
static char   szRendMechFmtFILE []  = "<DRM_OS2FILE,DRF_UNKNOWN>";

/*
 * Because the following items used by the target, are used only
 * during the drag operation, and drag is a system modal operation,
 * a target can always accept a drop.
 *
 * In any case, the target cannot allow the application to
 * close when a drag is in progress, either as a target or a source.
 * This is because the thread performing the operation would also be
 * ended.  A target could allow a close, but only if it prompted
 * the user to request permission.
 */

static USHORT	 usOperation;
      /* usOperation is used to remember the user selected
       * (or application selected default * operation) between
       * a DM_DRAGOVER
       * and a DM_DROP message, to avoid re-figuring the operation.
       * Because this is used during drag, which is a system modal
       * operation, a target can always be dropped on.
       * Used by target only.
       */

/*
 * The following is used by both the source and target to keep the total
 * number of drag operations that the application is currently involved
 * in. If the count is non-zero, the application will not allow the user
 * to close it.
 */
static ULONG	ulDragCount = 0L;

/**************************************************************************
 *
 *  Name       : FreeSourceResources (hab, pDraginfo)
 *
 *  Description: Frees the resources allocated by the source for a drag
 *		 operation
 *
 *  Concepts   :  accesses and deletes an instance of drag information
 *
 *  API's      :  DrgAccessDraginfo
 *		  DrgDeleteDraginfoStrHandles
 *		  DrgQueryDragitemCount
 *		  DrgQueryDragitemPtr
 *		  DrgFreeDraginfo
 *
 *  Parameters :  pDraginfo = pointer to the drag information structure
 *
 *  Return     :  TRUE	= if resources freed successfully
 *		  FALSE = unable to free all resources
 *
 *************************************************************************/
BOOL FreeSourceResources (HAB hab, PDRAGINFO pDraginfo)
{
   PDRAGITEM	   pDragitem;
   ULONG	   ulItemsDragged, i;
   ERRORID	   errorid;

   if(DrgAccessDraginfo(pDraginfo) && DrgDeleteDraginfoStrHandles(pDraginfo))
      ;		  /* drag access OK */
   else
   {
      MessageBox(HWND_DESKTOP, IDMSG_DRAGACCESSERROR, MB_OK | MB_ERROR, TRUE);
      return FALSE;
   }

   ulItemsDragged = DrgQueryDragitemCount(pDraginfo);

   for (i = 0L; i < ulItemsDragged; ++i)
   {
      pDragitem = DrgQueryDragitemPtr(pDraginfo, i);
      if(!FreeAppDragitem((PAPPDRAGITEM)pDragitem->ulItemID))
      {
	 MessageBox(HWND_DESKTOP, IDMSG_MEMFREEERROR, MB_OK | MB_ERROR, TRUE);
	 return FALSE;
      }
   }

   if(!FreeAppDraginfo(FindAppDraginfo(pDraginfo)))
   {
      MessageBox(HWND_DESKTOP, IDMSG_MEMFREEERROR, MB_OK | MB_ERROR, TRUE);
      return FALSE;
   }
   if(DrgFreeDraginfo(pDraginfo)
	       || PMERR_SOURCE_SAME_AS_TARGET == WinGetLastError(hab))
      return TRUE;
   else
   {
      MessageBox(HWND_DESKTOP, IDMSG_DRAGFREEERROR, MB_OK | MB_ERROR, TRUE);
      return FALSE;
   }
}  /* End of FreeSourceResources  */


/**************************************************************************
 *
 *  Name       : PreventClose (hwndClient, cDrag)
 *
 *  Description: Disables the close option on the system menu when
 *		 a drag is in progress, or enables it when all drag
 *		 operations have completed.
 *
 *  Concepts   :  system menu, enablement
 *
 *  API's      :  WinWindowFromID
 *		  WinSendMsg
 *
 *  Parameters :  hwndClient = client window handlenformation structure
 *		  cDrag	     = the enabled state of the close menu item
 *
 *  Return     :  TRUE	= if the close menu item is disabled
 *		  FALSE = if the close menu item has been enabled
 *
 *************************************************************************/
VOID PreventClose(HWND hwndClient, ULONG cDrag)
{
   HWND hwndSysMenu = WinWindowFromID(ParentOf(hwndClient), FID_SYSMENU);

   WinSendMsg(hwndSysMenu, MM_SETITEMATTR, MPFROM2SHORT(SC_CLOSE, (SHORT)TRUE),
	     MPFROM2SHORT(MIA_DISABLED, (SHORT)(cDrag ? MIA_DISABLED : FALSE)));

   return;
}  /*  End of PreventClose  */


/**************************************************************************
 *
 *  Name       : RemoveFinalBackslash (p)
 *
 *  Description: Checks the final character in a string and,
 *		 if it is a backslash, removes it.
 *
 *  Concepts   :  pathnames
 *
 *  API's      :  [none]
 *
 *  Parameters :  p = a pointer to the string
 *
 *  Return     :  [none]
 *
 *************************************************************************/
VOID RemoveFinalBackslash (PSZ p)
{
   for (; *p; ++p)
      ;			 /*  go to the end of the string  */

   if (*--p == '\\')
   {
      *p = '\0';
   }
}  /*  End of RemoveFinalBackslash  */


/**************************************************************************
 *
 *  Name       : InitDrag(hwndClient, hList, hptrFile, hptrFolder,
 *			  hptrMulti, pContainerName)
 *
 *  Description: Builds a DRAGINFO structure for the selected items,
 *		 then calls DrgDrag to perform the drag operation.
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  DrgAllocDraginfo
 *		  DrgAddStrHandle
 *		  WinQueryLboxItemText
 *		  DrgSetDragitem
 *
 *  Parameters :  hwndClient = client window handle
 *		  hList	     = handle of the list of file names
 *		  hptrFile   = file handle
 *		  hptrFolder = folder handle
 *		  hptrMulti  = multiple list handle
 *		  pContainerName - pointer to name of container
 *
 *  Return     :  TRUE	= the drag operation was performed
 *		  FALSE = unable to perform the drag operation
 *
 *************************************************************************/
BOOL InitDrag (HAB hab, HWND hwndClient, HWND hList, HPOINTER hptrFile,
	       HPOINTER hptrFolder, HPOINTER hptrMulti, PSZ pContainerName)
{
   CHAR		    chDrive;
   SHORT	    item;
   ULONG	    i, ulItemsDragged;
   HWND		    hwndTarget;
   LHANDLE	    hImage;
   DRAGITEM	    Dragitem;
   HSTR		    hstrType, hstrRMF, hstrContainerName;
   USHORT	    fsControl;
   USHORT	    fsSupportedOps;
   PPUBFILEFINDBUF  pFile;
   CHAR		    szItemName [64];
   CHAR		    szFullContainerName [CCHMAXPATH];
   PAPPDRAGINFO	    pAppDraginfo;
   PDRAGINFO	    pSourceDraginfo;
   DRAGIMAGE	    dimg;

   /*
    * Determine number of items selected prior to allocating the
    * DRAGINFO structure. Note that this sample program requires at
    * least one item to be selected in order to do a drag operation.
    */

   ulItemsDragged = 0L;
   item = WinQueryLboxSelItemFirst (hList);

   while (item != LIT_NONE)
   {
      ++ulItemsDragged;
      item = WinQueryLboxSelItemNext (hList, item);
   }

   if (ulItemsDragged == 0L)
   {
      MessageBox(HWND_DESKTOP, IDMSG_ATLEASTONE, MB_OK | MB_ERROR, TRUE);
      return FALSE;
   }

   /*
    * Allocate the DRAGINFO structure and the APPDRAGINFO structure.
    */
   pSourceDraginfo = DrgAllocDraginfo(ulItemsDragged);
   pAppDraginfo = AllocAppDraginfo (hwndClient, pSourceDraginfo);

   /*
    * Initialize variable used to initialize the internal DRAGITEM
    * structure.
    */
   hstrType  = DrgAddStrHandle (DRT_UNKNOWN);	    /* file type is unknown  */
   hstrRMF   = DrgAddStrHandle (szRendMechFmtFILE); /* os2file, unknown	     */
   strcpy (szFullContainerName, pContainerName);    /* container name	     */

   RemoveFinalBackslash (szFullContainerName);
   strcat (szFullContainerName, "\\");
   hstrContainerName = DrgAddStrHandle(szFullContainerName);

   fsControl = 0;
   chDrive = (CHAR) pContainerName[0];

   if((chDrive=='A') || (chDrive=='B') || (chDrive=='a') || (chDrive=='b'))
   {	   /* is source container removable media?   */
      fsControl = fsControl | (SHORT)DC_REMOVEABLEMEDIA;
   }

   fsSupportedOps = DO_COPYABLE | DO_MOVEABLE;	  /* can only move or copy */

   /*
    * Initialize common portions of the internal Dragitem buffer.
    */
   Dragitem.hwndItem	      = hwndClient;
   Dragitem.hstrType	      = hstrType;
   Dragitem.hstrRMF	      = hstrRMF;
   Dragitem.hstrContainerName = hstrContainerName;
   Dragitem.fsControl	      = fsControl;
   Dragitem.fsSupportedOps    = fsSupportedOps;

   /*
    * Initialize unique portions of the internal Dragitem buffer for each
    * item that was selected.
    */
   item = LIT_FIRST;

   for (i = 0L; i < ulItemsDragged; ++i)
   {
      item = WinQueryLboxSelItemNext (hList, item);
      WinQueryLboxItemText (hList, item, szItemName, sizeof(szItemName));
      pFile = FindFileName (szItemName);

      if (pFile == NULL)
      {
	 MessageBox(HWND_DESKTOP, IDMSG_CANTLOCATE, MB_OK | MB_ERROR, TRUE);
      }
      else
      {
	 Dragitem.hstrSourceName = DrgAddStrHandle (pFile->achName);
	 Dragitem.hstrTargetName = Dragitem.hstrSourceName;
	 Dragitem.ulItemID = (ULONG)AllocAppDragitem(pSourceDraginfo, pFile);
	 if(Dragitem.ulItemID == 0L)
	    return FALSE;
	 if (pFile->attrFile & FILE_DIRECTORY)
	    Dragitem.fsControl = Dragitem.fsControl | (SHORT)DC_CONTAINER;

	 if(!DrgSetDragitem(pSourceDraginfo, &Dragitem, sizeof(DRAGITEM), i))
	 {
	    MessageBox(HWND_DESKTOP, IDMSG_DRAGSETITEMERROR,
		       MB_OK | MB_ERROR, TRUE);
	    return FALSE;
	 }
      }
   }
   /*
    *  Drag the items.
    */
   if (ulItemsDragged == 1L)
      hImage = (Dragitem.fsControl & DC_CONTAINER) ? hptrFolder : hptrFile;
   else
      hImage = hptrMulti;

   ++ulDragCount;		      /* remember drag is in progress	  */
   PreventClose(hwndClient, ulDragCount);

   dimg.cb	 = sizeof (DRAGIMAGE);
   dimg.hImage	 = hImage;
   dimg.fl	 = DRG_ICON | DRG_TRANSPARENT;
   dimg.cxOffset = 0;
   dimg.cyOffset = 0;

   pSourceDraginfo->hwndSource = hwndClient;
   hwndTarget=DrgDrag(hwndClient, pSourceDraginfo, &dimg, 1L, VK_BUTTON2, NULL);

   if (hwndTarget == NULLHANDLE)
   {
     /*
      * User cancelled the drag or asked for help, so we can release
      * the resources allocated to the drag operation.
      */
      FreeSourceResources (hab, pSourceDraginfo);
      --ulDragCount;
      PreventClose (hwndClient, ulDragCount);
   }
   return TRUE;
}     /*  End of InitDrag  */


/**************************************************************************
 *
 *  Name       : EndConversation(hwndClient, mp1)
 *
 *  Description: Decrements ulItemsDragged by one for each
 *		 DM_ENDCONVERSATION received.  When ulItemsDragged
 *		 becomes 0, the DRAGINFO structure is freed.
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  WinWindowFromID
 *		  WinDeleteLboxItem
 *		  DrgFreeDraginfo
 *		  WinGetLastError
 *
 *  Parameters :  hwndClient = client window handle
 *		  mp1	     = drag item ID
 *		  mp2	     = render success flag
 *
 *  Return     :  NULL = successful end to the conversation
 *
 *************************************************************************/
MRESULT EndConversation(HAB hab, HWND hwndClient, MPARAM mp1, MPARAM mp2)
{
   PAPPDRAGITEM pAppDragitem = (PAPPDRAGITEM)mp1;
   PAPPDRAGINFO pAppDraginfo;
   USHORT	i;
   HWND		hwndLbox = WinWindowFromID (hwndClient, LIST_ID);

  /*
   *   Did the target properly render the item?
   */
   if(mp2 == (MPARAM)DMFL_TARGETFAIL)
      MessageBox(HWND_DESKTOP, IDMSG_TARGETRENDER, MB_OK | MB_ERROR, TRUE);
  /*
   * delete the item from the listbox if the operation was a move
   */
   if (pAppDragitem->pDraginfo->usOperation == (USHORT)DO_MOVE)
   {
      i = WinFindLboxItem (hwndLbox, pAppDragitem->pFile->achName);
      WinDeleteLboxItem (hwndLbox, i);
   }

   pAppDraginfo = FindAppDraginfo(FindDraginfo(pAppDragitem));
   if(!pAppDraginfo)
      MessageBox(HWND_DESKTOP, IDMSG_FINDAPPDRAGINFO, MB_OK | MB_ERROR, TRUE);
   FreeAppDragitem(pAppDragitem);

   if (--pAppDraginfo->usItemsDragged == 0)
   {
      if(DrgFreeDraginfo(pAppDraginfo->pDraginfo)
	       || PMERR_SOURCE_SAME_AS_TARGET == WinGetLastError(hab))
      {
	 if(!FreeAppDraginfo(pAppDraginfo))
	    MessageBox(HWND_DESKTOP, IDMSG_MEMFREEERROR, MB_OK | MB_ERROR,TRUE);
	 else
	 {
	    --ulDragCount;		     /* indicate drag completed */
	    PreventClose(hwndClient, ulDragCount);
	 }
      }
      else
	 MessageBox(HWND_DESKTOP, IDMSG_DRAGFREEERROR, MB_OK | MB_ERROR, TRUE);
   }
   return (MRESULT)NULL;
}  /*  End of EndConversation  */


/**************************************************************************
 *
 *  Name       : DragLeave()
 *
 *  Description: Called when a LN_DRAGLEAVE message is received.
 *		 As this application does not do target emphasis,
 *		 this function simply returns NULL.
 *
 *  Concepts   :  target emphasis
 *
 *  API's      :  [none]
 *
 *  Parameters :  [none]
 *
 *  Return     :  NULL = successful end
 *
 *************************************************************************/
MRESULT DragLeave (void)
{
   return (MRESULT)NULL;
}  /*  End of DragLeave	 */

/**************************************************************************
 *
 *  Name       : DropHelp(hwndClient, pDraginfo)
 *
 *  Description: Issues a message to notify the user what would happen
 *		 if mouse button 2 were released, based on the current
 *		 operation.
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  DrgAccessDraginfo
 *		  DrgDeleteDraginfoStrHandles
 *		  DrgFreeDraginfo
 *
 *  Parameters :  hwndClient = client window handle
 *		  pDraginfo  = pointer to drag information structure
 *
 *  Return     :  NULL = successful
 *
 *************************************************************************/
MRESULT DropHelp (HWND hwndClient, PDRAGINFO pDraginfo)
{
   char message[81];

   if(!DrgAccessDraginfo (pDraginfo))
   {
      MessageBox(HWND_DESKTOP, IDMSG_DRAGACCESSERROR, MB_OK | MB_ERROR, TRUE);
   }
   strcpy (message, "Dropping here would perform ");

   switch (pDraginfo->usOperation)
   {
   case DO_DEFAULT:
      strcat (message, "the default ");
      break;
   case DO_COPY:
      strcat (message, "a copy ");
      break;
   case DO_MOVE:
      strcat (message, "a move ");
      break;
   case DO_UNKNOWN:
      strcat (message, "an unknown (private) ");
      break;
   }

   strcat (message, "operation.");
   Message (message);

   if(DrgDeleteDraginfoStrHandles(pDraginfo) && DrgFreeDraginfo(pDraginfo))
      ;	       /*  free OK  */
   else
   {
      MessageBox(HWND_DESKTOP, IDMSG_DRAGERROR, MB_OK | MB_ERROR, TRUE);
   }
   hwndClient = hwndClient;	   /* unreferenced formal parameter */
   return (MRESULT)NULL;
}  /*  End of DropHelp	*/


/**************************************************************************
 *
 *  Name       : DragOver(pDraginfo, pCurrentDir)
 *
 *  Description: Provides visual feedback to the user as to whether it
 *		 can support the drag operation
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  DrgAccessDraginfo
 *		  DrgFreeDraginfo
 *		  DrgQueryDragitemPtr
 *		  DrgQueryStrName
 *		  DrgQueryDragitemCount
 *		  DrgVerifyRMF
 *		  WinGetLastError
 *
 *  Parameters :  pDraginfo  = pointer to drag information structure
 *		  pCurrentDir= the name of the current directory
 *
 *  Return     :  DOR_DROP, DO_MOVE for a default operation
 *
 *************************************************************************/
MRESULT DragOver(HAB hab, PDRAGINFO pDraginfo, PSZ pCurrentDir)
{
   USHORT	 usIndicator, cItems, i;
   ULONG	 ulBytesWritten;
   PDRAGITEM	 pditem;
   CHAR		 SourceDir [CCHMAXPATH], chDrive;

  /*
   * Get access to the DRAGINFO structure.
   */
   if(!DrgAccessDraginfo(pDraginfo))
   {
      MessageBox(HWND_DESKTOP, IDMSG_DRAGACCESSERROR, MB_OK | MB_ERROR, TRUE);
      return (MRFROM2SHORT (DOR_NODROPOP, 0));
   }
   usOperation = (USHORT)DO_MOVE;	   /* the default operation is move */

  /*
   * Determine if a drop can be accepted.
   */
   switch (pDraginfo->usOperation)
   {
    /*
     * return DOR_NODROPOP if current operation is link or unknown.
     */
   case DO_UNKNOWN:
      if(!DrgFreeDraginfo (pDraginfo))
	 MessageBox(HWND_DESKTOP, IDMSG_DRAGFREEERROR, MB_OK | MB_ERROR, TRUE);
      return (MRFROM2SHORT (DOR_NODROPOP, 0));

    /*
     * Determine default operation if current operation is default.
     * The default is move unless either the source or the target is
     * on different removable media.
     */
   case DO_DEFAULT:
      pditem = DrgQueryDragitemPtr (pDraginfo, 0);
      ulBytesWritten = DrgQueryStrName(pditem->hstrContainerName,
				       sizeof(SourceDir),
				       SourceDir);
      if(ulBytesWritten == 0L)
      {
	 MessageBox(HWND_DESKTOP, IDMSG_DRAGQUERYSTRINGERROR,
			  MB_OK | MB_ERROR, TRUE);
	 return (MRFROM2SHORT (DOR_NODROPOP, 0));
      }

	 /*
	  * if the source and target are on the
	  * same drive, the operation is a move
	  */
      if (*pCurrentDir == SourceDir[0])
	 usOperation = (USHORT)DO_MOVE;
	 /*
	  * otherwise, different drives
	  */
      else
      {
	 chDrive = (CHAR)pCurrentDir[0];
	   /*
	    * if the target is A: or B:, or the source
	    * is removeable media, the operation is a copy
	    */
	 if((chDrive == 'A') || (chDrive == 'B') || (chDrive == 'a') ||
	    (chDrive=='b') || pditem->fsControl & DC_REMOVEABLEMEDIA)
	 {
	    usOperation = (USHORT)DO_COPY;
	 }
	   /*
	    * otherwise, the default operation is a move
	    */
	 else
	 {
	    usOperation = (USHORT)DO_MOVE;
	 }
      }
      break;

   case DO_MOVE:
   case DO_COPY:
      usOperation = pDraginfo->usOperation;
      break;
   }

  /*
   * In order to support the operation, the source must support the
   * operation this target has decided upon.  The source must also
   * support a rendering mechanism of <DRM_OS2FILE,DRF_UNKNOWN>.
   * This target doesn't care about the file type.
   */

   usIndicator = DOR_DROP;
   cItems = DrgQueryDragitemCount (pDraginfo);

   /*
    * inspect each item to see if it is acceptable
    */
   for (i = 0; i < cItems; ++i)
   {
      pditem = DrgQueryDragitemPtr (pDraginfo, i);

     /*
      * the item is acceptable only if it is copyable and the
      * operation is a copy, or it is moveable and the operation
      * is a move, and it can render <DRM_OS2FILE,DRF_UNKNOWN>
      */
      if (((pditem->fsSupportedOps & DO_COPYABLE) &&
			(usOperation == (USHORT)DO_COPY)) ||
	  ((pditem->fsSupportedOps & DO_MOVEABLE) &&
			  (usOperation == (USHORT)DO_MOVE)))
      {
	 if (DrgVerifyRMF(pditem, "DRM_OS2FILE", "DRF_UNKNOWN"))
	 {
	    usIndicator = DOR_DROP;
	 }
	 else
	 {
	    usIndicator = DOR_NEVERDROP;
	    break;
	 }			      /* endif DrgVerifyRendMechFmt */
      }
      else
      {
	 usIndicator = DOR_NODROPOP;
	 break;
      }					    /* endif supported ops	  */
   }					   /* end for all items dragged	 */

   if(!DrgFreeDraginfo(pDraginfo))
      if(PMERR_SOURCE_SAME_AS_TARGET != WinGetLastError(hab))
      {
	 MessageBox(HWND_DESKTOP, IDMSG_DRAGFREEERROR, MB_OK | MB_ERROR, TRUE);
	 return (MRFROM2SHORT (DOR_NODROPOP, 0));
      }
   return (MRFROM2SHORT(usIndicator, usOperation));
}  /*  End of DragOver	*/


/**************************************************************************
 *
 *  Name       :  Drop (hwndClient, pDraginfo, pCurrentDir)
 *
 *  Description:  This function handles the drop operation
 *
 *		  Since we would not receive a DM_DROP message unless a
 *		  DOR_DROP reply had been given for a DM_DRAGOVER, we
 *		  can assume we have successfully validated that we can
 *		  accept the drop. Note that drag would havesent another
 *		  DM_DRAGOVER if the operation had changed.
 *
 *		  Therefore we don't have to re-validate the operation.
 *		  We can go ahead and perform the operation. We will
 *		  perform each copy in sequence, but will not capture
 *		  the mouse. This should allow the user to perform
 *		  other operations, but not require this application
 *		  to start another thread and coordinate the ending of
 *		  the conversation and freeing up of resources. The user
 *		  will not be able to perform any other operations with
 *		  this application while the operations are in progress.
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  DrgAccessDraginfo
 *		  DrgQueryDragitemPtr
 *		  DrgQueryStrName
 *		  DrgAddStrHandle
 *		  DrgSendTransferMsg
 *		  WinStartTimer
 *		  WinGetMsg
 *		  WinStopTimer
 *		  WinDispatchMsg
 *		  DrgFreeDragtransfer
 *		  DosGetNamedSharedMem
 *		  DrgDeleteDraginfoStrHandles
 *		  DrgFreeDraginfo
 *		  DosSubAllocMem
 *		  DosCreateThread
 *
 *  Parameters :  hwndClient = client window handle
 *		  pDraginfo  = ointer to a drag information structure
 *		  pCurrentDir= the name of the current directory
 *
 *  Return     :  NULL = successful
 *
 *************************************************************************/
MRESULT Drop(HAB hab, HWND hwndClient, PDRAGINFO pDraginfo, PSZ pCurrentDir)
{
   PDRAGTARGETTHREAD	 pParm;
   PBYTE	  aStack;
   TID		 tid;
   USHORT	 i;
   PDRAGTRANSFER pdxfer;
   PDRAGITEM	 pditem;
   HSTR		 hstrRMF;
   char		 szFullTargetName  [CCHMAXPATH];
   char		 szTargetFile [CCHMAXPATHCOMP];
   QMSG		 qmsg;
   USHORT	 cRetry, idTimer;
   ULONG	 mr, ulBytesWritten;

   /*
    * Get access to the Draginfo structure.
    */
   if(!DrgAccessDraginfo (pDraginfo))
      MessageBox(HWND_DESKTOP, IDMSG_DRAGACCESSERROR, MB_OK | MB_ERROR, TRUE);
   if (pDraginfo->hwndSource == hwndClient)
   {
     /*
      * Source window and target window are the same, so don't do the
      * operation... just release the resources allocated for the drag.
      */
      FreeSourceResources (hab, pDraginfo);
      --ulDragCount;
      PreventClose (hwndClient, ulDragCount);
      return (MRESULT)NULL;
   }

   pditem = DrgQueryDragitemPtr (pDraginfo, 0);

   /*
    * if the source name wasn't provided, let the source render
    */
   if (!pditem->hstrSourceName)
   {
      pdxfer   = DrgAllocDragtransfer (pDraginfo->cditem);
      hstrRMF  = DrgAddStrHandle ("<DRM_OS2FILE, DRF_UNKNOWN>");

      for (i=0; i<pDraginfo->cditem; i++)
      {
	 pditem = DrgQueryDragitemPtr (pDraginfo, i);

       /*
	* Build the fully qualified target file name.
	*/
	 strcpy (szFullTargetName, pCurrentDir);
	 RemoveFinalBackslash (szFullTargetName);
	 ulBytesWritten =DrgQueryStrName(pditem->hstrTargetName,
					 sizeof(szTargetFile),
					 szTargetFile);
	 if(ulBytesWritten == 0L)
	    MessageBox(HWND_DESKTOP, IDMSG_DRAGQUERYSTRINGERROR,
			   MB_OK | MB_ERROR, TRUE);

	 if (szTargetFile[0] != '\\')
	    strcat(szFullTargetName, "\\");
	 strcat(szFullTargetName, szTargetFile);

       /*
	* initialize the DRAGTRANSFER
	*/
	 pdxfer[i].cb		    = sizeof (DRAGTRANSFER);
	 pdxfer[i].hwndClient	    = hwndClient;
	 pdxfer[i].pditem	    = pditem;
	 pdxfer[i].hstrSelectedRMF  = hstrRMF;
	 pdxfer[i].hstrRenderToName = DrgAddStrHandle (szFullTargetName);
	 pdxfer[i].ulTargetInfo	    = 0L;
	 pdxfer[i].usOperation	    = pDraginfo->usOperation;
	 pdxfer[i].fsReply	    = 0;

	 /*
	  * send the DM_RENDER to the source, retrying as necessary
	  */
	 for (cRetry=0, mr=0; cRetry<MAX_RETRIES && !mr; cRetry++)
	 {
	    mr = (ULONG)DrgSendTransferMsg(pDraginfo->hwndSource,
					   DM_RENDER,
					   (MPARAM)(pdxfer + i),
					   (MPARAM)NULL);

	   /*
	    * if the source didn't render, retry if we can
	    */
	    if (!mr)
	    {
	       if (pdxfer[i].fsReply & DMFL_RENDERRETRY)
	       {
		  idTimer = WinStartTimer (0L, 0L, 0L, 1000L);

	       /*
		* start a timer to wait, but process
		* messages so we don't hang the queue
		*/
		  while (WinGetMsg (0L, &qmsg, 0L, 0, 0))
		  {
		     if ((qmsg.msg == WM_TIMER) &&
			       ((USHORT) qmsg.mp1 == idTimer))
		     {
			WinStopTimer (0L, 0L, idTimer);
			break;
		     }
		     else
			WinDispatchMsg (0L, &qmsg);
		  }
	       }
	       else
		  DrgFreeDragtransfer(pdxfer + i);
	    }
	 }
      }
   }

   /*
    * Allocate the stack segment for the target thread.
    */
   else if (DosGetNamedSharedMem((PPVOID)&aStack, (PSZ)"\\sharemem\\dragmem",
				 PAG_READ | PAG_WRITE) != 0LU)
   {
      MessageBox(HWND_DESKTOP, IDMSG_CANTCREATESTACK, MB_OK | MB_ERROR, TRUE);
      DrgDeleteDraginfoStrHandles(pDraginfo);
      DrgFreeDraginfo(pDraginfo);
   }
   else
   {
     /*
      * Build the parameter list structure for the target thread.
      */
      DosSubAllocMem(aStack, (PPVOID)&pParm, sizeof(DRAGTARGETTHREAD));

      pParm->pDraginfo	 = pDraginfo;
      pParm->hwndClient	 = hwndClient;
      pParm->usOperation = usOperation;
      strcpy(pParm->szTargetDir, pCurrentDir);

     /*
      * Remember that a drag is in progress and start the target thread.
      */
      ++ulDragCount;
      PreventClose (hwndClient, ulDragCount);

      vpThreadParms = pParm;

      DosCreateThread(&tid, (PFNTHREAD)TargetThread,
		      (ULONG)vpThreadParms, 0U, STACKSIZE);
   }

   return (MRESULT)NULL;
}    /*	 End of Drop  */


/**************************************************************************
 *
 *  Name       :  EndTarget(hwnd, pParm)
 *
 *  Description: This function is called whenever a WM_TARGETCOMPLETE
 *		 message is received. It releases all resources used
 *		 during the drag operation and remembers that the drag
 *		 operation has completed.
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  DrgQueryDragitemCount
 *		  DrgQueryDragitem
 *		  DrgSendTransferMsg
 *		  DrgDeleteDraginfoStrHandles
 *		  DrgFreeDraginfo
 *		  WinPostMsg
 *
 *  Parameters :  hwnd	     = window handle
 *		  pParm	     = points to drag information
 *
 *  Return     :  NULL = successful
 *
 *************************************************************************/
MRESULT EndTarget (HWND hwnd, PDRAGTARGETTHREAD pParm)
{
   USHORT		 cItems, i;
   DRAGITEM		 Dragitem;

  /*
   * Decrement the count of drag operations in progress and prepare to
   * release the resources allocated for the drag operation.
   */
   --ulDragCount;
   PreventClose (hwnd, ulDragCount);
   cItems = DrgQueryDragitemCount (pParm->pDraginfo);

  /*
   * End the conversation for each item in the DRAGINFO structure and
   * let source know we have completed the drag operation for each item.
   */
   for (i = 0; i < cItems; ++i)
   {
      if(!DrgQueryDragitem (pParm->pDraginfo, sizeof(Dragitem), &Dragitem, i))
	 MessageBox(HWND_DESKTOP, IDMSG_DRAGQUERYITEMERROR,
		    MB_OK | MB_ERROR, TRUE);
      DrgSendTransferMsg(Dragitem.hwndItem, DM_ENDCONVERSATION,
			 MPFROMLONG(Dragitem.ulItemID),
			 MPFROMLONG(DMFL_TARGETSUCCESSFUL));
   }
  /*
   * Free the draginfo structure.
   */
   if(DrgDeleteDraginfoStrHandles(pParm->pDraginfo)
		 && DrgFreeDraginfo(pParm->pDraginfo))
      ;
   else
      MessageBox(HWND_DESKTOP, IDMSG_DRAGERROR, MB_OK | MB_ERROR, TRUE);
   WinPostMsg(hwnd, WM_REFRESH, NULL, NULL);
   return (MRESULT)NULL;
}  /*  End of EndTarget	 */


/**************************************************************************
 *
 *  Name       :  CheckBusy()
 *
 *  Description:  This call determines if a drag is in progress.
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  [none]
 *
 *  Parameters :  [none]
 *
 *  Return     :  TRUE = a drag is in progress
 *		  FALSE = no drag in progress
 *
 *************************************************************************/
BOOL CheckBusy (void)
{
   return ((BOOL) ulDragCount);
}  /*  End of CheckBusy	 */


/**************************************************************************
 *
 *  Name       :  CheckClose(hwnd)
 *
 *  Description:  This call determines if a drag is in progress by
 *		  checking ulDragCount.	 If a drag is in progress,
 *		  close is not allowed.
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  WinPostMsg
 *
 *  Parameters :  hwnd = window handle
 *
 *  Return     :  TRUE = a drag is in progress
 *		  FALSE = no drag in progress
 *
 *************************************************************************/
MRESULT CheckClose (HWND hwnd)
{
   if (!ulDragCount)
      WinPostMsg (hwnd, WM_QUIT, (MPARAM)0L, (MPARAM)0L);

   return ((MPARAM)NULL);
}  /*  End of CheckClose  */

/**************************************************************************
 *
 *  Name       :  TargetThread()
 *
 *  Description:  This is the main function of this thread.
 *
 *  Concepts   :  direct manipulation
 *
 *  API's      :  DrgQueryDragitemCount
 *		  DrgQueryDragitem
 *		  DrgQueryStrName
 *		  DosCopy
 *		  DosDelete
 *		  DosEnterCritSec
 *
 *  Parameters :  [none]
 *
 *  Return     :  [none]
 *
 *************************************************************************/
VOID FAR TargetThread (void)
{
   PDRAGTARGETTHREAD pParm;
   USHORT	 cItems, i;
   ULONG	 ulBytesWritten;
   DRAGITEM	 Dragitem;
   char		 szFullSourceName  [CCHMAXPATH];
   char		 szFullTargetName  [CCHMAXPATH];
   char		 szSourceFile [CCHMAXPATHCOMP];
   char		 szTargetFile [CCHMAXPATHCOMP];

   pParm = vpThreadParms;

  /*
   * Get the number of items being dragged.
   */

   cItems = DrgQueryDragitemCount (pParm->pDraginfo);

  /*
   * If the source has provided the file name in the DRAGITEM
   * structures, we can go ahead and do a target copy without getting
   * the source involved in the operation.  We will always copy the file
   * using the suggested target name.  In this application the names
   * are always the same.
   */

   for (i = 0; i < cItems; ++i)
   {
      if(!DrgQueryDragitem (pParm->pDraginfo, sizeof(Dragitem), &Dragitem, i))
	 MessageBox(HWND_DESKTOP, IDMSG_DRAGQUERYITEMERROR,
		      MB_OK | MB_ERROR, TRUE);
      if(Dragitem.hstrSourceName    != 0L &&
	 Dragitem.hstrTargetName    != 0L &&
	 Dragitem.hstrContainerName != 0L)
      {
      /*
       * Build the fully qualified source file name.
       */
	 ulBytesWritten = DrgQueryStrName(Dragitem.hstrContainerName,
					  sizeof(szFullSourceName),
					  szFullSourceName);
	 if(ulBytesWritten == 0)
	    MessageBox(HWND_DESKTOP, IDMSG_DRAGQUERYSTRINGERROR,
		       MB_OK | MB_ERROR, TRUE);
	 ulBytesWritten = DrgQueryStrName(Dragitem.hstrSourceName,
					  sizeof(szSourceFile),
					  szSourceFile);
	 if(ulBytesWritten == 0)
	    MessageBox(HWND_DESKTOP, IDMSG_DRAGQUERYSTRINGERROR,
			MB_OK | MB_ERROR, TRUE);

	 if (szSourceFile[0] == '\\')
	    strcpy (szSourceFile, szSourceFile+1);

	 strcat (szFullSourceName, szSourceFile);

      /*
       * Build the fully qualified target file name.
       */
	 strcpy (szFullTargetName, pParm->szTargetDir);
	 RemoveFinalBackslash (szFullTargetName);
	 ulBytesWritten = DrgQueryStrName(Dragitem.hstrTargetName,
					  sizeof(szTargetFile),
					  szTargetFile);
	 if(ulBytesWritten == 0)
	    MessageBox(HWND_DESKTOP, IDMSG_DRAGQUERYSTRINGERROR,
			   MB_OK | MB_ERROR, TRUE);

	 if (szTargetFile[0] != '\\')
	    strcat (szFullTargetName, "\\");

	 strcat (szFullTargetName, szTargetFile);

      /*
       * Do the operation.  DosCopy works with different drives;
       * DosMove does not, so we will use DosCopy and DosDelete.
       */
	 switch (pParm->usOperation)
	 {
	 case DO_MOVE:	   /*  Mimic DosMove with DosCopy and DosDelete.  */
	     if(DosCopy(szFullSourceName, szFullTargetName, DCPY_EXISTING)
			  == NO_ERROR)
		DosDelete(szFullSourceName);
	     break;
	 case DO_COPY:
	     DosCopy (szFullSourceName, szFullTargetName, DCPY_EXISTING);
	     break;
	 }			      /* end switch usOperation */
      }				      /* end of target copy	*/
   }				      /* end of dragitem loop	*/

  /*
   * Post ourselves a target complete message so the client window
   * knows we are done and can free the stack segment and the draginfo
   * structure.
   */
   DosEnterCritSec();			/* make sure we finish before the   */
					/* main thread frees our stack	    */

   WinPostMsg (pParm->hwndClient, WM_TARGETCOMPLETE, pParm, NULL);
}  /*  End of TargetThread  */
/***************************  End of dragdrag.c	 *************************/

