home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / cnradv.zip / DRAG.C < prev    next >
C/C++ Source or Header  |  1993-07-19  |  61KB  |  1,382 lines

  1. /*********************************************************************
  2.  *----------------------------- DRAG.C ------------------------------*
  3.  *                                                                   *
  4.  * MODULE NAME :  drag.c                 AUTHOR:  Rick Fishman       *
  5.  * DATE WRITTEN:  12-05-92                                           *
  6.  *                                                                   *
  7.  * DESCRIPTION:                                                      *
  8.  *                                                                   *
  9.  *  This module is part of CNRADV.EXE. It contains the functions     *
  10.  *  necessary to implement container drag/drop.                      *
  11.  *                                                                   *
  12.  * CALLABLE FUNCTIONS:                                               *
  13.  *                                                                   *
  14.  *  MRESULT DragMessage( HWND hwndClient, ULONG msg, MPARAM mp1,     *
  15.  *                       MPARAM mp2 );                               *
  16.  *  VOID    DragInit( HWND hwndClient, PCNRDRAGINIT pcdi );          *
  17.  *  MRESULT DragOver( HWND hwndClient, PCNRDRAGINFO pcdi );          *
  18.  *  VOID    DragDrop( HWND hwndClient, PCNRDRAGINFO pcdi );          *
  19.  *                                                                   *
  20.  * HISTORY:                                                          *
  21.  *                                                                   *
  22.  *  12-05-92 - Program coded.                                        *
  23.  *  01-21-93 - Only do DrgFreeDraginfo if DrgDrag returns NULL.      *
  24.  *  07-06-93 - Added DM_ENDCONVERSATION processing.                  *
  25.  *  07-19-93 - Took out the hptrFolder logic since OS/2 2.1 fixed    *
  26.  *             the WinLoadFileIcon bug that didn't load the folder   *
  27.  *             icon for directories.                                 *
  28.  *                                                                   *
  29.  *  Rick Fishman                                                     *
  30.  *  Code Blazers, Inc.                                               *
  31.  *  4113 Apricot                                                     *
  32.  *  Irvine, CA. 92720                                                *
  33.  *  CIS ID: 72251,750                                                *
  34.  *                                                                   *
  35.  *                                                                   *
  36.  *********************************************************************/
  37.  
  38. /*********************************************************************/
  39. /*------- Include relevant sections of the OS/2 header files --------*/
  40. /*********************************************************************/
  41.  
  42. #define  INCL_DEV
  43. #define  INCL_DOSFILEMGR
  44. #define  INCL_DOSPROCESS
  45. #define  INCL_SHLERRORS
  46. #define  INCL_WINDIALOGS
  47. #define  INCL_WINERRORS
  48. #define  INCL_WINFRAMEMGR
  49. #define  INCL_WININPUT
  50. #define  INCL_WINMENUS
  51. #define  INCL_WINPOINTERS
  52. #define  INCL_WINSTDCNR
  53. #define  INCL_WINSTDDLGS
  54. #define  INCL_WINSTDDRAG
  55. #define  INCL_WINSYS
  56. #define  INCL_WINWINDOWMGR
  57.  
  58. /**********************************************************************/
  59. /*----------------------------- INCLUDES -----------------------------*/
  60. /**********************************************************************/
  61.  
  62. #include <os2.h>
  63. #include <stdarg.h>
  64. #include <stdio.h>
  65. #include <stdlib.h>
  66. #include <string.h>
  67. #include "cnradv.h"
  68.  
  69. /*********************************************************************/
  70. /*------------------- APPLICATION DEFINITIONS -----------------------*/
  71. /*********************************************************************/
  72.  
  73. #define DRAG_RMF  "(DRM_OS2FILE,DRM_PRINT,DRM_DISCARD)x(DRF_UNKNOWN)"
  74.  
  75. #define DOC_NAME  "CnrAdv"
  76.  
  77. /**********************************************************************/
  78. /*---------------------------- STRUCTURES ----------------------------*/
  79. /**********************************************************************/
  80.  
  81. /**********************************************************************/
  82. /*----------------------- FUNCTION PROTOTYPES ------------------------*/
  83. /**********************************************************************/
  84.  
  85. static INT     CountSelectedRecs   ( HWND hwndClient, PCNRITEM pciUnderMouse,
  86.                                      PBOOL pfProcessSelected );
  87. static VOID    SetSelectedDragItems( HWND hwndClient, PINSTANCE pi,
  88.                                      INT cRecs, PDRAGINFO pdinfo,
  89.                                      PDRAGIMAGE pdimg );
  90. static VOID    SetOneDragItem      ( HWND hwndClient, PINSTANCE pi,
  91.                                      PCNRITEM pciUnderMouse, PDRAGINFO pdinfo,
  92.                                      PDRAGIMAGE pdimg, INT iOffset );
  93. static VOID    OurOwnDrag          ( HWND hwndClient, PCNRITEM pciUnderMouse,
  94.                                      PDRAGINFO pdinfo );
  95. static VOID    MoveRecords         ( HWND hwndCnr, PCNRITEM pciUnderMouse,
  96.                                      PCNRITEM *apciToMove, USHORT cRecs );
  97. static VOID    SomeoneElsesDrag    ( HWND hwndClient, PCNRITEM pci,
  98.                                      PDRAGINFO pdinfo );
  99. static VOID    FillInNewRecord     ( PCNRITEM pci, PDRAGITEM pditem );
  100. static MRESULT PrintObject         ( HWND hwndClient, PDRAGITEM pdi,
  101.                                      PPRINTDEST ppd );
  102. static VOID    FormatDirListing    ( PSZ pszDirListing, PCNRITEM pci );
  103. static MRESULT DiscardObjects      ( HWND hwndClient, PDRAGINFO pdinfo );
  104. static BOOL    WindowInOurProcess  ( HWND hwndClient, HWND hwndSource );
  105. static VOID    RemoveSourceEmphasis( HWND hwndClient );
  106. static MRESULT EndConversation     ( HWND hwndClient );
  107.  
  108.  
  109. /**********************************************************************/
  110. /*------------------------ GLOBAL VARIABLES --------------------------*/
  111. /**********************************************************************/
  112.  
  113. /**********************************************************************/
  114. /*--------------------------- DragMessage ----------------------------*/
  115. /*                                                                    */
  116. /*  A DM_ MESSAGE WAS RECEIVED BY THE CLIENT WINDOW PROCEDURE.        */
  117. /*                                                                    */
  118. /*  INPUT: client window handle,                                      */
  119. /*         message id,                                                */
  120. /*         mp1 of the message,                                        */
  121. /*         mp2 of the message                                         */
  122. /*                                                                    */
  123. /*  1.                                                                */
  124. /*                                                                    */
  125. /*  OUTPUT: return code of message                                    */
  126. /*                                                                    */
  127. /*--------------------------------------------------------------------*/
  128. /**********************************************************************/
  129. MRESULT DragMessage( HWND hwndClient, ULONG msg, MPARAM mp1, MPARAM mp2 )
  130. {
  131.     switch( msg )
  132.     {
  133.         case DM_PRINTOBJECT:
  134.  
  135.             return PrintObject( hwndClient, (PDRAGITEM) mp1, (PPRINTDEST) mp2 );
  136.  
  137.  
  138.         case DM_DISCARDOBJECT:
  139.  
  140.             return DiscardObjects( hwndClient, (PDRAGINFO) mp1 );
  141.  
  142.  
  143.         case DM_DRAGERROR:
  144.  
  145.             return (MRESULT) DME_IGNOREABORT;
  146.  
  147.  
  148.         case DM_ENDCONVERSATION:
  149.  
  150.             return EndConversation( hwndClient );
  151.  
  152.  
  153.         // These are the rest of the DM_ messages that we don't currently
  154.         // process
  155.  
  156.         case DM_DRAGFILECOMPLETE:
  157.         case DM_DRAGLEAVE:
  158.         case DM_DRAGOVER:
  159.         case DM_DRAGOVERNOTIFY:
  160.         case DM_DROP:
  161.         case DM_DROPHELP:
  162.         case DM_EMPHASIZETARGET:
  163.         case DM_FILERENDERED:
  164.         case DM_RENDER:
  165.         case DM_RENDERCOMPLETE:
  166.         case DM_RENDERFILE:
  167.         case DM_RENDERPREPARE:
  168.  
  169.             return 0;
  170.     }
  171.  
  172.     return 0;
  173. }
  174.  
  175. /**********************************************************************/
  176. /*----------------------------- DragInit -----------------------------*/
  177. /*                                                                    */
  178. /*  PROCESS CN_INITDRAG NOTIFY MESSAGE.                               */
  179. /*                                                                    */
  180. /*  INPUT: client window handle,                                      */
  181. /*         pointer to the CNRDRAGINIT structure                       */
  182. /*                                                                    */
  183. /*  1.                                                                */
  184. /*                                                                    */
  185. /*  OUTPUT: nothing                                                   */
  186. /*                                                                    */
  187. /*--------------------------------------------------------------------*/
  188. /**********************************************************************/
  189. VOID DragInit( HWND hwndClient, PCNRDRAGINIT pcdi )
  190. {
  191.     PCNRITEM    pciUnderMouse = (PCNRITEM) pcdi->pRecord;
  192.     PDRAGIMAGE  pdimg = NULL;
  193.     PDRAGINFO   pdinfo = NULL;
  194.     BOOL        fProcessSelected;
  195.     PINSTANCE   pi = INSTDATA( hwndClient );
  196.     INT         cRecs;
  197.  
  198.     if( !pi )
  199.     {
  200.         Msg( "DragInit cant get Inst data RC(%X)", HWNDERR( hwndClient ) );
  201.  
  202.         return;
  203.     }
  204.  
  205.     // pSavedDragInfo is non-NULL if a drop has not yet completed. In this case
  206.     // we make it easy on ourselves by not allowing another drag. This pointer
  207.     // is freed during DM_ENDCONVERSATION processing.
  208.  
  209.     if( pi->pSavedDragInfo )
  210.     {
  211.         WinAlarm( HWND_DESKTOP, WA_WARNING );
  212.  
  213.         return;
  214.     }
  215.  
  216.     // Count the records that have CRA_SELECTED emphasis. Also return whether
  217.     // or not we should process the CRA_SELECTED records. If the container
  218.     // record under the mouse does not have this emphasis, we shouldn't.
  219.  
  220.     cRecs = CountSelectedRecs( hwndClient, pciUnderMouse, &fProcessSelected );
  221.  
  222.     if( cRecs )
  223.     {
  224.         INT iDragImageArraySize = cRecs * sizeof ( DRAGIMAGE );
  225.  
  226.         // Allocate an array of DRAGIMAGE structures. Each structure contains
  227.         // info about an image that will be under the mouse pointer during the
  228.         // drag. This image will represent a container record being dragged.
  229.  
  230.         pdimg = (PDRAGIMAGE) malloc( iDragImageArraySize );
  231.  
  232.         if( pdimg )
  233.             (void) memset( pdimg, 0, iDragImageArraySize );
  234.         else
  235.             Msg( "Out of memory in CnrInitDrag" );
  236.  
  237.         // Let PM allocate enough memory for a DRAGINFO structure as well as
  238.         // a DRAGITEM structure for each record being dragged. It will allocate
  239.         // shared memory so other processes can participate in the drag/drop.
  240.  
  241.         pdinfo = DrgAllocDraginfo( cRecs );
  242.  
  243.         if( pdinfo )
  244.         {
  245.             pi->pSavedDragInfo = pdinfo;
  246.             pi->cDragItems = cRecs;
  247.         }
  248.         else
  249.             Msg( "DrgAllocDraginfo failed. RC(%X)", HWNDERR( hwndClient ) );
  250.     }
  251.  
  252.     if( cRecs && pdinfo && pdimg )
  253.     {
  254.         // Set the data from the container records into the DRAGITEM and
  255.         // DRAGIMAGE structures. If we are to process CRA_SELECTED container
  256.         // records, do them all in one function. If not, pass a pointer to the
  257.         // container record under the mouse to a different function that will
  258.         // fill in just one of each DRAGITEM / DRAGIMAGE structures.
  259.  
  260.         if( fProcessSelected )
  261.             SetSelectedDragItems( hwndClient, pi, cRecs, pdinfo, pdimg );
  262.         else
  263.             SetOneDragItem( hwndClient, pi, pciUnderMouse, pdinfo, pdimg, 0 );
  264.  
  265.         // If DrgDrag returns NULLHANDLE, that means the user hit Esc or F1
  266.         // while the drag was going on so the target didn't have a chance to
  267.         // delete the string handles. So it is up to the source window to do
  268.         // it. Unfortunately there doesn't seem to be a way to determine
  269.         // whether the NULLHANDLE means Esc was pressed as opposed to there
  270.         // being an error in the drag operation. So we don't attempt to figure
  271.         // that out. To us, a NULLHANDLE means Esc was pressed...
  272.  
  273.         if( !DrgDrag( hwndClient, pdinfo, pdimg, cRecs, VK_ENDDRAG, NULL ) )
  274.         {
  275.             if( !DrgDeleteDraginfoStrHandles( pdinfo ) )
  276.                 Msg( "DragInit DrgDeleteDraginfoStrHandles RC(%X)",
  277.                      HWNDERR( hwndClient ) );
  278.  
  279.             if( !DrgFreeDraginfo( pdinfo ) )
  280.                 Msg( "DragInit DrgFreeDraginfo RC(%X)", HWNDERR( hwndClient ) );
  281.  
  282.             pi->pSavedDragInfo = NULL;
  283.         }
  284.  
  285.         // Take off source emphasis from the records that were dragged
  286.  
  287.         RemoveSourceEmphasis( hwndClient );
  288.     }
  289.  
  290.     if( pdimg )
  291.         free( pdimg );
  292. }
  293.  
  294. /**********************************************************************/
  295. /*------------------------ CountSelectedRecs -------------------------*/
  296. /*                                                                    */
  297. /*  COUNT THE NUMBER OF RECORDS THAT ARE CURRENTLY SELECTED.          */
  298. /*                                                                    */
  299. /*  INPUT: client window handle,                                      */
  300. /*         pointer to the record that was under the pointer,          */
  301. /*         address of BOOL - should we process selected records?      */
  302. /*                                                                    */
  303. /*  1.                                                                */
  304. /*                                                                    */
  305. /*  OUTPUT: number of records to process                              */
  306. /*                                                                    */
  307. /*--------------------------------------------------------------------*/
  308. /**********************************************************************/
  309. static INT CountSelectedRecs( HWND hwndClient, PCNRITEM pciUnderMouse,
  310.                               PBOOL pfProcessSelected )
  311. {
  312.     INT      cRecs = 0;
  313.     PCNRITEM pci;
  314.  
  315.     *pfProcessSelected = FALSE;
  316.  
  317.     // If the record under the mouse is NULL, we must be over whitespace, in
  318.     // which case we don't want to drag any records.
  319.  
  320.     if( pciUnderMouse )
  321.     {
  322.         pci = (PCNRITEM) CMA_FIRST;
  323.  
  324.         // Count the records with 'selection' emphasis. These are the records
  325.         // we want to drag, unless the container record under the mouse does
  326.         // not have selection emphasis. If that is the case, we only want to
  327.         // process that one.
  328.  
  329.         while( pci )
  330.         {
  331.             pci = (PCNRITEM) WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY,
  332.                                                 CM_QUERYRECORDEMPHASIS,
  333.                                                 MPFROMP( pci ),
  334.                                                 MPFROMSHORT( CRA_SELECTED ) );
  335.  
  336.             if( pci == (PCNRITEM) -1 )
  337.                 Msg( "CountSelectedRecs..CM_QUERYRECORDEMPHASIS RC(%X)",
  338.                      HWNDERR( hwndClient ) );
  339.             else if( pci )
  340.             {
  341.                 if( pci == pciUnderMouse )
  342.                     *pfProcessSelected = TRUE;
  343.  
  344.                 cRecs++;
  345.             }
  346.         }
  347.  
  348.         if( !(*pfProcessSelected) )
  349.             cRecs = 1;
  350.     }
  351.  
  352.     return cRecs;
  353. }
  354.  
  355. /**********************************************************************/
  356. /*----------------------- SetSelectedDragItems -----------------------*/
  357. /*                                                                    */
  358. /*  FILL THE DRAGINFO STRUCT WITH DRAGITEM STRUCTS FOR SELECTED RECS. */
  359. /*                                                                    */
  360. /*  INPUT: client window handle,                                      */
  361. /*         pointer to client window's instance data,                  */
  362. /*         count of selected records,                                 */
  363. /*         pointer to allocated DRAGINFO struct,                      */
  364. /*         pointer to allocated DRAGIMAGE array                       */
  365. /*                                                                    */
  366. /*  1.                                                                */
  367. /*                                                                    */
  368. /*  OUTPUT: nothing                                                   */
  369. /*                                                                    */
  370. /*--------------------------------------------------------------------*/
  371. /**********************************************************************/
  372. static VOID SetSelectedDragItems( HWND hwndClient, PINSTANCE pi, INT cRecs,
  373.                                   PDRAGINFO pdinfo, PDRAGIMAGE pdimg )
  374. {
  375.     PCNRITEM pci;
  376.     INT      i;
  377.  
  378.     pci = (PCNRITEM) CMA_FIRST;
  379.  
  380.     for( i = 0; i < cRecs; i++, pdimg++ )
  381.     {
  382.         pci = (PCNRITEM) WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY,
  383.                                             CM_QUERYRECORDEMPHASIS,
  384.                                             MPFROMP( pci ),
  385.                                             MPFROMSHORT( CRA_SELECTED ) );
  386.  
  387.         if( pci == (PCNRITEM) -1 )
  388.             Msg( "SetSelectedDragItems..CM_QUERYRECORDEMPHASIS RC(%X)",
  389.                  HWNDERR( hwndClient ) );
  390.         else
  391.             SetOneDragItem( hwndClient, pi, pci, pdinfo, pdimg, i );
  392.     }
  393. }
  394.  
  395. /**********************************************************************/
  396. /*-------------------------- SetOneDragItem --------------------------*/
  397. /*                                                                    */
  398. /*  SET ONE DRAGITEM STRUCT INTO A DRAGINFO STRUCT.                   */
  399. /*                                                                    */
  400. /*  INPUT: client window handle,                                      */
  401. /*         pointer to client window's instance data,                  */
  402. /*         pointer to CNRITEM that contains current container record, */
  403. /*         pointer to allocated DRAGINFO struct,                      */
  404. /*         pointer to allocated DRAGIMAGE array,                      */
  405. /*         record offset into DRAGINFO struct to place DRAGITEM       */
  406. /*                                                                    */
  407. /*  1.                                                                */
  408. /*                                                                    */
  409. /*  OUTPUT: nothing                                                   */
  410. /*                                                                    */
  411. /*--------------------------------------------------------------------*/
  412. /**********************************************************************/
  413. static VOID SetOneDragItem( HWND hwndClient, PINSTANCE pi, PCNRITEM pci,
  414.                             PDRAGINFO pdinfo, PDRAGIMAGE pdimg, INT iOffset )
  415. {
  416.     DRAGITEM ditem;
  417.  
  418.     (void) memset( &ditem, 0, sizeof( DRAGITEM ) );
  419.  
  420.     // Fill in the DRAGITEM struct
  421.  
  422.     ditem.hwndItem  = hwndClient;    // Window handle of the source of the drag
  423.     ditem.ulItemID  = (ULONG) pci;   // We use this to store the container rec
  424.     ditem.hstrType  = DrgAddStrHandle( DRT_UNKNOWN ); // Application defined
  425.     ditem.hstrRMF   = DrgAddStrHandle( DRAG_RMF ); // Defined at top of module
  426.     ditem.hstrContainerName = DrgAddStrHandle( pi->szDirectory );
  427.     ditem.hstrSourceName = DrgAddStrHandle( pci->szFileName );
  428.     ditem.hstrTargetName = ditem.hstrSourceName;
  429.     ditem.fsSupportedOps = DO_COPYABLE;  // We support copy only
  430.  
  431.     // Set the DRAGITEM struct into the memory allocated by
  432.     // DrgAllocDraginfo()
  433.  
  434.     DrgSetDragitem( pdinfo, &ditem, sizeof( DRAGITEM ), iOffset );
  435.  
  436.     // Fill in the DRAGIMAGE structure
  437.  
  438.     pdimg->cb       = sizeof( DRAGIMAGE );
  439.     pdimg->hImage   = pci->rc.hptrIcon;     // Image under mouse during drag
  440.     pdimg->fl       = DRG_ICON;             // hImage is an HPOINTER
  441.     pdimg->cxOffset = 5 * iOffset;          // Image offset from mouse pointer
  442.     pdimg->cyOffset = 5 * iOffset;          // Image offset from mouse pointer
  443.  
  444.     // Set source emphasis for this container record
  445.  
  446.     if( !WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY, CM_SETRECORDEMPHASIS,
  447.                             MPFROMP( pci ), MPFROM2SHORT( TRUE, CRA_SOURCE ) ) )
  448.         Msg( "SetOneDragItem..CM_SETRECORDEMPHASIS RC(%X)",
  449.              HWNDERR( hwndClient ) );
  450. }
  451.  
  452. /**********************************************************************/
  453. /*----------------------------- DragOver -----------------------------*/
  454. /*                                                                    */
  455. /*  PROCESS CN_DRAGOVER NOTIFY MESSAGE.                               */
  456. /*                                                                    */
  457. /*  INPUT: client window handle,                                      */
  458. /*         pointer to the CNRDRAGINFO structure                       */
  459. /*                                                                    */
  460. /*  1.                                                                */
  461. /*                                                                    */
  462. /*  OUTPUT: return value from CN_DRAGOVER processing                  */
  463. /*                                                                    */
  464. /*--------------------------------------------------------------------*/
  465. /**********************************************************************/
  466. MRESULT DragOver( HWND hwndClient, PCNRDRAGINFO pcdi )
  467. {
  468.     USHORT    usDrop, usDefaultOp;
  469.     PCNRITEM  pci = (PCNRITEM) pcdi->pRecord;
  470.     PDRAGINFO pdinfo = pcdi->pDragInfo;
  471.     PDRAGITEM pditem;
  472.     INT       i;
  473.     PINSTANCE pi = INSTDATA( hwndClient );
  474.  
  475.     if( !pi )
  476.     {
  477.         Msg( "DragOver cant get Inst data RC(%X)", HWNDERR( hwndClient ) );
  478.  
  479.         return MRFROM2SHORT( DOR_NEVERDROP, 0 );
  480.     }
  481.  
  482.     // Don't allow a drop over a record that is not DROPONABLE if we are in
  483.     // icon view. In the other views it makes sense to be dropping over another
  484.     // record since that is how you indicate where you want the record to be
  485.     // inserted. In icon view the user should insert over white space.
  486.  
  487.     if( pi->flCurrentView == CV_ICON && pci &&
  488.         !(pci->rc.flRecordAttr & CRA_DROPONABLE) )
  489.         return MRFROM2SHORT( DOR_NODROP, 0 );
  490.  
  491.     if( !DrgAccessDraginfo( pdinfo ) )
  492.     {
  493.         Msg( "DragOver DrgAccessDraginfo RC(%X)", HWNDERR( hwndClient ) );
  494.  
  495.         return MRFROM2SHORT( DOR_NEVERDROP, 0 );
  496.     }
  497.  
  498.     // Don't allow a drop if one of the dropped records is the same record
  499.     // that's under the mouse pointer. That causes problems in this app,
  500.     // although it may be appropriate in other applications.
  501.  
  502.     if( pci )
  503.         for( i = 0; i < pdinfo->cditem; i++ )
  504.         {
  505.             pditem = DrgQueryDragitemPtr( pdinfo, i );
  506.  
  507.             if( pci == (PCNRITEM) pditem->ulItemID )
  508.                 return MRFROM2SHORT( DOR_NODROP, 0 );
  509.         }
  510.  
  511.     pditem = DrgQueryDragitemPtr( pdinfo, 0 );
  512.  
  513.     if( pditem )
  514.     {
  515.         // We will only allow OS2 FILES to be dropped on us
  516.  
  517.         if( DrgVerifyRMF( pditem, "DRM_OS2FILE", NULL ) )
  518.         {
  519.             usDrop = DOR_DROP;
  520.  
  521.             // If we are the source as well as the target, the default operation
  522.             // will be to MOVE the record. Otherwise it will be to COPY it
  523.  
  524.             if( pdinfo->hwndSource == hwndClient )
  525.                 usDefaultOp = DO_MOVE;
  526.             else
  527.                 usDefaultOp = DO_COPY;
  528.         }
  529.         else
  530.         {
  531.             usDrop = DOR_NEVERDROP;
  532.  
  533.             usDefaultOp = 0;
  534.         }
  535.     }
  536.     else
  537.         Msg( "DragOver DrgQueryDragitemPtr RC(%X)", HWNDERR( hwndClient ) );
  538.  
  539.     // Free our handle to the shared memory if the source window is not in our
  540.     // process.
  541.  
  542.     if( !WindowInOurProcess( hwndClient, pdinfo->hwndSource ) )
  543.         if( !DrgFreeDraginfo( pdinfo ) )
  544.             Msg( "DragOver DrgFreeDraginfo RC(%X)", HWNDERR( hwndClient ) );
  545.  
  546.     return MRFROM2SHORT( usDrop, usDefaultOp );
  547. }
  548.  
  549. /**********************************************************************/
  550. /*---------------------------- DragDrop ------------------------------*/
  551. /*                                                                    */
  552. /*  PROCESS CN_DROP NOTIFY MESSAGE.                                   */
  553. /*                                                                    */
  554. /*  INPUT: client window handle,                                      */
  555. /*         pointer to the CNRDRAGINFO structure                       */
  556. /*                                                                    */
  557. /*  1.                                                                */
  558. /*                                                                    */
  559. /*  OUTPUT: nothing                                                   */
  560. /*                                                                    */
  561. /*--------------------------------------------------------------------*/
  562. /**********************************************************************/
  563. VOID DragDrop( HWND hwndClient, PCNRDRAGINFO pcdi )
  564. {
  565.     PDRAGINFO pdinfo = pcdi->pDragInfo;
  566.  
  567.     if( !DrgAccessDraginfo( pdinfo ) )
  568.     {
  569.         Msg( "DragDrop DrgAccessDraginfo RC(%X)", HWNDERR( hwndClient ) );
  570.  
  571.         return;
  572.     }
  573.  
  574.     if( pdinfo->hwndSource == hwndClient )
  575.         OurOwnDrag( hwndClient, (PCNRITEM) pcdi->pRecord, pdinfo );
  576.     else
  577.         SomeoneElsesDrag( hwndClient, (PCNRITEM) pcdi->pRecord, pdinfo );
  578.  
  579.     // Free the string handles in the DRAGINFO structure that were
  580.     // allocated by DrgAddStrHandle.
  581.  
  582.     if( !DrgDeleteDraginfoStrHandles( pdinfo ) )
  583.         Msg( "DragDrop DrgDeleteDraginfoStrHandles RC(%X)",
  584.              HWNDERR( hwndClient ) );
  585.  
  586.     // Free the shared memory we got access to using DrgAccessDragInfo. If
  587.     // the source process is the same as the target process, we get the
  588.     // PMERR_SOURCE_SAME_AS_TARGET message. It's ok to get that - it just
  589.     // means that we don't need to free the structure because the target
  590.     // process already freed it (this is a way to not have to call the
  591.     // WindowInOurProcess function called by other functions in this
  592.     // module).
  593.  
  594.     if( !DrgFreeDraginfo( pdinfo ) &&
  595.         PMERR_SOURCE_SAME_AS_TARGET != HWNDERR( hwndClient ) )
  596.         Msg( "DragDrop DrgFreeDraginfo RC(%X)", HWNDERR( hwndClient ) );
  597. }
  598.  
  599. /**********************************************************************/
  600. /*--------------------------- OurOwnDrag -----------------------------*/
  601. /*                                                                    */
  602. /*  PROCESS CN_DROP NOTIFY MESSAGE COMING FROM OUR OWN CONTAINER      */
  603. /*                                                                    */
  604. /*  INPUT: client window handle,                                      */
  605. /*         pointer to the container record under the mouse,           */
  606. /*         pointer to the DRAGINFO structure                          */
  607. /*                                                                    */
  608. /*  1.                                                                */
  609. /*                                                                    */
  610. /*  OUTPUT: nothing                                                   */
  611. /*                                                                    */
  612. /*--------------------------------------------------------------------*/
  613. /**********************************************************************/
  614. static VOID OurOwnDrag( HWND hwndClient, PCNRITEM pciUnderMouse,
  615.                         PDRAGINFO pdinfo )
  616. {
  617.     HWND      hwndCnr = WinWindowFromID( hwndClient, CNR_DIRECTORY );
  618.     PDRAGITEM pditem;
  619.     INT       i, iSpacing = 0;
  620.     RECTL     rclPrev;
  621.     PCNRITEM  *apci;
  622.     QUERYRECORDRECT qrr;
  623.     PINSTANCE pi = INSTDATA( hwndClient );
  624.  
  625.     // Allocate memory for an array of container record pointers. This array
  626.     // will be used during the CM_INVALIDATE message to invalidate all dropped
  627.     // records in one shot
  628.  
  629.     apci = (PCNRITEM *) malloc( pdinfo->cditem * sizeof( PCNRITEM ) );
  630.  
  631.     if( !apci )
  632.     {
  633.         Msg( "Out of memory in OurOwnDrag!" );
  634.  
  635.         return;
  636.     }
  637.  
  638.     (void) memset( &qrr, 0, sizeof( QUERYRECORDRECT ) );
  639.  
  640.     // Go thru each dropped record and erase it from its present place in the
  641.     // container, then invalidate it so it repaints itself in its new spot.
  642.     // This is really only valid in Icon view.
  643.  
  644.     for( i = 0; i < pdinfo->cditem; i++ )
  645.     {
  646.         pditem = DrgQueryDragitemPtr( pdinfo, i );
  647.  
  648.         if( !pditem )
  649.         {
  650.             Msg( "OurOwnDrag DrgQueryDragitemPtr RC(%X)", HWNDERR( hwndCnr ) );
  651.  
  652.             break;
  653.         }
  654.  
  655.         // Get the CNRITEM pointer that we stuck in ulItemID in DragInit
  656.  
  657.         apci[ i ] = (PCNRITEM) pditem->ulItemID;
  658.  
  659.         // Remove source emphasis from the dropped record
  660.  
  661.         if( !WinSendMsg( hwndCnr, CM_SETRECORDEMPHASIS,
  662.                          MPFROMP( apci[ i ] ),
  663.                          MPFROM2SHORT( FALSE, CRA_SOURCE ) ) )
  664.             Msg( "OurOwnDrag CM_SETRECORDEMPHASIS RC(%X)", HWNDERR( hwndCnr ) );
  665.  
  666.         // Get the current position of the container record before it is moved
  667.  
  668.         qrr.cb                  = sizeof( QUERYRECORDRECT );
  669.         qrr.pRecord             = (PRECORDCORE) apci[ i ];
  670.         qrr.fsExtent            = CMA_ICON | CMA_TEXT | CMA_TREEICON;
  671.         qrr.fRightSplitWindow   = FALSE;
  672.  
  673.         if( !WinSendMsg( hwndCnr, CM_QUERYRECORDRECT, MPFROMP( &rclPrev ),
  674.                          MPFROMP( &qrr ) ) )
  675.             Msg( "OurOwnDrag CM_QUERYRECORDRECT RC(%X)", HWNDERR( hwndCnr ) );
  676.  
  677.         // If Icon view, erase the visible record from the container
  678.         // (this doesn't remove the record). If not icon view, it doesn't make
  679.         // a difference because there shouldn't be holes left.
  680.  
  681.         if( pi && pi->flCurrentView == CV_ICON )
  682.             if( !WinSendMsg( hwndCnr, CM_ERASERECORD, MPFROMP( apci[ i ] ),
  683.                              NULL ) )
  684.                 Msg( "OurOwnDrag CM_ERASERECORD RC(%X)", HWNDERR( hwndCnr ) );
  685.  
  686.         // Find out where the mouse is in relation to the container and set the
  687.         // container record's new position as that point plus an offset from it.
  688.         // In other words, we are moving the icon.
  689.  
  690.         if( !WinQueryPointerPos( HWND_DESKTOP, &(apci[ i ]->rc.ptlIcon) ) )
  691.             Msg( "OurOwnDrag WinQueryPointerPos RC(%X)", HWNDERR( hwndCnr ) );
  692.  
  693.         if( !WinMapWindowPoints( HWND_DESKTOP, hwndCnr,
  694.                                  &(apci[ i ]->rc.ptlIcon), 1 ) )
  695.             Msg( "OurOwnDrag WinMapWindowPoints RC(%X)", HWNDERR( hwndCnr ) );
  696.  
  697.         if( i )
  698.         {
  699.             iSpacing += ((rclPrev.xRight - rclPrev.xLeft) + 5);
  700.  
  701.             apci[ i ]->rc.ptlIcon.x += iSpacing;
  702.         }
  703.  
  704.         // We reposition the record if in icon view. Note that if you repostion
  705.         // all records in one shot (instead of one at a time like here), some
  706.         // selection emphasis is left behind after the record is moved. This is
  707.         // another bug with the container.
  708.  
  709.         if( pi && pi->flCurrentView == CV_ICON )
  710.             if( !WinSendMsg( hwndCnr, CM_INVALIDATERECORD,
  711.                              MPFROMP( &(apci[ i ]) ),
  712.                              MPFROM2SHORT( 1, CMA_REPOSITION ) ) )
  713.                 Msg( "OurOwnDrag CM_INVALIDATERECORD RC(%X)", HWNDERR( hwndCnr ) );
  714.  
  715.         // Let the source know that we are done rendering this item
  716.  
  717.         DrgSendTransferMsg( pditem->hwndItem, DM_ENDCONVERSATION,
  718.                             MPFROMLONG( pditem->ulItemID ),
  719.                             MPFROMLONG( DMFL_TARGETSUCCESSFUL ) );
  720.     }
  721.  
  722.     // If the mouse was over a container record when the drop ocurred, move
  723.     // the records to their new position. This is accomplished by removing the
  724.     // records and then re-inserting them.
  725.  
  726.     if( pciUnderMouse )
  727.         MoveRecords( hwndCnr, pciUnderMouse, apci, pdinfo->cditem );
  728.  
  729.     // If icon view, we've already repositioned the record. If not icon view,
  730.     // we need to invalidate the whole container so that it fills in the empty
  731.     // holes.
  732.  
  733.     if( pi && pi->flCurrentView != CV_ICON )
  734.         if( !WinSendMsg( hwndCnr, CM_INVALIDATERECORD, MPFROMP( apci ),
  735.                          MPFROM2SHORT( 0, CMA_REPOSITION ) ) )
  736.             Msg( "OurOwnDrag CM_INVALIDATERECORD RC(%X)", HWNDERR( hwndCnr ) );
  737.  
  738.     free( apci );
  739. }
  740.  
  741. /**********************************************************************/
  742. /*--------------------------- MoveRecords ----------------------------*/
  743. /*                                                                    */
  744. /*  MOVE RECORDS BY REMOVING THEM AND RE-INSERTING THEM.              */
  745. /*                                                                    */
  746. /*  INPUT: container window handle,                                   */
  747. /*         pointer to container record under the mouse,               */
  748. /*         array of container record pointers to move,                */
  749. /*         number of records to move                                  */
  750. /*                                                                    */
  751. /*  1.                                                                */
  752. /*                                                                    */
  753. /*  OUTPUT: nothing                                                   */
  754. /*                                                                    */
  755. /*--------------------------------------------------------------------*/
  756. /**********************************************************************/
  757. static VOID MoveRecords( HWND hwndCnr, PCNRITEM pciUnderMouse,
  758.                          PCNRITEM *apciToMove, USHORT cRecsToMove )
  759. {
  760.     RECORDINSERT ri;
  761.     LONG         cRecsLeft;
  762.     PCNRITEM     pciParent = NULL;
  763.     INT          i;
  764.  
  765.     // First remove the records from the container
  766.  
  767.     cRecsLeft = (LONG) WinSendMsg( hwndCnr, CM_REMOVERECORD,
  768.                                    MPFROMP( apciToMove ),
  769.                                    MPFROM2SHORT( cRecsToMove, 0 ) );
  770.  
  771.     if( cRecsLeft == -1 )
  772.     {
  773.         Msg( "MoveRecords CM_REMOVERECORD RC(%X)", HWNDERR( hwndCnr ) );
  774.  
  775.         return;
  776.     }
  777.  
  778.     // Get the parent of the record that is under the mouse. Since we will be
  779.     // inserting the records after the record under the mouse, we need to set
  780.     // the new records' parent to be the same as that one's. If the mouse is
  781.     // at the top of the container, we will get CMA_FIRST, in which case we
  782.     // don't want to issue the CM_QUERYRECORD message as we will trap with
  783.     // CMA_FIRST being used as a record pointer.
  784.  
  785.     if( pciUnderMouse != (PCNRITEM) CMA_FIRST )
  786.         pciParent = (PCNRITEM) WinSendMsg( hwndCnr, CM_QUERYRECORD,
  787.                                        MPFROMP( pciUnderMouse ),
  788.                                        MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER));
  789.  
  790.     if( pciParent == (PCNRITEM) -1 )
  791.         Msg( "MoveRecords CM_QUERYRECORD RC(%X)", HWNDERR( hwndCnr ) );
  792.  
  793.     // Insert the new records after the one under the mouse. Note that
  794.     // CM_INSERTRECORD does not take in mp1 an array of record pointers like the
  795.     // CM_REMOVERECORD message does. It takes an array of RECORDCORE structures.
  796.     // We must insert them one at a time because if you want to insert more
  797.     // than one at a time you must be inserting a linked list allocated by
  798.     // CM_ALLOCRECORD. Since the CM_REMOVERECORD message does not free the
  799.     // records we use the same record memory to insert them back into the
  800.     // container.
  801.  
  802.     (void) memset( &ri, 0, sizeof( RECORDINSERT ) );
  803.  
  804.     ri.cb                 = sizeof( RECORDINSERT );
  805.     ri.pRecordOrder       = (PRECORDCORE) pciUnderMouse;
  806.     ri.pRecordParent      = (PRECORDCORE) pciParent;
  807.     ri.zOrder             = (USHORT) CMA_TOP;
  808.     ri.cRecordsInsert     = 1;
  809.     ri.fInvalidateRecord  = FALSE;
  810.  
  811.     for( i = 0; i < cRecsToMove; i++ )
  812.     {
  813.         if( !WinSendMsg( hwndCnr, CM_INSERTRECORD, MPFROMP( apciToMove[ i ] ),
  814.                          MPFROMP( &ri ) ) )
  815.             Msg( "MoveRecords CM_INSERTRECORD RC(%X)", HWNDERR( hwndCnr ) );
  816.  
  817.         // We want to insert each record after the prior one
  818.  
  819.         ri.pRecordOrder = (PRECORDCORE) apciToMove[ i ];
  820.     }
  821. }
  822.  
  823. /**********************************************************************/
  824. /*------------------------ SomeoneElsesDrag --------------------------*/
  825. /*                                                                    */
  826. /*  PROCESS CN_DROP NOTIFY MESSAGE COMING FROM OTHER THAN OUR WINDOW  */
  827. /*                                                                    */
  828. /*  INPUT: client window handle,                                      */
  829. /*         pointer to the container record under the mouse,           */
  830. /*         pointer to the DRAGINFO structure                          */
  831. /*                                                                    */
  832. /*  1.                                                                */
  833. /*                                                                    */
  834. /*  OUTPUT: nothing                                                   */
  835. /*                                                                    */
  836. /*--------------------------------------------------------------------*/
  837. /**********************************************************************/
  838. static VOID SomeoneElsesDrag( HWND hwndClient, PCNRITEM pciUnderMouse,
  839.                               PDRAGINFO pdinfo )
  840. {
  841.     HWND         hwndCnr = WinWindowFromID( hwndClient, CNR_DIRECTORY );
  842.     PDRAGITEM    pditem;
  843.     INT          i;
  844.     PCNRITEM     pci, pciFirst, pciParent = NULL, pciRecordAfter;
  845.     RECORDINSERT ri;
  846.  
  847.     // Let the container allocate memory for the new container records that
  848.     // were dropped on it.
  849.  
  850.     pci = pciFirst = WinSendMsg( hwndCnr, CM_ALLOCRECORD,
  851.                                  MPFROMLONG( EXTRA_RECORD_BYTES ),
  852.                                  MPFROMLONG( pdinfo->cditem ) );
  853.  
  854.     if( !pciFirst )
  855.     {
  856.         Msg( "SomeoneElsesDrag CM_ALLOCRECORD RC(%X)", HWNDERR( hwndClient ) );
  857.  
  858.         return;
  859.     }
  860.  
  861.     // Go thru each dropped record and add it to the linked list of records
  862.     // that will be added to the container
  863.  
  864.     for( i = 0; i < pdinfo->cditem; i++ )
  865.     {
  866.         pditem = DrgQueryDragitemPtr( pdinfo, i );
  867.  
  868.         if( !pditem )
  869.         {
  870.             Msg( "SomeoneElsesDrag DrgQueryDragitemPtr RC(%X)",
  871.                  HWNDERR( hwndCnr ) );
  872.  
  873.             break;
  874.         }
  875.  
  876.         FillInNewRecord( pci, pditem );
  877.  
  878.         // Find out where the mouse is in relation to the container and set the
  879.         // container record's new position as that point plus an offset from it.
  880.         // In other words, we are moving the icon.
  881.  
  882.         if( !WinQueryPointerPos( HWND_DESKTOP, &(pci->rc.ptlIcon) ) )
  883.             Msg( "OurOwnDrag WinQueryPointerPos RC(%X)", HWNDERR( hwndCnr ) );
  884.  
  885.         if( !WinMapWindowPoints( HWND_DESKTOP, hwndCnr,
  886.                                  &(pci->rc.ptlIcon), 1 ) )
  887.             Msg( "OurOwnDrag WinMapWindowPoints RC(%X)", HWNDERR( hwndCnr ) );
  888.  
  889.         if( i )
  890.             pci->rc.ptlIcon.x += 40;
  891.  
  892.         // Let the source know that we are done rendering this item
  893.  
  894.         DrgSendTransferMsg( pditem->hwndItem, DM_ENDCONVERSATION,
  895.                             MPFROMLONG( pditem->ulItemID ),
  896.                             MPFROMLONG( DMFL_TARGETSUCCESSFUL ) );
  897.  
  898.         pci = (PCNRITEM) pci->rc.preccNextRecord;
  899.     }
  900.  
  901.     // If there was a record under the mouse when records were dropped...
  902.  
  903.     if( pciUnderMouse )
  904.     {
  905.         pciRecordAfter = pciUnderMouse;
  906.  
  907.         // Get the parent of the record that is under the mouse. Since we will
  908.         // be inserting the records after the record under the mouse, we need
  909.         // to set the new records' parent to be the same as that one's.
  910.  
  911.         pciParent = (PCNRITEM) WinSendMsg( hwndCnr, CM_QUERYRECORD,
  912.                                     MPFROMP( pciUnderMouse ),
  913.                                     MPFROM2SHORT( CMA_PARENT, CMA_ITEMORDER ) );
  914.  
  915.         if( pciParent == (PCNRITEM) -1 )
  916.             Msg( "SomeOneElsesDrag CM_QUERYRECORD RC(%X)", HWNDERR( hwndCnr ) );
  917.     }
  918.     else
  919.         pciRecordAfter = (PCNRITEM) CMA_END;
  920.  
  921.     // Insert the container records. If there was a record under the mouse when
  922.     // the records were dropped, insert them after that one. Otherwise insert
  923.     // them at the end of the container's linked list.
  924.  
  925.     (void) memset( &ri, 0, sizeof( RECORDINSERT ) );
  926.  
  927.     ri.cb                 = sizeof( RECORDINSERT );
  928.     ri.pRecordOrder       = (PRECORDCORE) pciRecordAfter;
  929.     ri.pRecordParent      = (PRECORDCORE) pciParent;
  930.     ri.zOrder             = (USHORT) CMA_TOP;
  931.     ri.cRecordsInsert     = pdinfo->cditem;
  932.     ri.fInvalidateRecord  = TRUE;
  933.  
  934.     if( !WinSendMsg( hwndCnr, CM_INSERTRECORD, MPFROMP( pciFirst ),
  935.                      MPFROMP( &ri ) ) )
  936.         Msg( "SomeoneElsesDrag CM_INSERTRECORD RC(%X)", HWNDERR( hwndCnr ) );
  937. }
  938.  
  939. /**********************************************************************/
  940. /*------------------------ FillInNewRecord ---------------------------*/
  941. /*                                                                    */
  942. /*  POPULATE CONTAINER RECORD WITH FILE INFORMATION                   */
  943. /*                                                                    */
  944. /*  INPUT: pointer to record buffer to fill,                          */
  945. /*         pointer to DRAGITEM structure of dragged record            */
  946. /*                                                                    */
  947. /*  1.                                                                */
  948. /*                                                                    */
  949. /*  OUTPUT: nothing                                                   */
  950. /*                                                                    */
  951. /*--------------------------------------------------------------------*/
  952. /**********************************************************************/
  953. static VOID FillInNewRecord( PCNRITEM pci, PDRAGITEM pditem )
  954. {
  955.     CHAR        szSource[ CCHMAXPATH + 1 ];
  956.     PCH         pch;
  957.     HPOINTER    hptr;
  958.     FILESTATUS3 fs;
  959.     APIRET      rc;
  960.  
  961.     // DrgQueryStrName gets access to a drag/drop shared-memory string
  962.     // Get the fully-qualified source filename into szSource
  963.  
  964.     DrgQueryStrName( pditem->hstrContainerName, sizeof( szSource ), szSource );
  965.  
  966.     pch = szSource + strlen( szSource );
  967.  
  968.     if( pch > szSource && *(pch - 1) != '\\' )
  969.         *(pch)++ = '\\';
  970.  
  971.     DrgQueryStrName( pditem->hstrSourceName,
  972.                      sizeof( szSource ) - strlen( szSource ), pch );
  973.  
  974.     // If hstrTargetName is non-NULL, that means the source of the drag wants
  975.     // that to be the name of the file. If it is NULL, source-name is to be
  976.     // used as the file name.
  977.  
  978.     DrgQueryStrName( pditem->hstrTargetName, sizeof( pci->szFileName ),
  979.                      pci->szFileName );
  980.  
  981.     if( !(*pci->szFileName) )
  982.         DrgQueryStrName( pditem->hstrSourceName, sizeof( pci->szFileName ),
  983.                          pci->szFileName );
  984.  
  985.     // Get file information
  986.  
  987.     rc = DosQueryPathInfo( szSource, FIL_STANDARD, &fs, sizeof( FILESTATUS3 ) );
  988.  
  989.     if( rc )
  990.     {
  991.         (void) memset( &fs, 0, sizeof( FILESTATUS3 ) );
  992.  
  993.         Msg( "FillInNewRecord DosQueryPathInfo RC(%u)", rc );
  994.     }
  995.  
  996.     // Get the icon associated with the fully qualified source filename.
  997.  
  998.     hptr = WinLoadFileIcon( szSource, FALSE );
  999.  
  1000.     // Fill in the file information.
  1001.  
  1002.     pci->date.day       = fs.fdateLastWrite.day;
  1003.     pci->date.month     = fs.fdateLastWrite.month;
  1004.     pci->date.year      = fs.fdateLastWrite.year;
  1005.     pci->time.seconds   = fs.ftimeLastWrite.twosecs;
  1006.     pci->time.minutes   = fs.ftimeLastWrite.minutes;
  1007.     pci->time.hours     = fs.ftimeLastWrite.hours;
  1008.     pci->cbFile         = fs.cbFile;
  1009.     pci->attrFile       = fs.attrFile;
  1010.     pci->iDirPosition   = 0;
  1011.     pci->rc.pszIcon     = pci->szFileName;
  1012.     pci->rc.hptrIcon    = hptr;
  1013. }
  1014.  
  1015. /**********************************************************************/
  1016. /*--------------------------- PrintObject ----------------------------*/
  1017. /*                                                                    */
  1018. /*  PROCESS DM_PRINTOBJECT MESSAGE.                                   */
  1019. /*                                                                    */
  1020. /*  NOTE: PM sends us one of these messages for each item dropped on  */
  1021. /*        the printer object.  Go figure! This is not documented      */
  1022. /*        anywhere as far as I know.                                  */
  1023. /*                                                                    */
  1024. /*  INPUT: client window handle,                                      */
  1025. /*         pointer to DRAGITEM structure,                             */
  1026. /*         pointer to PRINTDEST structure                             */
  1027. /*                                                                    */
  1028. /*  1.                                                                */
  1029. /*                                                                    */
  1030. /*  OUTPUT: DRR_SOURCE or DRR_TARGET                                  */
  1031. /*                                                                    */
  1032. /*--------------------------------------------------------------------*/
  1033. /**********************************************************************/
  1034. static MRESULT PrintObject( HWND hwndClient, PDRAGITEM pditem, PPRINTDEST ppd )
  1035. {
  1036.     HAB      hab = ANCHOR( hwndClient );
  1037.     PCNRITEM pci = (PCNRITEM) pditem->ulItemID;
  1038.     HDC      hdc;
  1039.  
  1040.     // Get a printer device context using the information returned from PM
  1041.     // in the PRINTDEST structure
  1042.  
  1043.     hdc = DevOpenDC( hab, ppd->lType, ppd->pszToken, ppd->lCount, ppd->pdopData,
  1044.                      NULLHANDLE );
  1045.  
  1046.     if( pci && hdc )
  1047.     {
  1048.         LONG lBytes, lError;
  1049.  
  1050.         // If PM is telling us that we should let the user decide on job
  1051.         // properties, we accommodate it by putting up the job properties
  1052.         // dialog box.
  1053.  
  1054.         if( ppd->fl & PD_JOB_PROPERTY )
  1055.             DevPostDeviceModes( hab, ((PDEVOPENSTRUC) ppd->pdopData)->pdriv,
  1056.                                 ((PDEVOPENSTRUC)ppd->pdopData)->pszDriverName,
  1057.                                 ppd->pszPrinter, NULL, DPDM_POSTJOBPROP );
  1058.  
  1059.         // Tell the spooler that we are starting a document.
  1060.  
  1061.         lError = DevEscape( hdc, DEVESC_STARTDOC, strlen( DOC_NAME ),
  1062.                             DOC_NAME, &lBytes, NULL );
  1063.  
  1064.         if( lError == DEV_OK )
  1065.         {
  1066.             CHAR szDirListing[ 80 ];
  1067.  
  1068.             FormatDirListing( szDirListing, pci );
  1069.  
  1070.             // Write the directory listing of the file to the printer
  1071.  
  1072.             lError = DevEscape( hdc, DEVESC_RAWDATA, strlen( szDirListing ),
  1073.                                 szDirListing, &lBytes, NULL );
  1074.  
  1075.             if( lError == DEVESC_ERROR )
  1076.                 Msg( "Bad DevEscape RAWDATA. RC(%X)", HWNDERR( hwndClient ) );
  1077.  
  1078.             // Tell the spooler that we are done printing this document
  1079.  
  1080.             lError = DevEscape( hdc, DEVESC_ENDDOC, 0, NULL, &lBytes, NULL );
  1081.  
  1082.             if( lError == DEVESC_ERROR )
  1083.                 Msg( "Bad DevEscape ENDDOC. RC(%X)", HWNDERR( hwndClient ) );
  1084.         }
  1085.         else
  1086.             Msg( "Bad DevEscape STARTDOC for %s. RC(%X)", DOC_NAME,
  1087.                  HWNDERR( hwndClient ) );
  1088.     }
  1089.  
  1090.     if( hdc )
  1091.         DevCloseDC( hdc );
  1092.     else
  1093.         Msg( "DevOpenDC failed. RC(%X)", HWNDERR( hwndClient ) );
  1094.  
  1095.     // Tell PM that we are doing the printing. We could return DRR_TARGET
  1096.     // which would leave the printing burden on the printer object or DRR_ABORT
  1097.     // which would tell PM to abort the drop.
  1098.  
  1099.     return (MRESULT) DRR_SOURCE;
  1100. }
  1101.  
  1102. /**********************************************************************/
  1103. /*------------------------ FormatDirListing --------------------------*/
  1104. /*                                                                    */
  1105. /*  FORMAT A DIRECTORY LISTING FOR THE FILE BEING PRINTED.            */
  1106. /*                                                                    */
  1107. /*  INPUT: pointer to buffer to hold formatted directory listing,     */
  1108. /*         pointer to CNRITEM container record                        */
  1109. /*                                                                    */
  1110. /*  1.                                                                */
  1111. /*                                                                    */
  1112. /*  OUTPUT: nothing                                                   */
  1113. /*                                                                    */
  1114. /*--------------------------------------------------------------------*/
  1115. /**********************************************************************/
  1116. static VOID FormatDirListing( PSZ pszDirListing, PCNRITEM pci )
  1117. {
  1118.     CNRITEM ci = *pci;
  1119.     CHAR    chAmPm, szSizeOrDir[ 15 ];
  1120.     INT     iYear;
  1121.  
  1122.     if( ci.time.hours == 0 )
  1123.     {
  1124.         ci.time.hours = 12;
  1125.         chAmPm = 'a';
  1126.     }
  1127.     else if( ci.time.hours == 12 )
  1128.         chAmPm = 'p';
  1129.     else if( ci.time.hours < 12 )
  1130.         chAmPm = 'a';
  1131.     else
  1132.     {
  1133.         ci.time.hours -= 12;
  1134.         chAmPm = 'p';
  1135.     }
  1136.  
  1137.     if( pci->attrFile & FILE_DIRECTORY )
  1138.         (void) strcpy( szSizeOrDir, "      <DIR>   " );
  1139.     else
  1140.         (void) sprintf( szSizeOrDir, "%14ld", pci->cbFile );
  1141.  
  1142.     iYear = ci.date.year + 1980;
  1143.  
  1144.     iYear -= ( iYear >= 2000 ? 2000 : 1900 );
  1145.  
  1146.     sprintf( pszDirListing, "%2u-%02u-%02u  %2u:%02u%c %s  %-40.40s\n",
  1147.             ci.date.month, ci.date.day, iYear, ci.time.hours, ci.time.minutes,
  1148.             chAmPm, szSizeOrDir, pci->szFileName );
  1149. }
  1150.  
  1151. /**********************************************************************/
  1152. /*-------------------------- DiscardObjects --------------------------*/
  1153. /*                                                                    */
  1154. /*  PROCESS DM_DISCARDOBJECT MESSAGE.                                 */
  1155. /*                                                                    */
  1156. /*  NOTE: We get a DM_DISCARDOBJECT message for each record being     */
  1157. /*        dropped. Since we get a DRAGINFO pointer the first time,    */
  1158. /*        we process all records the first time around. The rest of   */
  1159. /*        the times we go thru the motions but don't really do        */
  1160. /*        anything.                                                   */
  1161. /*                                                                    */
  1162. /*  INPUT: client window handle,                                      */
  1163. /*         pointer to DRAGINFO structure                              */
  1164. /*                                                                    */
  1165. /*  1.                                                                */
  1166. /*                                                                    */
  1167. /*  OUTPUT: DRR_SOURCE or DRR_TARGET                                  */
  1168. /*                                                                    */
  1169. /*--------------------------------------------------------------------*/
  1170. /**********************************************************************/
  1171. static MRESULT DiscardObjects( HWND hwndClient, PDRAGINFO pdinfo )
  1172. {
  1173.     HWND      hwndCnr = WinWindowFromID( hwndClient, CNR_DIRECTORY );
  1174.     INT       i;
  1175.     PDRAGITEM pditem;
  1176.     PCNRITEM  *apci;
  1177.  
  1178.     // Allocate memory for an array of container record pointers. This array
  1179.     // will be used during the CM_REMOVERECORD message to remove all dropped
  1180.     // records in one shot
  1181.  
  1182.     apci = (PCNRITEM *) malloc( pdinfo->cditem * sizeof( PCNRITEM ) );
  1183.  
  1184.     if( apci )
  1185.         (void) memset( apci, 0, pdinfo->cditem * sizeof( PCNRITEM ) );
  1186.     else
  1187.     {
  1188.         Msg( "Out of memory in DiscardObjects!" );
  1189.  
  1190.         return (MRESULT) DRR_ABORT;
  1191.     }
  1192.  
  1193.     if( apci )
  1194.     {
  1195.         INT iNumToDiscard = pdinfo->cditem;
  1196.  
  1197.         for( i = 0; i < pdinfo->cditem; i++ )
  1198.         {
  1199.             pditem = DrgQueryDragitemPtr( pdinfo, i );
  1200.  
  1201.             if( pditem )
  1202.                 apci[ i ] = (PCNRITEM) pditem->ulItemID;
  1203.             else
  1204.             {
  1205.                 iNumToDiscard--;
  1206.  
  1207.                 Msg( "DiscardObjects DrgQueryDragitemPtr RC(%X)",
  1208.                      HWNDERR( hwndClient ) );
  1209.             }
  1210.         }
  1211.  
  1212.         if( iNumToDiscard )
  1213.         {
  1214.             // If CMA_FREE is used on CM_REMOVERECORD, the program traps on the
  1215.             // second DM_DISCARDOBJECT. So we can't blindly use CMA_FREE on the
  1216.             // CM_REMOVERECORD call. See comment below.
  1217.  
  1218.             INT cRecsLeft = (INT) WinSendMsg( hwndCnr, CM_REMOVERECORD,
  1219.                      MPFROMP( apci ),
  1220.                      MPFROM2SHORT( iNumToDiscard, CMA_INVALIDATE ) );
  1221.  
  1222.             // -1 means invalid parameter. The problem is that we get as many
  1223.             // DM_DISCARDOBJECT messages as there are records dragged but the
  1224.             // first one has enough info to process all of them. The messages
  1225.             // after the first are irrelevant because we already did the
  1226.             // removing. Since subsequent DM_DISCARDOBJECT messages will cause
  1227.             // the above to fail (records were already moved the first time),
  1228.             // we need to not try and free the records here. If we do, we'll
  1229.             // trap because they were freed the first time.
  1230.  
  1231.             if( cRecsLeft != -1 )
  1232.                 WinSendMsg( hwndCnr, CM_FREERECORD, MPFROMP( apci ),
  1233.                             MPFROMSHORT( iNumToDiscard ) );
  1234.         }
  1235.     }
  1236.     else
  1237.         Msg( "Out of memory in DiscardObjects" );
  1238.  
  1239.     if( apci )
  1240.         free( apci );
  1241.  
  1242.     // Tell PM that we are doing the discarding. We could return DRR_TARGET
  1243.     // which would leave the discarding burden on the shredder object or
  1244.     // DRR_ABORT which would tell PM to abort the drop.
  1245.  
  1246.     return (MRESULT) DRR_SOURCE;
  1247. }
  1248.  
  1249. /**********************************************************************/
  1250. /*------------------------ WindowInOurProcess ------------------------*/
  1251. /*                                                                    */
  1252. /*  DETERMINE IF THE SOURCE WINDOW IS IN OUR PROCESS.                 */
  1253. /*                                                                    */
  1254. /*  INPUT: source window handle                                       */
  1255. /*                                                                    */
  1256. /*  1.                                                                */
  1257. /*                                                                    */
  1258. /*  OUTPUT: TRUE or FALSE if window is in our process                 */
  1259. /*                                                                    */
  1260. /*--------------------------------------------------------------------*/
  1261. /**********************************************************************/
  1262. static BOOL WindowInOurProcess( HWND hwndClient, HWND hwndSource )
  1263. {
  1264.     APIRET rc;
  1265.     PTIB   ptib;
  1266.     PPIB   ppib;
  1267.     PID    pid;
  1268.     TID    tid;
  1269.     BOOL   fSuccess, fOneOfOurs;
  1270.  
  1271.     // Get our Process ID
  1272.  
  1273.     rc = DosGetInfoBlocks( &ptib, &ppib );
  1274.  
  1275.     if( !rc )
  1276.     {
  1277.         // Get the source window's Process ID
  1278.  
  1279.         fSuccess = WinQueryWindowProcess( hwndSource, &pid, &tid );
  1280.  
  1281.         if( !fSuccess )
  1282.             Msg( "WinQueryWindowProcess RC(%X)", HWNDERR( hwndClient ) );
  1283.     }
  1284.     else
  1285.         Msg( "DosGetInfoBlocks RC(%X)", rc );
  1286.  
  1287.     if( !rc && fSuccess )
  1288.         fOneOfOurs = (pid == ppib->pib_ulpid) ? TRUE : FALSE;
  1289.     else
  1290.         fOneOfOurs = TRUE;
  1291.  
  1292.     return fOneOfOurs;
  1293. }
  1294.  
  1295. /**********************************************************************/
  1296. /*----------------------- RemoveSourceEmphasis -----------------------*/
  1297. /*                                                                    */
  1298. /*  REMOVE SOURCE EMPHASIS FROM THE DRAGGED RECORDS.                  */
  1299. /*                                                                    */
  1300. /*  INPUT: client window handle                                       */
  1301. /*                                                                    */
  1302. /*  1.                                                                */
  1303. /*                                                                    */
  1304. /*  OUTPUT: nothing                                                   */
  1305. /*                                                                    */
  1306. /*--------------------------------------------------------------------*/
  1307. /**********************************************************************/
  1308. static VOID RemoveSourceEmphasis( HWND hwndClient )
  1309. {
  1310.     PCNRITEM pci = (PCNRITEM) CMA_FIRST;
  1311.  
  1312.     // For every record with source emphasis, remove it.
  1313.  
  1314.     while( pci )
  1315.     {
  1316.         pci = (PCNRITEM) WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY,
  1317.                                          CM_QUERYRECORDEMPHASIS,
  1318.                                          MPFROMP( pci ),
  1319.                                          MPFROMSHORT( CRA_SOURCE ) );
  1320.  
  1321.         if( pci == (PCNRITEM) -1 )
  1322.             Msg( "RemoveSourceEmphasis..CM_QUERYRECORDEMPHASIS RC(%X)",
  1323.                  HWNDERR( hwndClient ) );
  1324.         else if( pci )
  1325.             if( !WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY,
  1326.                                     CM_SETRECORDEMPHASIS, MPFROMP( pci ),
  1327.                                     MPFROM2SHORT( FALSE, CRA_SOURCE ) ) )
  1328.                 Msg( "RemoveSourceEmphasis..CM_SETRECORDEMPHASIS RC(%X)",
  1329.                      HWNDERR( hwndClient ) );
  1330.     }
  1331. }
  1332.  
  1333. /**********************************************************************/
  1334. /*-------------------------- EndConversation -------------------------*/
  1335. /*                                                                    */
  1336. /*  FREE THE RESOURCES USED BY DRAG/DROP PROCESSING. ONLY DO THIS IF  */
  1337. /*  THIS IS THE LAST ITEM (there is one end-conversation message sent */
  1338. /*  to us for each item dropped).                                     */
  1339. /*                                                                    */
  1340. /*  INPUT: client window handle                                       */
  1341. /*                                                                    */
  1342. /*  1.                                                                */
  1343. /*                                                                    */
  1344. /*  OUTPUT: nothing                                                   */
  1345. /*                                                                    */
  1346. /*--------------------------------------------------------------------*/
  1347. /**********************************************************************/
  1348. static MRESULT EndConversation( HWND hwndClient )
  1349. {
  1350.     PINSTANCE pi = INSTDATA( hwndClient );
  1351.  
  1352.     if( !pi )
  1353.     {
  1354.         Msg( "EndConversation cant get Inst data RC(%X)", HWNDERR(hwndClient) );
  1355.  
  1356.         return 0;
  1357.     }
  1358.  
  1359.     if( --pi->cDragItems == 0 )
  1360.     {
  1361.         // Free the shared memory we got access to using DrgAccessDragInfo. If
  1362.         // the source process is the same as the target process, we get the
  1363.         // PMERR_SOURCE_SAME_AS_TARGET message. It's ok to get that - it just
  1364.         // means that we don't need to free the structure because the target
  1365.         // process already freed it (this is a way to not have to call the
  1366.         // WindowInOurProcess function called by other functions in this
  1367.         // module).
  1368.  
  1369.         if( !DrgFreeDraginfo( pi->pSavedDragInfo ) &&
  1370.             PMERR_SOURCE_SAME_AS_TARGET != HWNDERR( hwndClient ) )
  1371.             Msg( "EndConversation DrgFreeDraginfo RC(%X)", HWNDERR(hwndClient));
  1372.  
  1373.         pi->pSavedDragInfo = NULL;
  1374.     }
  1375.  
  1376.     return 0;
  1377. }
  1378.  
  1379. /*************************************************************************
  1380.  *                     E N D     O F     S O U R C E                     *
  1381.  *************************************************************************/
  1382.