home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / CNRMNU.ZIP / POPULATE.C < prev    next >
Text File  |  1993-01-01  |  38KB  |  818 lines

  1. /*********************************************************************
  2.  *                                                                   *
  3.  * MODULE NAME :  populate.c             AUTHOR:  Rick Fishman       *
  4.  * DATE WRITTEN:  10-24-92                                           *
  5.  *                                                                   *
  6.  * DESCRIPTION:                                                      *
  7.  *                                                                   *
  8.  *  This module is part of CNRMENU.EXE. It performs the function of  *
  9.  *  filling a container window with file icons. This processing is   *
  10.  *  taking place in a secondary thread that was started with         *
  11.  *  _beginthread from the CreateContainer function in create.c.      *
  12.  *                                                                   *
  13.  *  Recursion is used to fill the container with all subdirectories  *
  14.  *  from the base directory.  This is done to demonstrate the Tree   *
  15.  *  view.                                                            *
  16.  *                                                                   *
  17.  *  The reason this is in a separate thread is that, if we are       *
  18.  *  traversing the root directory and its subdirectories, it could   *
  19.  *  take a long time to fill the container.                          *
  20.  *                                                                   *
  21.  *  This thread posts a UM_CONTAINER_FILLED message to the client    *
  22.  *  window when the container is filled.                             *
  23.  *                                                                   *
  24.  *  The first container created in this test program will actually   *
  25.  *  go to the file system to get the files and will allocate memory  *
  26.  *  for the container records. All other containers will use shared  *
  27.  *  records from the first container. This necessitates different    *
  28.  *  functions for each case. The first container uses                *
  29.  *  ProcessDirectory to fill the container. All additional containers*
  30.  *  use InsertSharedDir to do this.                                  *
  31.  *                                                                   *
  32.  *                                                                   *
  33.  * CALLABLE FUNCTIONS:                                               *
  34.  *                                                                   *
  35.  *  VOID PopulateContainer( PVOID pThreadParms );                    *
  36.  *                                                                   *
  37.  * HISTORY:                                                          *
  38.  *                                                                   *
  39.  *  10-24-92 - Source copied from CNRBASE.EXE sample.                *
  40.  *             Added more variables in the THREADPARMS structure.    *
  41.  *             Depending on the values in the THREADPARMS struct,    *
  42.  *               call new function InsertSharedDir rather than the   *
  43.  *               ProcessDirectory function. This new function will   *
  44.  *               insert records from another container into the new  *
  45.  *               container (saving memory).                          *
  46.  *             Set new iDirPosition variable in CNRITEM struct in    *
  47.  *               FillInRecord function. Pass this variable as a      *
  48.  *               parameter to that function. Pass it thru from the   *
  49.  *               ProcessDirectory function on down.                  *
  50.  *                                                                   *
  51.  *  Rick Fishman                                                     *
  52.  *  Code Blazers, Inc.                                               *
  53.  *  4113 Apricot                                                     *
  54.  *  Irvine, CA. 92720                                                *
  55.  *  CIS ID: 72251,750                                                *
  56.  *                                                                   *
  57.  *********************************************************************/
  58.  
  59. #pragma strings(readonly)   // used for debug version of memory mgmt routines
  60.  
  61. /*********************************************************************/
  62. /*------- Include relevant sections of the OS/2 header files --------*/
  63. /*********************************************************************/
  64.  
  65. #define  INCL_DOSERRORS
  66. #define  INCL_DOSFILEMGR
  67. #define  INCL_DOSPROCESS
  68. #define  INCL_WINERRORS
  69. #define  INCL_WINFRAMEMGR
  70. #define  INCL_WINPOINTERS
  71. #define  INCL_WINSTDCNR
  72. #define  INCL_WINWINDOWMGR
  73.  
  74. /**********************************************************************/
  75. /*----------------------------- INCLUDES -----------------------------*/
  76. /**********************************************************************/
  77.  
  78. #include <os2.h>
  79. #include <stdarg.h>
  80. #include <stdio.h>
  81. #include <stdlib.h>
  82. #include <string.h>
  83. #include "cnrmenu.h"
  84.  
  85. /*********************************************************************/
  86. /*------------------- APPLICATION DEFINITIONS -----------------------*/
  87. /*********************************************************************/
  88.  
  89. #define FILES_TO_GET       100        // Nbr of files to search for at a time
  90. #define FF_BUFFSIZE        (sizeof( FILEFINDBUF3 ) * FILES_TO_GET)
  91.  
  92. /**********************************************************************/
  93. /*---------------------------- STRUCTURES ----------------------------*/
  94. /**********************************************************************/
  95.  
  96. /**********************************************************************/
  97. /*----------------------- FUNCTION PROTOTYPES ------------------------*/
  98. /**********************************************************************/
  99.  
  100. static VOID ProcessDirectory ( HAB habThread, HWND hwndCnr, PCNRITEM pciParent,
  101.                                PSZ szDirBase, PSZ szDirectory );
  102. static VOID RecurseSubdirs   ( HAB habThread, HWND hwndCnr, PCNRITEM pciParent,
  103.                                PSZ szDir );
  104. static BOOL InsertRecords    ( HAB habThread, HWND hwndCnr, PCNRITEM pciParent,
  105.                                PSZ szDir, PFILEFINDBUF3 pffb, ULONG cFiles,
  106.                                PINT piDirPosition );
  107. static BOOL FillInRecord     ( PCNRITEM pci, PSZ szDir, PFILEFINDBUF3 pffb,
  108.                                INT iDirPosition );
  109. static VOID InsertSharedDir  ( HAB hab, HWND hwndCnrShare, HWND hwndCnr,
  110.                                PCNRITEM pciShrParent, PCNRITEM pciParent );
  111. static VOID RecurseSharedDirs( HAB hab, HWND hwndCnrShare, HWND hwndCnr,
  112.                                PCNRITEM pciParent );
  113. static BOOL InsertSharedRecs ( HAB hab, HWND hwndCnrShare, HWND hwndCnr,
  114.                                PCNRITEM pciShrParent, PCNRITEM pciParent );
  115.  
  116. /**********************************************************************/
  117. /*------------------------ GLOBAL VARIABLES --------------------------*/
  118. /**********************************************************************/
  119.  
  120. /**********************************************************************/
  121. /*------------------------ PopulateContainer -------------------------*/
  122. /*                                                                    */
  123. /*  THREAD THAT FILLS THE CONTAINER WITH RECORDS.                     */
  124. /*                                                                    */
  125. /*  INPUT: pointer to thread parameters passed by main thread         */
  126. /*                                                                    */
  127. /*  1.                                                                */
  128. /*                                                                    */
  129. /*  OUTPUT: nothing                                                   */
  130. /*                                                                    */
  131. /*--------------------------------------------------------------------*/
  132. /**********************************************************************/
  133. VOID PopulateContainer( PVOID pThreadParms )
  134. {
  135.     HAB         hab;
  136.     HMQ         hmq = NULLHANDLE;
  137.     HWND        hwndClient = ((PTHREADPARMS) pThreadParms)->hwndClient;
  138.     HWND        hwndCnrShare = ((PTHREADPARMS) pThreadParms)->hwndCnrShare;
  139.     PCNRITEM    pciParent = ((PTHREADPARMS) pThreadParms)->pciParent;
  140.     PINSTANCE   pi = INSTDATA( hwndClient );
  141.  
  142.     // We must create a message queue so that this thread can do WinSendMsg's.
  143.     // All contact with the container is done with WinSendMsg.
  144.  
  145.     hab = WinInitialize( 0 );
  146.  
  147.     if( hab )
  148.         hmq = WinCreateMsgQueue( hab, 0 );
  149.     else
  150.         Msg( "PopulateContainer WinInitialize failed!" );
  151.  
  152.     if( hmq )
  153.     {
  154.         if( pi )
  155.         {
  156.             if( hwndCnrShare && pciParent )
  157.  
  158.             // If we were passed the above info, that means we are to get our
  159.             // container records from another container rather than allocating
  160.             // the record memory ourself. The NULL in the last parameter tells
  161.             // the new container to start at the top level of the tree even
  162.             // though it is using a subdirectory. pciParent contains the
  163.             // record from the old container that points to the starting place
  164.             // of the new container
  165.  
  166.                 InsertSharedDir( hab, hwndCnrShare,
  167.                                  WinWindowFromID( hwndClient, CNR_DIRECTORY ),
  168.                                  pciParent, NULL );
  169.             else
  170.  
  171.             // Insert the container records from the specified directory. Using
  172.             // NULL for the parent record tells ProcessDirectory that it is
  173.             // dealing with the top-level directory should recursion cause
  174.             // subdirectories to be expanded. The last parameter is a pointer
  175.             // to a subdirectory used during recursion and can be NULL here.
  176.  
  177.                 ProcessDirectory( hab,
  178.                                   WinWindowFromID( hwndClient, CNR_DIRECTORY ),
  179.                                   NULL, pi->szDirectory, NULL );
  180.         }
  181.         else
  182.             Msg( "PopulateContainer cant get Inst data. RC(%X)", HABERR( hab ));
  183.     }
  184.     else
  185.         Msg( "PopulateContainer CreateMsgQueue failed! RC(%X)", HABERR( hab ) );
  186.  
  187.     if( hmq )
  188.         (void) WinDestroyMsgQueue( hmq );
  189.  
  190.     if( hab )
  191.         (void) WinTerminate( hab );
  192.  
  193.     free( pThreadParms );
  194.  
  195.     // Let the primary thread know the container is filled
  196.  
  197.     WinPostMsg( hwndClient, UM_CONTAINER_FILLED, NULL, NULL );
  198.  
  199.     _endthread();
  200.  
  201.     return;
  202. }
  203.  
  204. /**********************************************************************/
  205. /*------------------------- ProcessDirectory -------------------------*/
  206. /*                                                                    */
  207. /*  POPULATE THE CONTAINER WITH THE CONTENTS OF A DIRECTORY           */
  208. /*                                                                    */
  209. /*  INPUT: anchor block handle for this thread,                       */
  210. /*         container window handle,                                   */
  211. /*         parent container record,                                   */
  212. /*         base directory name with drive qualifier,                  */
  213. /*         directory to display                                       */
  214. /*                                                                    */
  215. /*  1.                                                                */
  216. /*                                                                    */
  217. /*  OUTPUT: nothing                                                   */
  218. /*                                                                    */
  219. /*--------------------------------------------------------------------*/
  220. /**********************************************************************/
  221. static VOID ProcessDirectory( HAB hab, HWND hwndCnr, PCNRITEM pciParent,
  222.                               PSZ szDirBase, PSZ szDirectory )
  223. {
  224.     // Allocate a buffer big enough to hold FILES_TO_GET files. Then allocate
  225.     // a work buffer to hold the full file spec.
  226.  
  227.     PFILEFINDBUF3 pffb = malloc( FF_BUFFSIZE );
  228.     PSZ           szFileSpec = malloc( CCHMAXPATH + 1 );
  229.     INT           iDirPosition = 0;
  230.  
  231.     if( pffb && szFileSpec )
  232.     {
  233.         HDIR   hdir = HDIR_SYSTEM;
  234.         ULONG  ulMaxFiles = FILES_TO_GET;
  235.         PCH    pchEndPath;
  236.         APIRET rc;
  237.         PINSTANCE pi = INSTDATA( PARENT( hwndCnr ) );
  238.  
  239.         // Combine C:\DIR1\DIR2 and DIR3 to make C:\DIR1\DIR2\DIR3\*.*
  240.         // Keep a placeholder so we can strip the trailing '\*.*' after the
  241.         // DosFindFirst has completed.
  242.  
  243.         (void) strcpy( szFileSpec, szDirBase );
  244.  
  245.         if( szDirectory )
  246.         {
  247.             (void) strcat( szFileSpec, "\\" );
  248.  
  249.             (void) strcat( szFileSpec, szDirectory );
  250.         }
  251.  
  252.         pchEndPath = szFileSpec + strlen( szFileSpec );
  253.  
  254.         (void) strcat( szFileSpec, "\\*.*" );
  255.  
  256.         // Get buffer of files up to the maximum FILES_TO_GET files. Get both
  257.         // normal files and directories.
  258.  
  259.         rc = DosFindFirst( szFileSpec, &hdir, FILE_NORMAL | FILE_DIRECTORY,
  260.                            pffb, FF_BUFFSIZE, &ulMaxFiles, FIL_STANDARD );
  261.  
  262.         *pchEndPath = 0;
  263.  
  264.         // Let the user know what directory we're processing unless we're
  265.         // in the process of shutting down
  266.  
  267.         if( pi && !pi->fShutdown )
  268.             SetWindowTitle( PARENT( hwndCnr ), "%s: Processing %s...",
  269.                             PROGRAM_TITLE, szFileSpec );
  270.  
  271.         while( !rc )
  272.         {
  273.             // If the main thread wants to shutdown, accommodate it
  274.  
  275.             if( pi && pi->fShutdown )
  276.                 break;
  277.  
  278.             // Insert the files into the container. Pass a pointer to the int
  279.             // that keeps track of the relative position of the file within the
  280.             // directory. This variable will be incremented for every record
  281.             // inserted into the container. We do this so the user can sort by
  282.             // a different key and at a later time get back to this order by
  283.             // sorting by this variable.
  284.  
  285.             if( InsertRecords( hab, hwndCnr, pciParent, szFileSpec, pffb,
  286.                                ulMaxFiles, &iDirPosition ) )
  287.  
  288.                 // Get more files if there are any
  289.  
  290.                 rc = DosFindNext( hdir, pffb, FF_BUFFSIZE, &ulMaxFiles );
  291.             else
  292.                 rc = 1;
  293.         }
  294.  
  295.         DosFindClose( hdir );
  296.  
  297.         // Give up our timeslice so we don't monopolize the cpu
  298.  
  299.         DosSleep( 5 );
  300.  
  301.         // Recursively insert child records into the container for any
  302.         // subdirectories found under this directory
  303.  
  304.         if( pi && !pi->fShutdown )
  305.             RecurseSubdirs( hab, hwndCnr, pciParent, szFileSpec );
  306.     }
  307.     else
  308.         Msg( "ProcessDirectory Out of Memory!" );
  309.  
  310.     if( pffb )
  311.         free( pffb );
  312.  
  313.     if( szFileSpec )
  314.         free( szFileSpec );
  315.  
  316.     return;
  317. }
  318.  
  319. /**********************************************************************/
  320. /*-------------------------- RecurseSubdirs --------------------------*/
  321. /*                                                                    */
  322. /*  CALL ProcessDirectory FOR EACH SUBDIRECTORY OF THE BASE DIRECTORY */
  323. /*                                                                    */
  324. /*  INPUT: anchor block handle for this thread,                       */
  325. /*         container window handle,                                   */
  326. /*         parent container record,                                   */
  327. /*         base directory name with drive qualifier                   */
  328. /*                                                                    */
  329. /*  1.                                                                */
  330. /*                                                                    */
  331. /*  OUTPUT: nothing                                                   */
  332. /*                                                                    */
  333. /*--------------------------------------------------------------------*/
  334. /**********************************************************************/
  335. static VOID RecurseSubdirs( HAB hab, HWND hwndCnr, PCNRITEM pciParent,
  336.                             PSZ szDirBase )
  337. {
  338.     USHORT    usWhatRec = (pciParent == NULL) ? CMA_FIRST : CMA_FIRSTCHILD;
  339.     PCNRITEM  pciPrev = pciParent, pciNext;
  340.     PINSTANCE pi = INSTDATA( PARENT( hwndCnr ) );
  341.  
  342.     // Note that this function is called AFTER a subdirectory's files have
  343.     // been inserted into the container. We traverse those records to see if
  344.     // any are subdirectories themselves.
  345.  
  346.     while( fTrue )
  347.     {
  348.         // If the main thread wants to shutdown, accommodate it
  349.  
  350.         if( pi && pi->fShutdown )
  351.             break;
  352.  
  353.         // Get the next child record. We start with usWhatRec set to
  354.         // CMA_FIRSTCHILD, then use CMA_NEXT after getting the first child.
  355.         // This enumerates the children of pciParent. The docs don't
  356.         // go into it, but my tests show that using CMA_NEXT after a
  357.         // CMA_FIRSTCHILD will return NULL after the last *child* has been
  358.         // enumerated, rather than the last record in the container. Of course
  359.         // that is how it should work but it's not documented.
  360.  
  361.         // BUG NOTE (10/19/92): I had to use CMA_FIRST instead of CMA_FIRSTCHILD
  362.         // if pciParent is NULL (parent is the top container level). This bug
  363.         // is fixed in the Service Pack due any day now
  364.  
  365.         pciNext = WinSendMsg( hwndCnr, CM_QUERYRECORD, MPFROMP( pciPrev ),
  366.                               MPFROM2SHORT( usWhatRec, CMA_ITEMORDER ) );
  367.  
  368.         if( (INT) pciNext == -1 )
  369.         {
  370.             Msg( "RecurseSubdirs CM_QUERYRECORD RC(%X)", HABERR( hab ) );
  371.  
  372.             break;
  373.         }
  374.  
  375.         if( !pciNext )
  376.             break;
  377.  
  378.         // If we found a subdirectory that isn't '.' or '..', recursively
  379.         // call PopulateContainer for that subdirectory (remember, while we're
  380.         // in this function, the parent's ProcessDirectory hasn't completed
  381.         // yet)
  382.  
  383.         if( (pciNext->attrFile & FILE_DIRECTORY) &&
  384.              pciNext->szFileName[0] != '.' )
  385.             ProcessDirectory( hab, hwndCnr, pciNext, szDirBase,
  386.                               pciNext->szFileName );
  387.  
  388.         usWhatRec = CMA_NEXT;
  389.  
  390.         pciPrev = pciNext;
  391.     }
  392.  
  393.     return;
  394. }
  395.  
  396. /**********************************************************************/
  397. /*--------------------------- InsertRecords --------------------------*/
  398. /*                                                                    */
  399. /*  INSERT DIRECTORY ENTRIES INTO THE CONTAINER.                      */
  400. /*                                                                    */
  401. /*  INPUT: anchor block handle for this thread,                       */
  402. /*         container window handle,                                   */
  403. /*         parent container record,                                   */
  404. /*         directory being displayed,                                 */
  405. /*         buffer containing directory entries,                       */
  406. /*         count of files in directory buffer,                        */
  407. /*         pointer to relative position of this file in the directory */
  408. /*                                                                    */
  409. /*  1.                                                                */
  410. /*                                                                    */
  411. /*  OUTPUT: TRUE or FALSE if successful or not                        */
  412. /*                                                                    */
  413. /*--------------------------------------------------------------------*/
  414. /**********************************************************************/
  415. static BOOL InsertRecords( HAB hab, HWND hwndCnr, PCNRITEM pciParent,
  416.                            PSZ szDirectory, PFILEFINDBUF3 pffb, ULONG cFiles,
  417.                            PINT piDirPosition )
  418. {
  419.     BOOL     fSuccess = TRUE;
  420.     PBYTE    pbBuf = (PBYTE) pffb;
  421.     PCNRITEM pci;
  422.  
  423.     // Allocate memory for cFiles container records. EXTRA_RECORD_BYTES refers
  424.     // to the number of bytes per record over and above the MINIRECORDCORE
  425.     // structure size that we need per record. Take a look at the PCNRITEM
  426.     // struct in CNRMENU.H to see what kind of data we are storing. The good
  427.     // thing is that the container will allocate this for us during the
  428.     // CM_ALLOCRECORD message. When we do a CM_REMOVERECORD during WM_DESTROY
  429.     // processing, we can free all this container memory in 1 shot. Note that
  430.     // CM_ALLOCRECORD allocates a linked list of records and sets the
  431.     // MINIRECORDCORE.cb size field for each record. It also appears to
  432.     // zero out all allocated memory besides this .cb field. It knows to
  433.     // allocate enough memory for MINIRECORDCORE rather than RECORDCORE structs
  434.     // due to using CCS_MINIRECORDCORE on the WinCreateWindow of the container.
  435.  
  436.     pci = WinSendMsg( hwndCnr, CM_ALLOCRECORD, MPFROMLONG( EXTRA_RECORD_BYTES ),
  437.                       MPFROMLONG( cFiles ) );
  438.  
  439.     if( pci )
  440.     {
  441.         INT           i;
  442.         PFILEFINDBUF3 pffbFile;
  443.         RECORDINSERT  ri;
  444.         PCNRITEM      pciFirst = pci;
  445.         ULONG         cFilesInserted = cFiles;
  446.  
  447.         // Insert all files into the container in one shot by filling in each
  448.         // linked list node that the container allocated for us.
  449.  
  450.         for( i = 0; i < cFiles; i++ )
  451.         {
  452.             // Get next FILEFINDBUF3 structure that points to a found file.
  453.  
  454.             pffbFile = (PFILEFINDBUF3) pbBuf;
  455.  
  456.             // Fill in the container record with the file info. Pass the
  457.             // integer that keeps track of the relative position of this file
  458.             // within the directory. We will add this to the CNRITEM struct for
  459.             // this record for later use during a sort by directory order.
  460.  
  461.             if( FillInRecord( pci, szDirectory, pffbFile, ++(*piDirPosition) ) )
  462.  
  463.                 // Get the next container record in the linked list that the
  464.                 // container allocated for us.
  465.  
  466.                 pci = (PCNRITEM) pci->rc.preccNextRecord;
  467.             else
  468.                 cFilesInserted--;
  469.  
  470.             // Point to the next file in the buffer. This is done by adding
  471.             // an offset value to the current location in the buffer. Since
  472.             // the file name is variable length, this offset points to the
  473.             // end of the current file name and the beginning of the next
  474.             // one.
  475.  
  476.             pbBuf += pffbFile->oNextEntryOffset;
  477.         }
  478.  
  479.         // Use the RECORDINSERT structure to tell the container how to
  480.         // insert this batch of records. Here we ask to insert the
  481.         // records at the end of the linked list. The parent record indicates
  482.         // who to stick this batch of records under (if pciParent is NULL, the
  483.         // records are at the top level). (Child records are only displayed in
  484.         // Tree view). The zOrder is used for icon view only and specifies the
  485.         // ZORDER that places one record on top of another. In this case we are
  486.         // placing this batch of records at the top of the ZORDER. Also since
  487.         // fInvalidateRecord is TRUE, we will cause the records to be painted
  488.         // as they are inserted. In a container with a small amount of records
  489.         // you probably want to set this to FALSE and do a CM_INVALIDATERECORD
  490.         // (using 0 for cNumRecord) after all records have been inserted. But
  491.         // here the user needs visual feedback if a large amount of
  492.         // subdirectories are found.
  493.  
  494.         (void) memset( &ri, 0, sizeof( RECORDINSERT ) );
  495.  
  496.         ri.cb                 = sizeof( RECORDINSERT );
  497.         ri.pRecordOrder       = (PRECORDCORE) CMA_END;
  498.         ri.pRecordParent      = (PRECORDCORE) pciParent;
  499.         ri.zOrder             = (USHORT) CMA_TOP;
  500.         ri.cRecordsInsert     = cFilesInserted;
  501.         ri.fInvalidateRecord  = TRUE;
  502.  
  503.         if( !WinSendMsg( hwndCnr, CM_INSERTRECORD, MPFROMP( pciFirst ),
  504.                          MPFROMP( &ri ) ) )
  505.         {
  506.             fSuccess = FALSE;
  507.  
  508.             Msg( "InsertRecords CM_INSERTRECORD RC(%X)", HABERR( hab ) );
  509.         }
  510.     }
  511.     else
  512.     {
  513.         fSuccess = FALSE;
  514.  
  515.         Msg( "InsertRecords CM_ALLOCRECORD RC(%X)", HABERR( hab ) );
  516.     }
  517.  
  518.     return fSuccess;
  519. }
  520.  
  521. /**********************************************************************/
  522. /*-------------------------- FillInRecord ----------------------------*/
  523. /*                                                                    */
  524. /*  POPULATE CONTAINER RECORD WITH FILE INFORMATION                   */
  525. /*                                                                    */
  526. /*  INPUT: pointer to record buffer to fill,                          */
  527. /*         directory path of file,                                    */
  528. /*         pointer to FILEFINDBUF3 that describes the file,           */
  529. /*         relative position of this file in the directory            */
  530. /*                                                                    */
  531. /*  1.                                                                */
  532. /*                                                                    */
  533. /*  OUTPUT: TRUE or FALSE if successful or not                        */
  534. /*                                                                    */
  535. /*--------------------------------------------------------------------*/
  536. /**********************************************************************/
  537. static BOOL FillInRecord( PCNRITEM pci, PSZ szDirectory, PFILEFINDBUF3 pffb,
  538.                           INT iDirPosition )
  539. {
  540.     BOOL     fSuccess = TRUE;
  541.     CHAR     szFullFileName[ CCHMAXPATH + 1 ];
  542.     HPOINTER hptr;
  543.  
  544.     // Copy the file name into the storage allocated by the container.
  545.  
  546.     (void) memset( pci->szFileName, 0, sizeof( pci->szFileName ) );
  547.     (void) memcpy( pci->szFileName, pffb->achName, pffb->cchName );
  548.  
  549.     // Get the fully qualified path for this file
  550.  
  551.     (void) memset( szFullFileName, 0, sizeof( szFullFileName ) );
  552.  
  553.     (void) strcpy( szFullFileName, szDirectory );
  554.  
  555.     szFullFileName[ strlen( szFullFileName ) ] = '\\';
  556.  
  557.     (void) strcat( szFullFileName, pci->szFileName );
  558.  
  559.     // Let PM get the icon for us by using WinLoadFileIcon. Note that a
  560.     // WinFreeFileIcon for this icon is not necessary because we are getting
  561.     // a shared copy by using FALSE as the last parameter. If you do a
  562.     // WinFreeFileIcon on this hptr you will get a PMERR_INVALID_PROCESS_ID.
  563.  
  564.     hptr = WinLoadFileIcon( szFullFileName, FALSE );
  565.  
  566.     // WinLoadFileIcon doesn't allow for any error investigation
  567.     // (WinGetLastError always returns zero). It seems to fail when a file is
  568.     // opened in write mode since it fails on the CNRMENU debug file and on the
  569.     // .INI files. Use a default icon if it fails.
  570.  
  571.     if( !hptr )
  572.         hptr = WinQuerySysPointer( HWND_DESKTOP, SPTR_QUESICON, FALSE );
  573.  
  574.     // Set up the file name pointer to point to the file name. This is
  575.     // crucial because we instructed the container in the
  576.     // SetContainerColumns function (CREATE.C) to use rc.pszIcon as a pointer
  577.     // to the file name (for reasons explained in that function).
  578.  
  579.     // Fill in all fields of the container record.
  580.  
  581.     pci->date.day       = pffb->fdateLastWrite.day;
  582.     pci->date.month     = pffb->fdateLastWrite.month;
  583.     pci->date.year      = pffb->fdateLastWrite.year + 1980;
  584.     pci->time.seconds   = pffb->ftimeLastWrite.twosecs;
  585.     pci->time.minutes   = pffb->ftimeLastWrite.minutes;
  586.     pci->time.hours     = pffb->ftimeLastWrite.hours;
  587.     pci->cbFile         = pffb->cbFile;
  588.     pci->attrFile       = pffb->attrFile;
  589.     pci->iDirPosition   = iDirPosition;
  590.  
  591.     // Fill in all fields of the MINIRECORDCORE structure. Note that the .cb
  592.     // field of the MINIRECORDCORE struct was filled in by CM_ALLOCRECORD.
  593.  
  594.     pci->rc.pszIcon     = pci->szFileName;
  595.     pci->rc.hptrIcon    = hptr;
  596.  
  597.     return fSuccess;
  598. }
  599.  
  600. /**********************************************************************/
  601. /*-------------------------- InsertSharedDir -------------------------*/
  602. /*                                                                    */
  603. /*  FILL A NEW CONTAINER WITH THE RECORDS FROM ANOTHER CONTAINER.     */
  604. /*                                                                    */
  605. /*  INPUT: thread's anchor block handle,                              */
  606. /*         window handle of old container that has the records,       */
  607. /*         window handle of new container,                            */
  608. /*         CNRITEM that is the parent record in the old container,    */
  609. /*         CNRITEM that is the parent record in the new container     */
  610. /*                                                                    */
  611. /*  1.                                                                */
  612. /*                                                                    */
  613. /*  OUTPUT: nothing                                                   */
  614. /*                                                                    */
  615. /*--------------------------------------------------------------------*/
  616. /**********************************************************************/
  617. static VOID InsertSharedDir( HAB hab, HWND hwndCnrShare, HWND hwndCnr,
  618.                              PCNRITEM pciShrParent, PCNRITEM pciParent )
  619. {
  620.     PINSTANCE pi = INSTDATA( PARENT( hwndCnr ) );
  621.  
  622.     if( !pi )
  623.     {
  624.         Msg( "InsertSharedDir cant get Inst Data RC(%X)", HABERR( hab ) );
  625.  
  626.         return;
  627.     }
  628.  
  629.     SetWindowTitle( PARENT( hwndCnr ), "%s: Processing %s\\%s...",
  630.                     PROGRAM_TITLE, pi->szDirectory,
  631.                     pciParent ? pciParent->szFileName : "" );
  632.  
  633.     if( InsertSharedRecs( hab, hwndCnrShare, hwndCnr, pciShrParent, pciParent ))
  634.     {
  635.         // If the main thread wants to shutdown, accommodate it
  636.  
  637.         if( pi->fShutdown )
  638.             return;
  639.  
  640.         // Invalidate all the records at once if we are at the top level. We
  641.         // do this because we insert the records one at a time and invalidating
  642.         // each individually affects performance. If we are not at the top
  643.         // level, we invalidate individually because the user cannot see that
  644.         // and it is easier.
  645.  
  646.         if( !pciParent )
  647.             if( !WinSendMsg( hwndCnr, CM_INVALIDATERECORD, NULL, NULL ) )
  648.                 Msg( "InsertSharedDir CM_INVALIDATERECORD RC(%X)", HABERR(hab));
  649.  
  650.         // Give up our timeslice so we don't monopolize the cpu
  651.  
  652.         DosSleep( 5 );
  653.  
  654.         // Now handle subdirectories of this directory
  655.  
  656.         RecurseSharedDirs( hab, hwndCnrShare, hwndCnr, pciShrParent );
  657.     }
  658.  
  659.     return;
  660. }
  661.  
  662. /**********************************************************************/
  663. /*------------------------ RecurseSharedDirs -------------------------*/
  664. /*                                                                    */
  665. /*  RECURSIVE FUNCTION THAT INSERTS SHARED RECORDS INTO A CONTAINER.  */
  666. /*                                                                    */
  667. /*  INPUT: thread's anchor block handle,                              */
  668. /*         container window handle containing shared records,         */
  669. /*         container window handle to insert records into,            */
  670. /*         parent record in the new container                         */
  671. /*                                                                    */
  672. /*  1.                                                                */
  673. /*                                                                    */
  674. /*  OUTPUT: nothing                                                   */
  675. /*                                                                    */
  676. /*--------------------------------------------------------------------*/
  677. /**********************************************************************/
  678. static VOID RecurseSharedDirs( HAB hab, HWND hwndCnrShare, HWND hwndCnr,
  679.                                PCNRITEM pciParent )
  680. {
  681.     USHORT    usWhatRec = (pciParent==NULL) ? CMA_FIRST : CMA_FIRSTCHILD;
  682.     PCNRITEM  pciPrev = pciParent, pciNext;
  683.     PINSTANCE pi = INSTDATA( PARENT( hwndCnr ) );
  684.  
  685.     while( fTrue )
  686.     {
  687.         // If the main thread wants to shutdown, accommodate it
  688.  
  689.         if( pi && pi->fShutdown )
  690.             break;
  691.  
  692.         // Get the next child record. We start with usWhatRec set to
  693.         // CMA_FIRSTCHILD, then use CMA_NEXT after getting the first child.
  694.         // This enumerates the children of pciParent. The docs don't
  695.         // go into it, but my tests show that using CMA_NEXT after a
  696.         // CMA_FIRSTCHILD will return NULL after the last *child* has been
  697.         // enumerated, rather than the last record in the container. Of course
  698.         // that is how it should work but it's not documented.
  699.  
  700.         pciNext = WinSendMsg( hwndCnrShare, CM_QUERYRECORD, MPFROMP( pciPrev ),
  701.                               MPFROM2SHORT( usWhatRec, CMA_ITEMORDER ) );
  702.  
  703.         if( (INT) pciNext == -1 )
  704.         {
  705.             Msg( "RecurseSharedDirs CM_QUERYRECORD RC(%X)", HABERR( hab ) );
  706.  
  707.             break;
  708.         }
  709.  
  710.         if( !pciNext )
  711.             break;
  712.  
  713.         if( (pciNext->attrFile & FILE_DIRECTORY) &&
  714.              pciNext->szFileName[0] != '.' )
  715.             InsertSharedDir( hab, hwndCnrShare, hwndCnr, pciNext, pciNext );
  716.  
  717.         usWhatRec = CMA_NEXT;
  718.  
  719.         pciPrev = pciNext;
  720.     }
  721.  
  722.     return;
  723. }
  724.  
  725. /**********************************************************************/
  726. /*------------------------ InsertSharedRecs --------------------------*/
  727. /*                                                                    */
  728. /*  INSERT SHARED RECORDS FROM OLD CONTAINER TO NEW CONTAINER.        */
  729. /*                                                                    */
  730. /*  INPUT: thread's anchor block handle,                              */
  731. /*         container window handle containing shared records,         */
  732. /*         new container window handle,                               */
  733. /*         parent record in the container with the shared records,    */
  734. /*         parent record in new container                             */
  735. /*                                                                    */
  736. /*  1.                                                                */
  737. /*                                                                    */
  738. /*  OUTPUT: TRUE or FALSE if successful or not                        */
  739. /*                                                                    */
  740. /*--------------------------------------------------------------------*/
  741. /**********************************************************************/
  742. static BOOL InsertSharedRecs( HAB hab, HWND hwndCnrShare, HWND hwndCnr,
  743.                               PCNRITEM pciShrParent, PCNRITEM pciParent )
  744. {
  745.     BOOL         fSuccess = TRUE;
  746.     USHORT       usWhatRec = (pciShrParent==NULL) ? CMA_FIRST : CMA_FIRSTCHILD;
  747.     PCNRITEM     pciPrev = pciShrParent, pciNext;
  748.     RECORDINSERT ri;
  749.  
  750.     // We insert the shared records one record at a time because the container
  751.     // seems to be very touchy if records are inserted in 100-record blocks,
  752.     // then sorted in the original container. If after they are sorted in a
  753.     // different way that they were inserted into the container and we try and
  754.     // insert them as shared records in another container we get strange errors
  755.     // on the insert. So we remain safe and insert one at a time. Because of
  756.     // that we don't invalidate each record on the insert if we are at the top
  757.     // level because the user would see that. Instead we will invalidate all
  758.     // top level records at once in the function that called us. If we are not
  759.     // on the top level, we invalidate as we go. This affects performance a bit
  760.     // but at least the user doesn't really notice it.
  761.  
  762.     (void) memset( &ri, 0, sizeof( RECORDINSERT ) );
  763.  
  764.     ri.cb                 = sizeof( RECORDINSERT );
  765.     ri.pRecordOrder       = (PRECORDCORE) CMA_END;
  766.     ri.pRecordParent      = (PRECORDCORE) pciParent;
  767.     ri.zOrder             = (USHORT) CMA_TOP;
  768.     ri.cRecordsInsert     = 1;
  769.     ri.fInvalidateRecord  = pciParent ? TRUE : FALSE;
  770.  
  771.     while( fTrue )
  772.     {
  773.         // Get the next child record. We start with usWhatRec set to
  774.         // CMA_FIRSTCHILD, then use CMA_NEXT after getting the first child.
  775.         // This enumerates the children of pciParent. The docs don't
  776.         // go into it, but my tests show that using CMA_NEXT after a
  777.         // CMA_FIRSTCHILD will return NULL after the last *child* has been
  778.         // enumerated, rather than the last record in the container. Of course
  779.         // that is how it should work but it's not documented.
  780.  
  781.         pciNext = WinSendMsg( hwndCnrShare, CM_QUERYRECORD, MPFROMP( pciPrev ),
  782.                               MPFROM2SHORT( usWhatRec, CMA_ITEMORDER ) );
  783.  
  784.         if( (INT) pciNext == -1 )
  785.         {
  786.             Msg( "InsertSharedRecs CM_QUERYRECORD RC(%X)", HABERR( hab ) );
  787.  
  788.             fSuccess = FALSE;
  789.  
  790.             break;
  791.         }
  792.  
  793.         if( !pciNext )
  794.             break;
  795.  
  796.         if( !WinSendMsg( hwndCnr, CM_INSERTRECORD, MPFROMP( pciNext ),
  797.                          MPFROMP( &ri ) ) )
  798.         {
  799.             Msg( "InsertSharedRecs CM_INSERTRECORD for %s RC(%X)",
  800.                  pciNext ? pciNext->szFileName : "", HABERR( hab ) );
  801.  
  802.             fSuccess = FALSE;
  803.  
  804.             break;
  805.         }
  806.  
  807.         usWhatRec = CMA_NEXT;
  808.  
  809.         pciPrev = pciNext;
  810.     }
  811.  
  812.     return fSuccess;
  813. }
  814.  
  815. /*************************************************************************
  816.  *                     E N D     O F     S O U R C E                     *
  817.  *************************************************************************/
  818.