home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / c / cnrbas.zip / POPULATE.C < prev    next >
Text File  |  1992-10-13  |  24KB  |  524 lines

  1. /*********************************************************************
  2.  *                                                                   *
  3.  * MODULE NAME :  populate.c             AUTHOR:  Rick Fishman       *
  4.  * DATE WRITTEN:  10-09-92                                           *
  5.  *                                                                   *
  6.  * DESCRIPTION:                                                      *
  7.  *                                                                   *
  8.  *  This module is part of CNRBASE.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.  * CALLABLE FUNCTIONS:                                               *
  25.  *                                                                   *
  26.  *  VOID PopulateContainer( PVOID pThreadParms );                    *
  27.  *                                                                   *
  28.  * HISTORY:                                                          *
  29.  *                                                                   *
  30.  *  10-09-92 - Program coded                                         *
  31.  *                                                                   *
  32.  *  Rick Fishman                                                     *
  33.  *  Code Blazers, Inc.                                               *
  34.  *  4113 Apricot                                                     *
  35.  *  Irvine, CA. 92720                                                *
  36.  *  CIS ID: 72251,750                                                *
  37.  *                                                                   *
  38.  *********************************************************************/
  39.  
  40. #pragma strings(readonly)   // used for debug version of memory mgmt routines
  41.  
  42. /*********************************************************************/
  43. /*------- Include relevant sections of the OS/2 header files --------*/
  44. /*********************************************************************/
  45.  
  46. #define  INCL_DOSERRORS
  47. #define  INCL_DOSFILEMGR
  48. #define  INCL_WINERRORS
  49. #define  INCL_WINFRAMEMGR
  50. #define  INCL_WINSTDCNR
  51. #define  INCL_WINWINDOWMGR
  52.  
  53. /**********************************************************************/
  54. /*----------------------------- INCLUDES -----------------------------*/
  55. /**********************************************************************/
  56.  
  57. #include <os2.h>
  58. #include <stdarg.h>
  59. #include <stdio.h>
  60. #include <stdlib.h>
  61. #include <string.h>
  62. #include "cnrbase.h"
  63.  
  64. /*********************************************************************/
  65. /*------------------- APPLICATION DEFINITIONS -----------------------*/
  66. /*********************************************************************/
  67.  
  68. #define FILES_TO_GET       100         // Nbr of files to search for at a time
  69. #define FF_BUFFSIZE        (sizeof( FILEFINDBUF3 ) * FILES_TO_GET)
  70.  
  71. /**********************************************************************/
  72. /*---------------------------- STRUCTURES ----------------------------*/
  73. /**********************************************************************/
  74.  
  75. /**********************************************************************/
  76. /*----------------------- FUNCTION PROTOTYPES ------------------------*/
  77. /**********************************************************************/
  78.  
  79. static VOID ProcessDirectory ( HWND hwndCnr, PCNRITEM pciParent, PSZ szDirBase,
  80.                                PSZ szDirectory );
  81. static VOID RecurseSubdirs   ( HWND hwndCnr, PCNRITEM pciParent, PSZ szDir );
  82. static BOOL InsertRecords    ( HWND hwndCnr, PCNRITEM pciParent, PSZ szDir,
  83.                                PFILEFINDBUF3 pffb, ULONG cFiles);
  84. static BOOL FillInRecord     ( PCNRITEM pci, PSZ szDir, PFILEFINDBUF3 pffb );
  85.  
  86. /**********************************************************************/
  87. /*------------------------ GLOBAL VARIABLES --------------------------*/
  88. /**********************************************************************/
  89.  
  90. /**********************************************************************/
  91. /*------------------------ PopulateContainer -------------------------*/
  92. /*                                                                    */
  93. /*  THREAD THAT FILLS THE CONTAINER WITH RECORDS.                     */
  94. /*                                                                    */
  95. /*  INPUT: pointer to thread parameters passed by main thread         */
  96. /*                                                                    */
  97. /*  1.                                                                */
  98. /*                                                                    */
  99. /*  OUTPUT: nothing                                                   */
  100. /*                                                                    */
  101. /*--------------------------------------------------------------------*/
  102. /**********************************************************************/
  103. VOID PopulateContainer( PVOID pThreadParms )
  104. {
  105.     HAB         hab;
  106.     HMQ         hmq = NULLHANDLE;
  107.     HWND        hwndClient = ((PTHREADPARMS) pThreadParms)->hwndClient;
  108.     PINSTANCE   pi = INSTDATA( hwndClient );
  109.  
  110.     // We must create a message queue so that this thread can do WinSendMsg's.
  111.     // All contact with the container is done with WinSendMsg.
  112.  
  113.     hab = WinInitialize( 0 );
  114.  
  115.     if( hab )
  116.         hmq = WinCreateMsgQueue( hab, 0 );
  117.     else
  118.         Msg( "PopulateContainer WinInitialize failed!" );
  119.  
  120.     if( hmq )
  121.     {
  122.         if( pi )
  123.         {
  124.             // Insert the container records from the specified directory. Using
  125.             // NULL for the parent record tells ProcessDirectory that it is
  126.             // dealing with the top-level directory should recursion cause
  127.             // subdirectories to be expanded. The last parameter is a pointer
  128.             // to a subdirectory used during recursion and can be NULL here.
  129.  
  130.             ProcessDirectory( WinWindowFromID( hwndClient, CNR_DIRECTORY ),
  131.                               NULL, pi->szDirectory, NULL );
  132.         }
  133.         else
  134.             Msg( "PopulateContainer cant get Inst data. RC(%X)",
  135.                  HWNDERR( hwndClient ) );
  136.     }
  137.     else
  138.         Msg( "PopulateContainer CreateMsgQueue failed! RC(%X)", HABERR( hab ) );
  139.  
  140.     if( hmq )
  141.         (void) WinDestroyMsgQueue( hmq );
  142.  
  143.     if( hab )
  144.         (void) WinTerminate( hab );
  145.  
  146.     free( pThreadParms );
  147.  
  148.     // Let the primary thread know the container is filled
  149.  
  150.     WinPostMsg( hwndClient, UM_CONTAINER_FILLED, NULL, NULL );
  151.  
  152.     _endthread();
  153. }
  154.  
  155. /**********************************************************************/
  156. /*------------------------- ProcessDirectory -------------------------*/
  157. /*                                                                    */
  158. /*  POPULATE THE CONTAINER WITH THE CONTENTS OF A DIRECTORY           */
  159. /*                                                                    */
  160. /*  INPUT: container window handle,                                   */
  161. /*         parent container record,                                   */
  162. /*         base directory name with drive qualifier,                  */
  163. /*         directory to display                                       */
  164. /*                                                                    */
  165. /*  1.                                                                */
  166. /*                                                                    */
  167. /*  OUTPUT: nothing                                                   */
  168. /*                                                                    */
  169. /*--------------------------------------------------------------------*/
  170. /**********************************************************************/
  171. static VOID ProcessDirectory( HWND hwndCnr, PCNRITEM pciParent,
  172.                               PSZ szDirBase, PSZ szDirectory )
  173. {
  174.     // Allocate a buffer big enough to hold FILES_TO_GET files. Then allocate
  175.     // a work buffer to hold the full file spec.
  176.  
  177.     PFILEFINDBUF3 pffb = malloc( FF_BUFFSIZE );
  178.     PSZ           szFileSpec = malloc( CCHMAXPATH + 1 );
  179.  
  180.     if( pffb && szFileSpec )
  181.     {
  182.         HDIR   hdir = HDIR_SYSTEM;
  183.         ULONG  ulMaxFiles = FILES_TO_GET;
  184.         PCH    pchEndPath;
  185.         APIRET rc;
  186.         PINSTANCE pi = INSTDATA( PARENT( hwndCnr ) );
  187.  
  188.         // Combine C:\DIR1\DIR2 and DIR3 to make C:\DIR1\DIR2\DIR3\*.*
  189.         // Keep a placeholder so we can strip the trailing '\*.*' after the
  190.         // DosFindFirst has completed.
  191.  
  192.         (void) strcpy( szFileSpec, szDirBase );
  193.  
  194.         if( szDirectory )
  195.         {
  196.             (void) strcat( szFileSpec, "\\" );
  197.  
  198.             (void) strcat( szFileSpec, szDirectory );
  199.         }
  200.  
  201.         pchEndPath = szFileSpec + strlen( szFileSpec );
  202.  
  203.         (void) strcat( szFileSpec, "\\*.*" );
  204.  
  205.         // Get buffer of files up to the maximum FILES_TO_GET files. Get both
  206.         // normal files and directories.
  207.  
  208.         rc = DosFindFirst( szFileSpec, &hdir, FILE_NORMAL | FILE_DIRECTORY,
  209.                            pffb, FF_BUFFSIZE, &ulMaxFiles, FIL_STANDARD );
  210.  
  211.         *pchEndPath = 0;
  212.  
  213.         // Let the user know what directory we're processing unless we're
  214.         // in the process of shutting down
  215.  
  216.         if( pi && !pi->fShutdown )
  217.             SetWindowTitle( PARENT( hwndCnr ), "%s: Processing %s...",
  218.                             PROGRAM_TITLE, szFileSpec );
  219.  
  220.         while( !rc )
  221.         {
  222.             // If the main thread wants to shutdown, accommodate it
  223.  
  224.             if( pi && pi->fShutdown )
  225.                 break;
  226.  
  227.             // Insert the files into the container
  228.  
  229.             if( InsertRecords( hwndCnr, pciParent, szFileSpec, pffb,
  230.                                ulMaxFiles ) )
  231.             {
  232.                 // Recursively insert child records into the container for any
  233.                 // subdirectories found under this directory
  234.  
  235.                 RecurseSubdirs( hwndCnr, pciParent, szFileSpec );
  236.  
  237.                 // Get more files if there are any
  238.  
  239.                 rc = DosFindNext( hdir, pffb, FF_BUFFSIZE, &ulMaxFiles );
  240.             }
  241.             else
  242.                 rc = 1;
  243.         }
  244.  
  245.         DosFindClose( hdir );
  246.     }
  247.     else
  248.         Msg( "ProcessDirectory Out of Memory!" );
  249.  
  250.     if( pffb )
  251.         free( pffb );
  252.  
  253.     if( szFileSpec )
  254.         free( szFileSpec );
  255. }
  256.  
  257. /**********************************************************************/
  258. /*-------------------------- RecurseSubdirs --------------------------*/
  259. /*                                                                    */
  260. /*  CALL ProcessDirectory FOR EACH SUBDIRECTORY OF THE BASE DIRECTORY */
  261. /*                                                                    */
  262. /*  INPUT: container window handle,                                   */
  263. /*         parent container record,                                   */
  264. /*         base directory name with drive qualifier                   */
  265. /*                                                                    */
  266. /*  1.                                                                */
  267. /*                                                                    */
  268. /*  OUTPUT: nothing                                                   */
  269. /*                                                                    */
  270. /*--------------------------------------------------------------------*/
  271. /**********************************************************************/
  272. static VOID RecurseSubdirs( HWND hwndCnr, PCNRITEM pciParent, PSZ szDirBase )
  273. {
  274.     USHORT    usWhatRec = CMA_FIRSTCHILD;
  275.     PCNRITEM  pci = pciParent;
  276.     PINSTANCE pi = INSTDATA( PARENT( hwndCnr ) );
  277.  
  278.     // Note that this function is called AFTER a subdirectory's files have
  279.     // been inserted into the container. We traverse those records to see if
  280.     // any are subdirectories themselves.
  281.  
  282.     for( ; ; )
  283.     {
  284.         // If the main thread wants to shutdown, accommodate it
  285.  
  286.         if( pi && pi->fShutdown )
  287.             break;
  288.  
  289.         // Get the next child record. We start with usWhatRec set to
  290.         // CMA_FIRSTCHILD, then use CMA_NEXT after getting the first child.
  291.         // This enumerates the children of pciParent. The docs don't
  292.         // go into it, but my tests show that using CMA_NEXT after a
  293.         // CMA_FIRSTCHILD will return NULL after the last *child* has been
  294.         // enumerated, rather than the last record in the container. Of course
  295.         // that is how it should work but it's not documented.
  296.  
  297.         pci = WinSendMsg( hwndCnr, CM_QUERYRECORD, MPFROMP( pci ),
  298.                           MPFROM2SHORT( usWhatRec, CMA_ITEMORDER ) );
  299.  
  300.         if( (INT) pci == -1 )
  301.         {
  302.             Msg( "RecurseSubdirs CM_QUERYRECORD RC(%X)", HWNDERR( hwndCnr ) );
  303.  
  304.             break;
  305.         }
  306.  
  307.         if( !pci )
  308.             break;
  309.  
  310.         // If we found a subdirectory that isn't '.' or '..', recursively
  311.         // call PopulateContainer for that subdirectory (remember, while we're
  312.         // in this function, the parent's PopulateContainer hasn't completed
  313.         // yet)
  314.  
  315.         if( (pci->attrFile & FILE_DIRECTORY) && pci->szFileName[0] != '.' )
  316.             ProcessDirectory( hwndCnr, pci, szDirBase, pci->szFileName );
  317.  
  318.         usWhatRec = CMA_NEXT;
  319.     }
  320. }
  321.  
  322. /**********************************************************************/
  323. /*--------------------------- InsertRecords --------------------------*/
  324. /*                                                                    */
  325. /*  INSERT DIRECTORY ENTRIES INTO THE CONTAINER.                      */
  326. /*                                                                    */
  327. /*  INPUT: container window handle,                                   */
  328. /*         parent container record,                                   */
  329. /*         directory being displayed,                                 */
  330. /*         buffer containing directory entries,                       */
  331. /*         count of files in directory buffer                         */
  332. /*                                                                    */
  333. /*  1.                                                                */
  334. /*                                                                    */
  335. /*  OUTPUT: TRUE or FALSE if successful or not                        */
  336. /*                                                                    */
  337. /*--------------------------------------------------------------------*/
  338. /**********************************************************************/
  339. static BOOL InsertRecords( HWND hwndCnr, PCNRITEM pciParent, PSZ szDirectory,
  340.                            PFILEFINDBUF3 pffb, ULONG cFiles )
  341. {
  342.     BOOL     fSuccess = TRUE;
  343.     PBYTE    pbBuf = (PBYTE) pffb;
  344.     PCNRITEM pci;
  345.  
  346.     // Allocate memory for cFiles container records. EXTRA_RECORD_BYTES refers
  347.     // to the number of bytes per record over and above the MINIRECORDCORE
  348.     // structure size that we need per record. Take a look at the PCNRITEM
  349.     // struct in CNRBASE.H to see what kind of data we are storing. The good
  350.     // thing is that the container will allocate this for us during the
  351.     // CM_ALLOCRECORD message. When we do a CM_REMOVERECORD during WM_DESTROY
  352.     // processing, we can free all this container memory in 1 shot. Note that
  353.     // CM_ALLOCRECORD allocates a linked list of records and sets the
  354.     // MINIRECORDCORE.cb size field for each record. It also appears to
  355.     // zero out all allocated memory besides this .cb field. It knows to
  356.     // allocate enough memory for MINIRECORDCORE rather than RECORDCORE structs
  357.     // due to using CCS_MINIRECORDCORE on the WinCreateWindow of the container.
  358.  
  359.     pci = WinSendMsg( hwndCnr, CM_ALLOCRECORD, MPFROMLONG( EXTRA_RECORD_BYTES ),
  360.                       MPFROMLONG( cFiles ) );
  361.  
  362.     if( pci )
  363.     {
  364.         INT           i;
  365.         PFILEFINDBUF3 pffbFile;
  366.         RECORDINSERT  ri;
  367.         PCNRITEM      pciFirst = pci;
  368.         ULONG         cFilesInserted = cFiles;
  369.  
  370.         // Insert all files into the container in one shot by filling in each
  371.         // linked list node that the container allocated for us.
  372.  
  373.         for( i = 0; i < cFiles; i++ )
  374.         {
  375.             // Get next FILEFINDBUF3 structure that points to a found file.
  376.  
  377.             pffbFile = (PFILEFINDBUF3) pbBuf;
  378.  
  379.             // Fill in the container record with the file info.
  380.  
  381.             if( FillInRecord( pci, szDirectory, pffbFile ) )
  382.  
  383.                 // Get the next container record in the linked list that the
  384.                 // container allocated for us.
  385.  
  386.                 pci = (PCNRITEM) pci->rc.preccNextRecord;
  387.             else
  388.                 cFilesInserted--;
  389.  
  390.             // Point to the next file in the buffer. This is done by adding
  391.             // an offset value to the current location in the buffer. Since
  392.             // the file name is variable length, this offset points to the
  393.             // end of the current file name and the beginning of the next
  394.             // one.
  395.  
  396.             pbBuf += pffbFile->oNextEntryOffset;
  397.         }
  398.  
  399.         // Use the RECORDINSERT structure to tell the container how to
  400.         // insert this batch of records. Here we ask to insert the
  401.         // records at the end of the linked list. The parent record indicates
  402.         // who to stick this batch of records under (if pciParent is NULL, the
  403.         // records are at the top level). (Child records are only displayed in
  404.         // Tree view). The zOrder is used for icon view only and specifies the
  405.         // ZORDER that places one record on top of another. In this case we are
  406.         // placing this batch of records at the top of the ZORDER. Also since
  407.         // fInvalidateRecord is TRUE, we will cause the records to be painted
  408.         // as they are inserted. In a container with a small amount of records
  409.         // you probably want to set this to FALSE and do a CM_INVALIDATERECORD
  410.         // (using 0 for cNumRecord) after all records have been inserted. But
  411.         // here the user needs visual feedback if a large amount of
  412.         // subdirectories are found.
  413.  
  414.         (void) memset( &ri, 0, sizeof( RECORDINSERT ) );
  415.  
  416.         ri.cb                 = sizeof( RECORDINSERT );
  417.         ri.pRecordOrder       = (PRECORDCORE) CMA_END;
  418.         ri.pRecordParent      = (PRECORDCORE) pciParent;
  419.         ri.zOrder             = (USHORT) CMA_TOP;
  420.         ri.cRecordsInsert     = cFilesInserted;
  421.         ri.fInvalidateRecord  = TRUE;
  422.  
  423.         if( !WinSendMsg( hwndCnr, CM_INSERTRECORD, MPFROMP( pciFirst ),
  424.                          MPFROMP( &ri ) ) )
  425.         {
  426.             fSuccess = FALSE;
  427.  
  428.             Msg( "InsertRecords CM_INSERTRECORD RC(%X)", HWNDERR( hwndCnr ) );
  429.         }
  430.     }
  431.     else
  432.     {
  433.         fSuccess = FALSE;
  434.  
  435.         Msg( "InsertRecords CM_ALLOCRECORD RC(%X)", HWNDERR( hwndCnr ) );
  436.     }
  437.  
  438.     return fSuccess;
  439. }
  440.  
  441. /**********************************************************************/
  442. /*-------------------------- FillInRecord ----------------------------*/
  443. /*                                                                    */
  444. /*  POPULATE CONTAINER RECORD WITH FILE INFORMATION                   */
  445. /*                                                                    */
  446. /*  INPUT: pointer to record buffer to fill,                          */
  447. /*         directory path of file,                                    */
  448. /*         pointer to FILEFINDBUF3 that describes the file            */
  449. /*                                                                    */
  450. /*  1.                                                                */
  451. /*                                                                    */
  452. /*  OUTPUT: TRUE or FALSE if successful or not                        */
  453. /*                                                                    */
  454. /*--------------------------------------------------------------------*/
  455. /**********************************************************************/
  456. static BOOL FillInRecord( PCNRITEM pci, PSZ szDirectory, PFILEFINDBUF3 pffb )
  457. {
  458.     BOOL     fSuccess = TRUE;
  459.     CHAR     szFullFileName[ CCHMAXPATH + 1 ];
  460.     HPOINTER hptr;
  461.  
  462.     // Copy the file name into the storage allocated by the container.
  463.  
  464.     (void) memset( pci->szFileName, 0, sizeof( pci->szFileName ) );
  465.     (void) memcpy( pci->szFileName, pffb->achName, pffb->cchName );
  466.  
  467.     // Get the fully qualified path for this file
  468.  
  469.     (void) memset( szFullFileName, 0, sizeof( szFullFileName ) );
  470.  
  471.     (void) strcpy( szFullFileName, szDirectory );
  472.  
  473.     szFullFileName[ strlen( szFullFileName ) ] = '\\';
  474.  
  475.     (void) strcat( szFullFileName, pci->szFileName );
  476.  
  477.     // Let PM get the icon for us by using WinLoadFileIcon. Note that a
  478.     // WinFreeFileIcon for this icon is not necessary because we are getting
  479.     // a shared copy by using FALSE as the last parameter. If you do a
  480.     // WinFreeFileIcon on this hptr you will get a PMERR_INVALID_PROCESS_ID.
  481.  
  482.     hptr = WinLoadFileIcon( szFullFileName, FALSE );
  483.  
  484.     // When debug info is enabled, the file that debug info gets written to
  485.     // is open and has 0 bytes. I think this is why WinLoadFileIcon fails for
  486.     // this file. In any case, we don't want an error message to come up for
  487.     // that file since it only happens when we specifically enable debug info.
  488.  
  489.     if( !hptr && stricmp( pci->szFileName, DEBUG_FILENAME ) != 0 )
  490.         Msg( "WinLoadFileIcon failed for %s. No icon will be displayed for "
  491.              "this file.", pci->szFileName );
  492.  
  493.     // Set up the file name pointer to point to the file name. This is
  494.     // crucial because we instructed the container in the
  495.     // SetContainerColumns function (CREATE.C) to use pszFileName as a pointer
  496.     // to the file name (for reasons explained in that function).
  497.  
  498.     pci->pszFileName    = pci->szFileName;
  499.  
  500.     // Fill in all fields of the container record.
  501.  
  502.     pci->hptrIcon       = hptr;
  503.     pci->date.day       = pffb->fdateLastWrite.day;
  504.     pci->date.month     = pffb->fdateLastWrite.month;
  505.     pci->date.year      = pffb->fdateLastWrite.year;
  506.     pci->time.seconds   = pffb->ftimeLastWrite.twosecs;
  507.     pci->time.minutes   = pffb->ftimeLastWrite.minutes;
  508.     pci->time.hours     = pffb->ftimeLastWrite.hours;
  509.     pci->cbFile         = pffb->cbFile;
  510.     pci->attrFile       = pffb->attrFile;
  511.  
  512.     // Fill in all fields of the MINIRECORDCORE structure. Note that the .cb
  513.     // field of the MINIRECORDCORE struct was filled in by CM_ALLOCRECORD.
  514.  
  515.     pci->rc.pszIcon     = pci->pszFileName;
  516.     pci->rc.hptrIcon    = hptr;
  517.  
  518.     return fSuccess;
  519. }
  520.  
  521. /*************************************************************************
  522.  *                     E N D     O F     S O U R C E                     *
  523.  *************************************************************************/
  524.