home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / CNRBAS.ZIP / CNRBASE.C next >
C/C++ Source or Header  |  1993-03-27  |  34KB  |  834 lines

  1. /*********************************************************************
  2.  *                                                                   *
  3.  * MODULE NAME :  cnrbase.c              AUTHOR:  Rick Fishman       *
  4.  * DATE WRITTEN:  10-09-92                                           *
  5.  *                                                                   *
  6.  * HOW TO RUN THIS PROGRAM:                                          *
  7.  *                                                                   *
  8.  *  By just entering CNRBASE on the command line, a container will   *
  9.  *  be created that will contain the files in the current directory. *
  10.  *  Any subdirectories will be included and expandable in Tree view. *
  11.  *  The container starts in Tree view and a menu lets you switch     *
  12.  *  between the other supported views.                               *
  13.  *                                                                   *
  14.  *  Optionally enter 'CNRBASE directory' and that directory will be  *
  15.  *  displayed.                                                       *
  16.  *                                                                   *
  17.  * MODULE DESCRIPTION:                                               *
  18.  *                                                                   *
  19.  *  Root module for CNRBASE.EXE, a program that demonstrates the     *
  20.  *  base functionality of a container control. This module contains  *
  21.  *  the client window procedure and all the supporting functions for *
  22.  *  the container messages.                                          *
  23.  *                                                                   *
  24.  *  This sample creates a simple Directory folder that displays      *
  25.  *  icons for each file in a directory. It shows the Details, Name,  *
  26.  *  Icon, and Tree views and lets the user switch between them. It   *
  27.  *  shows simple direct-editing. The main purpose for this sample is *
  28.  *  to demonstrate the process of creating a functional container,   *
  29.  *  not to show its use once created.                                *
  30.  *                                                                   *
  31.  *  The container is populated with records from a secondary thread. *
  32.  *  This is done not to complicate things but to let the user        *
  33.  *  interact with the container before it is completely filled if we *
  34.  *  are traversing a directory with many subdirectories.             *
  35.  *                                                                   *
  36.  *  Drag/Drop, Deltas, Context Menus, Ownerdraw, MiniIcons,          *
  37.  *  record-sharing, sorting are not demonstrated in this sample      *
  38.  *  program.                                                         *
  39.  *                                                                   *
  40.  * OTHER MODULES:                                                    *
  41.  *                                                                   *
  42.  *  create.c -   contains the code used to create and tailor the     *
  43.  *               container. Once the container is created there are  *
  44.  *               no functions called in this module.                 *
  45.  *                                                                   *
  46.  *  populate.c - contains the code for the thread used to fill the   *
  47.  *               container with records.                             *
  48.  *                                                                   *
  49.  *  common.c -   contains common support routines for CNRBASE.EXE.   *
  50.  *                                                                   *
  51.  * NOTES:                                                            *
  52.  *                                                                   *
  53.  *  This program loses some simplicity by using PM programming       *
  54.  *  techniques that most non-beginner programmers use such as the    *
  55.  *  use of multiple threads, Window Words and error checking. These  *
  56.  *  techniques are not necessary for this sample and this sample     *
  57.  *  would be easier to understand if they were not used. I believe   *
  58.  *  the benefits outweigh the drawbacks because this program will    *
  59.  *  now serve as a more useful template. It will also more easily    *
  60.  *  allow for the creation of multiple instances of the sample       *
  61.  *  Directory window, which will become important in any program     *
  62.  *  that builds on this one.                                         *
  63.  *                                                                   *
  64.  *  I hope this code proves useful for other PM programmers. The     *
  65.  *  more of us the better!                                           *
  66.  *                                                                   *
  67.  * HISTORY:                                                          *
  68.  *                                                                   *
  69.  *  10-09-92 - Program coded                                         *
  70.  *  03-27-93   Changed PSZ szArg to char *szArg  - compiler bug.     *
  71.  *                                                                   *
  72.  *  Rick Fishman                                                     *
  73.  *  Code Blazers, Inc.                                               *
  74.  *  4113 Apricot                                                     *
  75.  *  Irvine, CA. 92720                                                *
  76.  *  CIS ID: 72251,750                                                *
  77.  *                                                                   *
  78.  *********************************************************************/
  79.  
  80. #pragma strings(readonly)   // used for debug version of memory mgmt routines
  81.  
  82. /*********************************************************************/
  83. /*------- Include relevant sections of the OS/2 header files --------*/
  84. /*********************************************************************/
  85.  
  86. #define INCL_DOSERRORS
  87. #define INCL_WINDIALOGS
  88. #define INCL_WINERRORS
  89. #define INCL_WINFRAMEMGR
  90. #define INCL_WINMLE
  91. #define INCL_WINSTDCNR
  92. #define INCL_WINSTDDLGS
  93. #define INCL_WINWINDOWMGR
  94.  
  95. #define GLOBALS_DEFINED        // This module defines the globals in cnrbase.h
  96.  
  97. /**********************************************************************/
  98. /*----------------------------- INCLUDES -----------------------------*/
  99. /**********************************************************************/
  100.  
  101. #include <os2.h>
  102. #include <stdarg.h>
  103. #include <stdio.h>
  104. #include <stdlib.h>
  105. #include <string.h>
  106. #include "cnrbase.h"
  107.  
  108. /*********************************************************************/
  109. /*------------------- APPLICATION DEFINITIONS -----------------------*/
  110. /*********************************************************************/
  111.  
  112. /**********************************************************************/
  113. /*---------------------------- STRUCTURES ----------------------------*/
  114. /**********************************************************************/
  115.  
  116. /**********************************************************************/
  117. /*----------------------- FUNCTION PROTOTYPES ------------------------*/
  118. /**********************************************************************/
  119.  
  120.        INT   main               ( INT iArgc, char *szArg[] );
  121. static BOOL  InitClient         ( HWND hwndClient, PSZ szDirectory );
  122. static VOID  SetContainerView   ( HWND hwndClient, ULONG ulViewType );
  123. static VOID  CnrBeginEdit       ( HWND hwndClient, PCNREDITDATA pced );
  124. static VOID  CnrEndEdit         ( HWND hwndClient, PCNREDITDATA pced );
  125. static ULONG GetMaxNameSize     ( CHAR chDrive );
  126. static VOID  ContainerFilled    ( HWND hwndClient );
  127. static VOID  UserWantsToClose   ( HWND hwndClient );
  128. static VOID  FreeResources      ( HWND hwndClient );
  129.  
  130. FNWP wpClient;
  131.  
  132. /**********************************************************************/
  133. /*------------------------ GLOBAL VARIABLES --------------------------*/
  134. /**********************************************************************/
  135.  
  136. /**********************************************************************/
  137. /*------------------------------ MAIN --------------------------------*/
  138. /*                                                                    */
  139. /*  PROGRAM ENTRYPOINT                                                */
  140. /*                                                                    */
  141. /*  INPUT: command line                                               */
  142. /*                                                                    */
  143. /*  1.                                                                */
  144. /*                                                                    */
  145. /*  OUTPUT: return code                                               */
  146. /*                                                                    */
  147. /*--------------------------------------------------------------------*/
  148. /**********************************************************************/
  149. INT main( INT iArgc, char *szArg[] )
  150. {
  151.     BOOL  fSuccess = FALSE;
  152.     HAB   hab;
  153.     HMQ   hmq = NULLHANDLE;
  154.     QMSG  qmsg;
  155.     PSZ   szStartingDir = NULL;
  156.     HWND  hwndFrame = NULLHANDLE;
  157.  
  158.     // This macro is defined for the debug version of the C Set/2 Memory
  159.     // Management routines. Since the debug version writes to stderr, we
  160.     // send all stderr output to a debuginfo file. Look in MAKEFILE to see how
  161.     // to enable the debug version of those routines.
  162.  
  163. #ifdef __DEBUG_ALLOC__
  164.     freopen( DEBUG_FILENAME, "w", stderr );
  165. #endif
  166.  
  167.     // fTrue will be used for all while( fTrue ) loops. The C Set/2++ compiler
  168.     // took away the ability to use while( TRUE ) and for( ; ; ) constructs.
  169.  
  170.     fTrue = TRUE;
  171.  
  172.     // Get the directory to display from the command line if specified.
  173.  
  174.     if( iArgc > 1 )
  175.         szStartingDir = szArg[ 1 ];
  176.  
  177.     hab = WinInitialize( 0 );
  178.  
  179.     if( hab )
  180.         hmq = WinCreateMsgQueue( hab, 0 );
  181.     else
  182.     {
  183.         DosBeep( 1000, 100 );
  184.  
  185.         (void) fprintf( stderr, "WinInitialize failed!" );
  186.     }
  187.  
  188.     if( hmq )
  189.  
  190.         // CS_SIZEREDRAW needed for initial display of the container in the
  191.         // client window. Allocate enough extra bytes for 1 window word.
  192.  
  193.         fSuccess = WinRegisterClass( hab, DIRECTORY_WINCLASS, wpClient,
  194.                                      CS_SIZEREDRAW, sizeof( PVOID ) );
  195.     else
  196.     {
  197.         DosBeep( 1000, 100 );
  198.  
  199.         (void) fprintf( stderr, "WinCreateMsgQueue RC(%X)", HABERR( hab ) );
  200.     }
  201.  
  202.     if( fSuccess )
  203.  
  204.         // CreateDirectoryWin is in CREATE.C
  205.  
  206.         hwndFrame = CreateDirectoryWin( szStartingDir );
  207.     else
  208.         Msg( "WinRegisterClass RC(%X)", HABERR( hab ) );
  209.  
  210.     if( hwndFrame )
  211.         while( WinGetMsg( hab, &qmsg, NULLHANDLE, 0, 0 ) )
  212.             (void) WinDispatchMsg( hab, &qmsg );
  213.  
  214.     if( hmq )
  215.         (void) WinDestroyMsgQueue( hmq );
  216.  
  217.     if( hab )
  218.         (void) WinTerminate( hab );
  219.  
  220. #ifdef __DEBUG_ALLOC__
  221.     _dump_allocated( -1 );
  222. #endif
  223.  
  224.     return 0;
  225. }
  226.  
  227. /**********************************************************************/
  228. /*---------------------------- wpClient ------------------------------*/
  229. /*                                                                    */
  230. /*  CLIENT WINDOW PROCEDURE FOR THE DIRECTORY WINDOW.                 */
  231. /*                                                                    */
  232. /*  NOTE: This window is created by CreateDirectoryWin in CREATE.C    */
  233. /*                                                                    */
  234. /*  INPUT: standard window procedure parameters                       */
  235. /*                                                                    */
  236. /*  1.                                                                */
  237. /*                                                                    */
  238. /*  OUTPUT: result of message processing                              */
  239. /*                                                                    */
  240. /*--------------------------------------------------------------------*/
  241. /**********************************************************************/
  242. MRESULT EXPENTRY wpClient( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
  243. {
  244.     switch( msg )
  245.     {
  246.         case WM_CREATE:
  247.  
  248.             // If window initialization fails, don't create window
  249.  
  250.             if( InitClient( hwnd, (PSZ) mp1 ) )
  251.             {
  252.                 iWinCount++;
  253.  
  254.                 break;
  255.             }
  256.             else
  257.                 return (MRESULT) TRUE;
  258.  
  259.  
  260.         case UM_CONTAINER_FILLED:
  261.  
  262.             // This message is posted to us by the thread that fills the
  263.             // container with records. This indicates that the container is
  264.             // now filled.
  265.  
  266.             ContainerFilled( hwnd );
  267.  
  268.             return 0;
  269.  
  270.  
  271.         case WM_ERASEBACKGROUND:
  272.  
  273.             // Paint the client window in the default color
  274.  
  275.             return (MRESULT) TRUE;
  276.  
  277.  
  278.         case WM_SIZE:
  279.  
  280.             // Size the container with the client window
  281.  
  282.             if( !WinSetWindowPos( WinWindowFromID( hwnd, CNR_DIRECTORY ),
  283.                                   NULLHANDLE, 0, 0, SHORT1FROMMP( mp2 ),
  284.                                   SHORT2FROMMP( mp2 ), SWP_MOVE | SWP_SIZE ) )
  285.                 Msg( "WM_SIZE WinSetWindowPos RC(%X)", HWNDERR( hwnd ) );
  286.  
  287.             return 0;
  288.  
  289.  
  290.         case WM_COMMAND:    // Menu messages
  291.  
  292.             switch( SHORT1FROMMP( mp1 ) )
  293.             {
  294.                 case IDM_TREE:
  295.  
  296.                     SetContainerView( hwnd, CV_TREE | CV_ICON );
  297.  
  298.                     return 0;
  299.  
  300.                 case IDM_NAME:
  301.  
  302.                     SetContainerView( hwnd, CV_NAME | CV_FLOW );
  303.  
  304.                     return 0;
  305.  
  306.                 case IDM_TEXT:
  307.  
  308.                     SetContainerView( hwnd, CV_TEXT | CV_FLOW );
  309.  
  310.                     return 0;
  311.  
  312.                 case IDM_ICON:
  313.  
  314.                     SetContainerView( hwnd, CV_ICON );
  315.  
  316.                     return 0;
  317.  
  318.                 case IDM_DETAILS:
  319.  
  320.                     SetContainerView( hwnd, CV_DETAIL );
  321.  
  322.                     return 0;
  323.             }
  324.  
  325.             break;
  326.  
  327.  
  328.         case WM_CONTROL:        // These control messages sent by the container
  329.  
  330.             if( SHORT1FROMMP( mp1 ) == CNR_DIRECTORY )
  331.                 switch( SHORT2FROMMP( mp1 ) )
  332.                 {
  333.                     case CN_BEGINEDIT:
  334.  
  335.                         CnrBeginEdit( hwnd, (PCNREDITDATA) mp2 );
  336.  
  337.                         break;
  338.  
  339.  
  340.                     case CN_ENDEDIT:
  341.  
  342.                         CnrEndEdit( hwnd, (PCNREDITDATA) mp2 );
  343.  
  344.                         break;
  345.                 }
  346.  
  347.             break;
  348.  
  349.  
  350.         case WM_CLOSE:
  351.  
  352.             // Don't let the WM_QUIT message get posted. *We* will decide
  353.             // when to shut down the message queue.
  354.  
  355.             UserWantsToClose( hwnd );
  356.  
  357.             return 0;
  358.  
  359.  
  360.         case WM_DESTROY:
  361.  
  362.             FreeResources( hwnd );
  363.  
  364.             // If this is the last window open, shut down the message queue
  365.             // which will kill the .EXE. In this program there is only one
  366.             // window so the first WM_DESTROY will always do this.
  367.  
  368.             if( --iWinCount == 0 )
  369.                 (void) WinPostMsg( NULLHANDLE, WM_QUIT, NULL, NULL );
  370.  
  371.             break;
  372.     }
  373.  
  374.     return WinDefWindowProc( hwnd, msg, mp1, mp2 );
  375. }
  376.  
  377. /**********************************************************************/
  378. /*--------------------------- InitClient -----------------------------*/
  379. /*                                                                    */
  380. /*  PROCESS WM_CREATE FOR THE CLIENT WINDOW.                          */
  381. /*                                                                    */
  382. /*  INPUT: client window handle,                                      */
  383. /*         pointer to directory to display in container               */
  384. /*                                                                    */
  385. /*  1.                                                                */
  386. /*                                                                    */
  387. /*  OUTPUT: TRUE or FALSE if successful or not                        */
  388. /*                                                                    */
  389. /*--------------------------------------------------------------------*/
  390. /**********************************************************************/
  391. static BOOL InitClient( HWND hwndClient, PSZ szDirectory )
  392. {
  393.     BOOL      fSuccess = TRUE;
  394.     PINSTANCE pi = (PINSTANCE) malloc( sizeof( INSTANCE ) );
  395.  
  396.     if( pi )
  397.     {
  398.         (void) memset( pi, 0, sizeof( INSTANCE ) );
  399.  
  400.         if( WinSetWindowPtr( hwndClient, 0, pi ) )
  401.         {
  402.             // CreateContainer is located in CREATE.C
  403.  
  404.             HWND hwndCnr = CreateContainer( hwndClient, szDirectory );
  405.  
  406.             if( hwndCnr )
  407.  
  408.                 // Set the initial container view to Tree/Icon view
  409.  
  410.                 SetContainerView( hwndClient, CV_TREE | CV_ICON );
  411.             else
  412.                 fSuccess = FALSE;
  413.         }
  414.         else
  415.         {
  416.             fSuccess = FALSE;
  417.  
  418.             Msg( "InitClient WinSetWindowPtr RC(%X)", HWNDERR( hwndClient ) );
  419.         }
  420.     }
  421.     else
  422.     {
  423.         fSuccess = FALSE;
  424.  
  425.         Msg( "InitClient out of memory!" );
  426.     }
  427.  
  428.     return fSuccess;
  429. }
  430.  
  431. /**********************************************************************/
  432. /*------------------------- SetContainerView -------------------------*/
  433. /*                                                                    */
  434. /*  SET THE TYPE OF VIEW FOR THE CONTAINER                            */
  435. /*                                                                    */
  436. /*  INPUT: client window handle,                                      */
  437. /*         view type to set to                                        */
  438. /*                                                                    */
  439. /*  1.                                                                */
  440. /*                                                                    */
  441. /*  OUTPUT: nothing                                                   */
  442. /*                                                                    */
  443. /*--------------------------------------------------------------------*/
  444. /**********************************************************************/
  445. static VOID SetContainerView( HWND hwndClient, ULONG ulViewType )
  446. {
  447.     PINSTANCE pi = INSTDATA( hwndClient );
  448.     CNRINFO   cnri;
  449.  
  450.     if( !pi )
  451.     {
  452.         Msg( "Set..View cant get Inst data. RC(%X)", HWNDERR( hwndClient ) );
  453.  
  454.         return;
  455.     }
  456.  
  457.     cnri.cb = sizeof( CNRINFO );
  458.  
  459.     // Set the container window attributes: Set the container view mode. Use a
  460.     // container title. Put a separator between the title and the records
  461.     // beneath it. Make the container title read-only.
  462.  
  463.     cnri.flWindowAttr = ulViewType | CA_CONTAINERTITLE | CA_TITLESEPARATOR |
  464.                         CA_TITLEREADONLY;
  465.  
  466.     switch( ulViewType )
  467.     {
  468.         case CV_TREE:
  469.         case (CV_TREE | CV_ICON):
  470.  
  471.             (void) strcpy( pi->szCnrTitle, "Tree/icon view - " );
  472.  
  473.             // Use default spacing between levels in the tree view. Also use
  474.             // the default width of a line that shows record relationships.
  475.  
  476.             cnri.cxTreeIndent = -1;
  477.             cnri.cxTreeLine   = -1;
  478.  
  479.             cnri.flWindowAttr |=  CA_TREELINE;
  480.  
  481.             break;
  482.  
  483.         case CV_ICON:
  484.  
  485.             (void) strcpy( pi->szCnrTitle, "Icon view - " );
  486.  
  487.             break;
  488.  
  489.         case CV_NAME:
  490.         case (CV_NAME | CV_FLOW):
  491.  
  492.             (void) strcpy( pi->szCnrTitle, "Name/flowed view - " );
  493.  
  494.             break;
  495.  
  496.         case CV_DETAIL:
  497.  
  498.             (void) strcpy( pi->szCnrTitle, "Detail view - " );
  499.  
  500.             // If we are in DETAIL view, tell the container that we will be
  501.             // using column headings.
  502.  
  503.             cnri.flWindowAttr |= CA_DETAILSVIEWTITLES;
  504.  
  505.             break;
  506.  
  507.         case CV_TEXT:
  508.         case (CV_TEXT | CV_FLOW):
  509.  
  510.             (void) strcpy( pi->szCnrTitle, "Text/flowed view - " );
  511.  
  512.             break;
  513.     }
  514.  
  515.     (void) strcat( pi->szCnrTitle, pi->szDirectory );
  516.  
  517.     cnri.pszCnrTitle = pi->szCnrTitle;
  518.  
  519.     // Set the line spacing between rows to be the minimal value so we conserve
  520.     // on container whitespace.
  521.  
  522.     cnri.cyLineSpacing = 0;
  523.  
  524.     // Set the container parameters. Note that mp2 specifies which fields of
  525.     // of the CNRINFO structure to use. The CMA_FLWINDOWATTR says to use
  526.     // flWindowAttr to specify which 'Window Attribute' fields to use.
  527.  
  528.     if( !WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY, CM_SETCNRINFO,
  529.                             MPFROMP( &cnri ),
  530.                             MPFROMLONG( CMA_FLWINDOWATTR | CMA_CNRTITLE |
  531.                                         CMA_LINESPACING ) ) )
  532.         Msg( "SetContainerView CM_SETCNRINFO RC(%X)", HWNDERR( hwndClient ) );
  533.  
  534.     // The CM_ARRANGE message is applicable only in ICON view. It will arrange
  535.     // the icons according to CUA. Note that this message is unnecessary if
  536.     // the CCS_AUTOPOSITION style is used on the WinCreateWindow call for the
  537.     // container. The problem with using that style is that you have no control
  538.     // over *when* the arranging is done.
  539.  
  540.     if( ulViewType == CV_ICON )
  541.         if( !WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY, CM_ARRANGE, NULL,
  542.                                 NULL ) )
  543.             Msg( "SetContainerView CM_ARRANGE RC(%X)", HWNDERR( hwndClient ) );
  544.  
  545.     return;
  546. }
  547.  
  548. /**********************************************************************/
  549. /*--------------------------- CnrBeginEdit ---------------------------*/
  550. /*                                                                    */
  551. /*  PROCESS CN_BEGINEDIT NOTIFY MESSAGE.                              */
  552. /*                                                                    */
  553. /*  INPUT: client window handle,                                      */
  554. /*         pointer to the CNREDITDATA structure                       */
  555. /*                                                                    */
  556. /*  1.                                                                */
  557. /*                                                                    */
  558. /*  OUTPUT: nothing                                                   */
  559. /*                                                                    */
  560. /*--------------------------------------------------------------------*/
  561. /**********************************************************************/
  562. static VOID CnrBeginEdit( HWND hwndClient, PCNREDITDATA pced )
  563. {
  564.     PFIELDINFO  pfi = pced->pFieldInfo;
  565.     PINSTANCE   pi = INSTDATA( hwndClient );
  566.  
  567.     if( !pi )
  568.     {
  569.         Msg( "CnrBeginEdit cant get Inst data. RC(%X)", HWNDERR( hwndClient ) );
  570.  
  571.         return;
  572.     }
  573.  
  574.     // pfi only available if details view. If we are in details view and
  575.     // the column the user is direct-editing is the file name field, set the
  576.     // text limit of the MLE to the Maximum that the filename can be.
  577.     // If MLM_SETTEXTLIMIT returns a non-zero value, it means that the text
  578.     // length in the MLE is greater than what we are trying to set it to.
  579.  
  580.     if( !pfi || pfi->offStruct == FIELDOFFSET( CNRITEM, rc.pszIcon ) )
  581.         if( WinSendDlgItemMsg( WinWindowFromID( hwndClient, CNR_DIRECTORY ),
  582.                         CID_MLE, MLM_SETTEXTLIMIT,
  583.                         MPFROMLONG( GetMaxNameSize( pi->szDirectory[ 0 ] ) ),
  584.                         NULL) )
  585.             Msg( "MLM_SETTEXTLIMIT failed. RC(%X)", HWNDERR( hwndClient ) );
  586.  
  587.     return;
  588. }
  589.  
  590. /**********************************************************************/
  591. /*-------------------------- GetMaxNameSize --------------------------*/
  592. /*                                                                    */
  593. /*  GET THE MAXIMUM SIZE OF A FILE NAME FOR A DRIVE.                  */
  594. /*                                                                    */
  595. /*  INPUT: drive letter                                               */
  596. /*                                                                    */
  597. /*  1.                                                                */
  598. /*                                                                    */
  599. /*  OUTPUT: max filename size                                         */
  600. /*                                                                    */
  601. /*--------------------------------------------------------------------*/
  602. /**********************************************************************/
  603.  
  604. #define QFSBUFFSIZE 100
  605.  
  606. static ULONG GetMaxNameSize( CHAR chDrive )
  607. {
  608.     APIRET      rc;
  609.     CHAR        szDrive[ 3 ], achBuf[ QFSBUFFSIZE ];
  610.     PFSQBUFFER2 pfsqb = (PFSQBUFFER2) achBuf;
  611.     ULONG       cbFileName = 0, cbBuf = sizeof( achBuf );
  612.     PSZ         szFSDName;
  613.  
  614.     szDrive[ 0 ] = chDrive;
  615.     szDrive[ 1 ] = ':';
  616.     szDrive[ 2 ] = 0;
  617.  
  618.     // Get the file system type for this drive (i.e. HPFS, FAT, etc.)
  619.  
  620.     rc = DosQueryFSAttach( szDrive, 0, FSAIL_QUERYNAME, (PFSQBUFFER2) achBuf,
  621.                            &cbBuf );
  622.  
  623.     // Should probably handle ERROR_BUFFER_OVERFLOW more gracefully, but not
  624.     // in this sample program <g>
  625.  
  626.     if( rc )
  627.         cbFileName = 12;                     // If any errors, assume FAT
  628.     else
  629.     {
  630.         szFSDName = pfsqb->szName + pfsqb->cbName + 1;
  631.  
  632.         if( !stricmp( "FAT", szFSDName ) )
  633.             cbFileName = 12;
  634.         else
  635.             cbFileName = CCHMAXPATH;         // If not FAT, assume maximum path
  636.     }
  637.  
  638.     return cbFileName;
  639. }
  640.  
  641. /**********************************************************************/
  642. /*---------------------------- CnrEndEdit ----------------------------*/
  643. /*                                                                    */
  644. /*  PROCESS CN_ENDEDIT NOTIFY MESSAGE.                                */
  645. /*                                                                    */
  646. /*  INPUT: client window handle,                                      */
  647. /*         pointer to the CNREDITDATA structure                       */
  648. /*                                                                    */
  649. /*  1.                                                                */
  650. /*                                                                    */
  651. /*  OUTPUT: nothing                                                   */
  652. /*                                                                    */
  653. /*--------------------------------------------------------------------*/
  654. /**********************************************************************/
  655. static VOID CnrEndEdit( HWND hwndClient, PCNREDITDATA pced )
  656. {
  657.     PINSTANCE   pi = INSTDATA( hwndClient );
  658.     PCNRITEM    pci = (PCNRITEM) pced->pRecord;
  659.     PFIELDINFO  pfi = pced->pFieldInfo;
  660.     HWND        hwndCnr, hwndMLE;
  661.     CHAR        szData[ CCHMAXPATH + 1 ];
  662.  
  663.     if( !pi )
  664.     {
  665.         Msg( "CnrEndEdit cant get Inst data. RC(%X)", HWNDERR( hwndClient ) );
  666.  
  667.         return;
  668.     }
  669.  
  670.     hwndCnr = WinWindowFromID( hwndClient, CNR_DIRECTORY );
  671.  
  672.     // Get the handle to the MLE that the container uses for direct editing
  673.  
  674.     hwndMLE = WinWindowFromID( hwndCnr, CID_MLE );
  675.  
  676.     // pfi only available if details view
  677.  
  678.     if( !pfi || pfi->offStruct == FIELDOFFSET( CNRITEM, rc.pszIcon ) )
  679.     {
  680.         WinQueryWindowText( hwndMLE, sizeof( szData ), szData );
  681.  
  682.         // Just a cute little test to make sure this is all working...
  683.  
  684.         if( !stricmp( szData, "BADREC" ) )
  685.             (void) WinAlarm( HWND_DESKTOP, WA_WARNING );
  686.     }
  687.  
  688.     // Invalidate the container record that was being direct-edited because
  689.     // the text has probably changed. Note that this should only be done if the
  690.     // text changed. Since this is just a sample program we do it regardless...
  691.  
  692.     if( !WinSendMsg( hwndCnr, CM_INVALIDATERECORD, MPFROMP( &pci ),
  693.                      MPFROM2SHORT( 1, CMA_TEXTCHANGED ) ) )
  694.         Msg( "CnrEndEdit CM_INVALIDATERECORD RC(%X)", HWNDERR( hwndCnr ) );
  695.  
  696.     return;
  697. }
  698.  
  699. /**********************************************************************/
  700. /*------------------------- ContainerFilled --------------------------*/
  701. /*                                                                    */
  702. /*  THE FILL THREAD HAS COMPLETED.                                    */
  703. /*                                                                    */
  704. /*  INPUT: client window handle                                       */
  705. /*                                                                    */
  706. /*  1.                                                                */
  707. /*                                                                    */
  708. /*  OUTPUT: nothing                                                   */
  709. /*                                                                    */
  710. /*--------------------------------------------------------------------*/
  711. /**********************************************************************/
  712. static VOID ContainerFilled( HWND hwndClient )
  713. {
  714.     PINSTANCE pi = INSTDATA( hwndClient );
  715.  
  716.     if( !pi )
  717.     {
  718.         Msg( "ContainerFilled cant get Inst data. RC(%X)", HWNDERR(hwndClient));
  719.  
  720.         return;
  721.     }
  722.  
  723.     // If the user closed the window while the Fill thread was executing, we
  724.     // want to shut down this window now.
  725.  
  726.     if( pi->fShutdown )
  727.         WinDestroyWindow( PARENT( hwndClient ) );
  728.     else
  729.     {
  730.         // Set a flag so the window will know the Fill thread has finished
  731.  
  732.         pi->fContainerFilled = TRUE;
  733.  
  734.         // Set the titlebar to the program title. We do this because while
  735.         // the container was being filled, the titlebar text was changed
  736.         // to indicate progress.
  737.  
  738.         SetWindowTitle( hwndClient, PROGRAM_TITLE );
  739.     }
  740.  
  741.     return;
  742. }
  743.  
  744. /**********************************************************************/
  745. /*------------------------- UserWantsToClose -------------------------*/
  746. /*                                                                    */
  747. /*  PROCESS THE WM_CLOSE MESSAGE.                                     */
  748. /*                                                                    */
  749. /*  INPUT: client window handle                                       */
  750. /*                                                                    */
  751. /*  1.                                                                */
  752. /*                                                                    */
  753. /*  OUTPUT: nothing                                                   */
  754. /*                                                                    */
  755. /*--------------------------------------------------------------------*/
  756. /**********************************************************************/
  757. static VOID UserWantsToClose( HWND hwndClient )
  758. {
  759.     PINSTANCE pi = INSTDATA( hwndClient );
  760.  
  761.     if( !pi )
  762.     {
  763.         Msg( "UserWantsToClose cant get Inst data. RC(%X)",HWNDERR(hwndClient));
  764.  
  765.         return;
  766.     }
  767.  
  768.     // If the Fill thread has completed, destroy the frame window.
  769.     // If the Fill thread has not completed, set a flag that will cause it to
  770.     // terminate, then the destroy will occur under the UM_CONTAINER_FILLED
  771.     // message.
  772.  
  773.     if( pi->fContainerFilled )
  774.         WinDestroyWindow( PARENT( hwndClient ) );
  775.     else
  776.     {
  777.         // Indicate in the titlebar that the window is in the process of
  778.         // closing.
  779.  
  780.         SetWindowTitle( hwndClient, "%s: CLOSING...", PROGRAM_TITLE );
  781.  
  782.         // Set a flag that will tell the Fill thread to shut down
  783.  
  784.         pi->fShutdown = TRUE;
  785.     }
  786.  
  787.     return;
  788. }
  789.  
  790. /**********************************************************************/
  791. /*-------------------------- FreeResources ---------------------------*/
  792. /*                                                                    */
  793. /*  FREE PROGRAM RESOURCES.                                           */
  794. /*                                                                    */
  795. /*  INPUT: client window handle                                       */
  796. /*                                                                    */
  797. /*  1.                                                                */
  798. /*                                                                    */
  799. /*  OUTPUT: nothing                                                   */
  800. /*                                                                    */
  801. /*--------------------------------------------------------------------*/
  802. /**********************************************************************/
  803. static VOID FreeResources( HWND hwndClient )
  804. {
  805.     PINSTANCE pi = INSTDATA( hwndClient );
  806.  
  807.     if( pi )
  808.         free( pi );
  809.     else
  810.         Msg( "FreeResources cant get Inst data. RC(%X)", HWNDERR( hwndClient ));
  811.  
  812.     // Free the memory that was allocated with CM_ALLOCDETAILFIELDINFO. The
  813.     // zero in the first SHORT of mp2 says to free memory for all columns
  814.  
  815.     if( -1 == (INT) WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY,
  816.                                        CM_REMOVEDETAILFIELDINFO, NULL,
  817.                                        MPFROM2SHORT( 0, CMA_FREE ) ) )
  818.         Msg( "CM_REMOVEDETAILFIELDINFO failed! RC(%X)", HWNDERR( hwndClient ) );
  819.  
  820.     // Free the memory allocated by the CM_INSERTRECORD messages. The zero
  821.     // in the first SHORT of mp2 says to free memory for all records
  822.  
  823.     if( -1 == (INT) WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY,
  824.                                        CM_REMOVERECORD, NULL,
  825.                                        MPFROM2SHORT( 0, CMA_FREE ) ) )
  826.         Msg( "CM_REMOVERECORD failed! RC(%X)", HWNDERR( hwndClient ) );
  827.  
  828.     return;
  829. }
  830.  
  831. /*************************************************************************
  832.  *                     E N D     O F     S O U R C E                     *
  833.  *************************************************************************/
  834.