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

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