home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / io / filer / expdir.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  18KB  |  573 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /***************************************************************************\
  13. *
  14. *    PROGRAM: EXPDIR.C
  15. *
  16. *    PURPOSE: Directory Listbox expansion/collapse functions
  17. *
  18. \***************************************************************************/
  19. #define  STRICT
  20. #include <windows.h>
  21. #include <limits.h>
  22. #include "globals.h"
  23. #include "filer.h"
  24. #include "drvproc.h"
  25. #include "expdir.h"
  26.  
  27. extern HANDLE ghModule;
  28.  
  29. /***************************************************************************\
  30. *
  31. * ExpDir()
  32. *
  33. * Thread Function
  34. * Called when directory is selected (Enter or Double-click).  Expands subdirs
  35. *  of selected directory if not expanded, indenting subdirs with a '|'.  If
  36. *  already expaneded, deleteds all subdirs from listbox
  37. *
  38. * Returns:  TRUE if successful, FALSE if error.
  39. *
  40. * History:
  41. * 5/11/93
  42. *   Created.
  43. *
  44. \***************************************************************************/
  45. BOOL ExpDir(LPCINFO lpCInfo)
  46. {
  47.     HANDLE          hFile = NULL;   //  Find file handle
  48.     WIN32_FIND_DATA FileData;       //  Find file info structure
  49.  
  50.     TCHAR           szFileName[DIRECTORY_STRING_SIZE << 1];   // file name buffer
  51.     PTCHAR          lpszHold;      //  Points to actual name in LB string
  52.  
  53.     BOOL            fDone = FALSE;  //  Loop flag for finding files in dir
  54.  
  55.     DWORD           dwAttrib;       //  Holds file attribute flags
  56.  
  57.     LONG            lIndex,         //  Index of selected listbox member
  58.                     lDirDepth;
  59.  
  60.  
  61.     if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
  62.             == WAIT_TIMEOUT ){
  63.         ErrorMsg(TEXT("ExpDir: Dir LB Mutex Timeout."));
  64.         return(0);
  65.     }
  66.  
  67.     //
  68.     // If the LB is empty, we insert the root, unexpanded
  69.     //
  70.     if( !SendMessage( lpCInfo->hDirLB, LB_GETCOUNT, 0, 0) ){
  71.  
  72.         TCHAR   szHold[DIRECTORY_STRING_SIZE << 1];
  73.  
  74.         szHold[0] = TEXT('+');
  75.         lstrcpy( &(szHold[1]), lpCInfo->lpDriveInfo->DriveName );
  76.  
  77.         //
  78.         // Also copy the DriveName to the Caption Bar.
  79.         //
  80.         lstrcpy(lpCInfo->CaptionBarText, lpCInfo->lpDriveInfo->DriveName );
  81.  
  82.         SendMessage(lpCInfo->hDirLB, LB_ADDSTRING, 0,
  83.                     (LPARAM)szHold );
  84.  
  85.         ReleaseMutex( lpCInfo->hDirMutex );
  86.         return(1);
  87.     }
  88.  
  89.     //
  90.     // Retrieve index of selected (careted) directory.
  91.     //
  92.     lIndex = SendMessage( lpCInfo->hDirLB, LB_GETCARETINDEX,
  93.                           (WPARAM)NULL, (LPARAM)NULL );
  94.  
  95.     if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex,
  96.                         (LPARAM)szFileName ) < 0 ){
  97.         ErrorMsg(TEXT("Expand Directory:  Get listbox text failure"));
  98.         ExpDirExit(lpCInfo, hFile);
  99.         return(0);
  100.     }
  101.  
  102.     lDirDepth = GetDirDepth( szFileName, &lpszHold );
  103.  
  104.     if( lDirDepth == -1 ){
  105.         ErrorMsg(TEXT(" Expand Directory:  GetDirDepth failure"));
  106.         ExpDirExit(lpCInfo, hFile);
  107.         return(0);
  108.     }
  109.  
  110.     //
  111.     // If Directory is already expanded, collapse it, and vice versa.
  112.     //  First, change the symbol.
  113.     //
  114.     if( *lpszHold == TEXT('-'))
  115.         *lpszHold = TEXT('+');
  116.     else
  117.         *lpszHold = TEXT('-');
  118.  
  119.     //
  120.     // Clear WM_SETREDRAW flag, so changes will not be seen until entire
  121.     //  expansion/collapse is complete.  Reset flag at end of function
  122.     //
  123.     if( SendMessage(lpCInfo->hDirLB, WM_SETREDRAW, (WPARAM)FALSE,
  124.                 (LPARAM)0 ) < 0){
  125.         ErrorMsg(TEXT("Expand Directory:  Clear redraw flag failure"));
  126.         ExpDirExit(lpCInfo, hFile);
  127.         return(0);
  128.     }
  129.  
  130.     //
  131.     // Delete old dir string, insert new, and reset the selection
  132.     //
  133.     if( SendMessage(lpCInfo->hDirLB, LB_DELETESTRING, (WPARAM)lIndex,
  134.                 (LPARAM)0 ) < 0){
  135.         ErrorMsg(TEXT("Expand Directory:  Delete dir string failure"));
  136.         ExpDirExit(lpCInfo, hFile);
  137.         return(0);
  138.     }
  139.     if( SendMessage(lpCInfo->hDirLB, LB_INSERTSTRING, (WPARAM)lIndex,
  140.                (LPARAM)szFileName ) < 0){
  141.         ErrorMsg(TEXT("Expand Directory:  Insert dir string failure"));
  142.         ExpDirExit(lpCInfo, hFile);
  143.         return(0);
  144.     }
  145.     if( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL, (WPARAM)lIndex,
  146.                (LPARAM)0 ) < 0){
  147.         ErrorMsg(TEXT("Expand Directory:  Insert dir string failure"));
  148.         ExpDirExit(lpCInfo, hFile);
  149.         return(0);
  150.     }
  151.  
  152.     //
  153.     // The symbol has been changed, now collapse if needed, then reset
  154.     //  redraw flag, leave critical section, and exit.
  155.     //
  156.     if( *lpszHold == TEXT('+') ){
  157.         CollapseDir( lpCInfo, lIndex, lDirDepth);
  158.         ExpDirExit(lpCInfo, hFile);
  159.         return(1);
  160.     }
  161.  
  162.     //
  163.     // If we're here, directory needs to be expanded.
  164.     //  Enumerate subdirectories beneath the dir entry in the listbox.
  165.     //
  166.     // First, Get the full path of the directory (in szFileName)
  167.     //
  168.     if( !ConstructDirName(lpCInfo, lIndex, szFileName) ){
  169.         ErrorMsg(TEXT("Expand Directory:  ConstructDirName failure"));
  170.         ExpDirExit(lpCInfo, hFile);
  171.         return(0);
  172.     }
  173.  
  174.     //
  175.     // Check to see if there is a terminating backslash
  176.     //  Then append a '*' as a wildcard for FindFirstFile.
  177.     //
  178.     lpszHold = TStrChr(szFileName, TEXT('\0'));
  179.  
  180.     lpszHold--;
  181.     if( *lpszHold != TEXT('\\') ){
  182.         lpszHold++;
  183.         *lpszHold = TEXT('\\');
  184.     }
  185.  
  186.     lpszHold++;
  187.     lstrcpy( lpszHold, TEXT("*"));
  188.  
  189.     //
  190.     // Start a search on all the files within the directory
  191.     //
  192.     hFile = FindFirstFile( szFileName, &FileData );
  193.     if( hFile == INVALID_HANDLE_VALUE ){
  194.         ErrorMsg(TEXT("Expand Directory: FindFirstFile failure."));
  195.         ExpDirExit(lpCInfo, hFile);
  196.         return(0);
  197.     }
  198.  
  199.     //
  200.     // Walk all the files in the directory.
  201.     //
  202.     while( !fDone ){
  203.  
  204.         //
  205.         // Check to see if the thread has been requested to kill itself.
  206.         //  This code does not clear the suicide flag for synchronization
  207.         //  reasons - it is left to the calling code.
  208.         //
  209.         if( lpCInfo->fSuicide ){
  210.             ErrorMsg(TEXT("Expand Directory: killing thread per request."));
  211.             ExpDirExit(lpCInfo, hFile);
  212.  
  213.             //
  214.             // Post an MM_REFRESH Message if the user has re-collapsed the
  215.             //  Dir LB.
  216.             //
  217.             if( !lpCInfo->fDirExpand )
  218.                 if( !PostMessage(lpCInfo->hwnd, WM_COMMAND, MM_REFRESH,
  219.                                  (LPARAM)0) )
  220.                     ErrorMsg(TEXT("ExpDir: MM_REFRESH call failure."));
  221.  
  222.             //
  223.             // We must return failure (0) here, so if FullExpand is calling,
  224.             //  it will terminate.
  225.             //
  226.             return(0);
  227.         }
  228.  
  229.         //
  230.         // Append filename to path, and get attributes
  231.         //
  232.         lstrcpy(lpszHold, FileData.cFileName);
  233.         dwAttrib = GetFileAttributes( szFileName );
  234.  
  235.         //
  236.         // Check if file is a directory.  If not, or if '.' or '..', fall
  237.         //  through.  If so, add it to the directory listbox.
  238.         //
  239.         if( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )
  240.             if( lstrcmp( FileData.cFileName,TEXT(".") ) )
  241.                 if( lstrcmp( FileData.cFileName,TEXT("..") ) ){
  242.                     TCHAR   szLBEntry[DIRECTORY_STRING_SIZE << 1];
  243.  
  244.                     ConstructLBEntry(lDirDepth, FileData.cFileName, szLBEntry);
  245.                     //
  246.                     //  Increment index in order to add subdir after
  247.                     //  dirs just inserted.
  248.                     //
  249.                     if( SendMessage(lpCInfo->hDirLB, LB_INSERTSTRING,
  250.                                        (WPARAM)++lIndex,
  251.                                        (LPARAM)szLBEntry) < 0){
  252.                         ErrorMsg(TEXT("Expand Directory: error inserting string."));
  253.                         ExpDirExit(lpCInfo, hFile);
  254.                         return(0);
  255.                     }
  256.                 }
  257.  
  258.         fDone = !FindNextFile( hFile, &FileData );
  259.     }
  260.  
  261.     ExpDirExit(lpCInfo, hFile);
  262.     return(1);
  263. }
  264.  
  265.  
  266. /***************************************************************************\
  267. *
  268. * ExpDirExit()
  269. *
  270. * Performs clean-up operations for ExpDir, closing handles, etc.
  271. *
  272. * History:
  273. * 5/28/93
  274. *   Created.
  275. *
  276. \***************************************************************************/
  277. void ExpDirExit(LPCINFO lpCInfo, HANDLE hFile)
  278. {
  279.  
  280.     //
  281.     // Reset redraw flag. Post this message, to avoid synchro problems
  282.     //
  283.     if( PostMessage(lpCInfo->hDirLB, WM_SETREDRAW, (WPARAM)TRUE,
  284.                 (LPARAM)0 ) < 0)
  285.         ErrorMsg(TEXT("Expand Directory:  Clear redraw flag failure"));
  286.  
  287.     //
  288.     // Close FindFirstFile session
  289.     //
  290.     if( hFile != NULL )
  291.         FindClose(hFile);
  292.  
  293.     //
  294.     // Release Dir LB Mutex
  295.     //
  296.     ReleaseMutex( lpCInfo->hDirMutex);
  297. }
  298.  
  299.  
  300. /***************************************************************************\
  301. *
  302. * ConstructDirName()
  303. *
  304. * Builds the fully qualified path of the current directory, by walking back
  305. *  through the Dir LB tree.
  306. *
  307. * Returns:  TRUE if successful, FALSE if error.
  308. *           Returns the full directory path of given directory name in
  309. *             lpszDirName.
  310. *
  311. * History:
  312. * 5/11/93
  313. *   Created.
  314. *
  315. \***************************************************************************/
  316. BOOL ConstructDirName(LPCINFO lpCInfo, LONG lIndex, LPTSTR lpszDirName)
  317. {
  318.     LONG    lDirDepth,      //  Depth of selected directory
  319.             lSeekDepth = LONG_MAX;
  320.  
  321.     TCHAR   szFileName[DIRECTORY_STRING_SIZE << 1];   // file buffer
  322.     LPTSTR  lpszInfoPtr,
  323.             lpszHold;
  324.  
  325.     //
  326.     // Clear the directory name buffer
  327.     //
  328.     *lpszDirName = TEXT('\0');
  329.  
  330.     //
  331.     // Walk up the entries in the listbox, constructing the full path from
  332.     //   the bottom up.
  333.     //
  334.     while( lIndex >= 0 ){
  335.  
  336.         //
  337.         // Get listbox text, and compute the depth of the directory
  338.         //
  339.         if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex,
  340.                             (LPARAM)szFileName ) < 0 ){
  341.             ErrorMsg(TEXT(" Expand Directory:  Get listbox text failure"));
  342.             return(0);
  343.         }
  344.  
  345.         lDirDepth = GetDirDepth(szFileName, &lpszInfoPtr);
  346.         if( lDirDepth == -1){
  347.             ErrorMsg(TEXT("ConstructDirName: GetDirDepth failed"));
  348.             return(0);
  349.         }
  350.  
  351.         //
  352.         // If we've reached the next level up, add to the directory name
  353.         //
  354.         if( lDirDepth < lSeekDepth ){
  355.             lSeekDepth = lDirDepth;
  356.  
  357.             // check if we will exceed the size of our buffer
  358.             if( lstrlen(lpszInfoPtr) + lstrlen(lpszDirName) >
  359.                     (DIRECTORY_STRING_SIZE << 1) ){
  360.                 ErrorMsg(TEXT("ConstructDirName:  Exceeded Directory Size limit"));
  361.                 return(0);
  362.             }
  363.  
  364.             // Find the end of the directory name
  365.             lpszHold = TStrChr(lpszInfoPtr, TEXT('\0'));
  366.  
  367.             // If we're not at the root, add a '\'
  368.             if( lIndex && (*lpszDirName != TEXT('\0')) )
  369.                 *lpszHold++ = TEXT('\\');
  370.  
  371.             // Append the heretofore computed path to the end of the dir name
  372.             lstrcpy( lpszHold, lpszDirName);
  373.             // Copy the whole path so far back into the final buffer
  374.             lstrcpy( lpszDirName, ++lpszInfoPtr);
  375.         }
  376.  
  377.         // If the first level dir has been added, jump to the root,
  378.         //   else go up to the previous entry in the listbox.
  379.         if( lDirDepth == 1 )
  380.             lIndex = 0;
  381.         else
  382.             lIndex--;
  383.     }
  384.  
  385.     return(TRUE);
  386. }
  387.  
  388.  
  389. /***************************************************************************\
  390. *
  391. * GetDirDepth()
  392. *
  393. * Returns:  -1 if error
  394. *           otherwise, the depth of the directory (i.e. root is depth 0,
  395. *           c:\foo is depth 1, etc.  This is computed in perhaps not the
  396. *           most efficient way, by counting the '|'s proceeding the name.
  397. *
  398. *           This function also returns in lpszDirName a pointer to the
  399. *           end of the preceeding '|' characters within the given listbox str.
  400. *           If the function fails, this pointer value is undefined.
  401. *
  402. * The left shifted index is to skip the tab characters. See ConstructLBEntry().
  403. *
  404. * History:
  405. * 5/7/93
  406. *   Created.
  407. *
  408. \***************************************************************************/
  409. LONG GetDirDepth(LPTSTR lpszLBString, LPTSTR *lpszDirName)
  410. {
  411.     TCHAR cBar;
  412.     LONG  lCount = 0;
  413.  
  414.     do{
  415.  
  416.         cBar = lpszLBString[lCount << 1];
  417.  
  418.         if( cBar == TEXT('|') )
  419.             lCount++;
  420.         else
  421.             if( cBar != TEXT('+') && cBar != TEXT('-') ){
  422.                 ErrorMsg(TEXT("GetDirDepth: string parse error"));
  423.                 return(-1);
  424.             }
  425.  
  426.     }while( cBar != TEXT('+') && cBar != TEXT('-') );
  427.  
  428.     *lpszDirName = &(lpszLBString[lCount << 1]);
  429.  
  430.     return( lCount );
  431. }
  432.  
  433.  
  434. /***************************************************************************\
  435. *
  436. * CollapseDir()
  437. *
  438. * If directory is expanded, collapses it, by deleteing any subdirectory
  439. *  entries below it.
  440. *
  441. * Returns:  TRUE if successful, FALSE if error
  442. *
  443. * History:
  444. * 5/11/93
  445. *   Created.
  446. *
  447. \***************************************************************************/
  448. BOOL CollapseDir(LPCINFO lpCInfo, LONG lIndex, LONG lDirDepth)
  449. {
  450.     TCHAR   szFileName[DIRECTORY_STRING_SIZE << 1];   // file name buffer
  451.     LPTSTR  lpszNamePtr;
  452.  
  453.     LONG    lDepthHold;
  454.  
  455.  
  456.     //
  457.     // Remove any following LB entries until we return to same depth
  458.     //
  459.     do{
  460.  
  461.         if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex + 1,
  462.                             (LPARAM)szFileName ) < 0 ){
  463.             ErrorMsg(TEXT(" Expand Directory:  Get listbox text failure"));
  464.             return(0);
  465.         }
  466.  
  467.         lDepthHold = GetDirDepth( szFileName, &lpszNamePtr);
  468.         if( lDirDepth == -1 ){
  469.             ErrorMsg(TEXT(" Expand Directory:  GetDirDepth failure"));
  470.             return(0);
  471.         }
  472.  
  473.         if( lDirDepth < lDepthHold )
  474.             if( SendMessage( lpCInfo->hDirLB, LB_DELETESTRING, (WPARAM)lIndex + 1,
  475.                                 (LPARAM)0 ) < 0 ){
  476.                 ErrorMsg(TEXT(" Expand Directory:  Delete String failure"));
  477.                 return(0);
  478.             }
  479.  
  480.     }while( lDirDepth < lDepthHold );
  481.  
  482.     return(1);
  483. }
  484.  
  485.  
  486. /***************************************************************************\
  487. *
  488. * ConstructLBEntry()
  489. *
  490. * Given the parent's directory depth, and the subdirectory name, inserts
  491. * '|'s equal to the depth+1 of the parent (plus tab character), and an
  492. * unexpanded '+' directory marker, then the name of the subdirectory.
  493. *
  494. * The left shifted index is to skip the tab characters.
  495. *
  496. * Returns:  void.  the completed listbox entry is returned in szLBEntry.
  497. *
  498. * History:
  499. * 5/14/93
  500. *   Created.
  501. *
  502. \***************************************************************************/
  503. void ConstructLBEntry(LONG lDirDepth, LPTSTR szFileName, LPTSTR szLBEntry)
  504. {
  505.     int i;
  506.  
  507.     for(i = 0; i <= lDirDepth; i++){
  508.         szLBEntry[i << 1] = TEXT('|');
  509.         szLBEntry[(i << 1) + 1] = 9;
  510.     }
  511.  
  512.     szLBEntry[i << 1] = TEXT('+');
  513.     szLBEntry[(i << 1) + 1] = TEXT('\0');
  514.  
  515.     lstrcat(szLBEntry, szFileName);
  516. }
  517.  
  518.  
  519. /***************************************************************************\
  520. *
  521. * FullExpand()
  522. *
  523. * From the unexpanded root, walks down directory LB, expanding each directory
  524. *  until it reaches the end of the tree.
  525. *
  526. * Returns:  void
  527. *
  528. * History:
  529. * 5/20/93
  530. *   Created.
  531. *
  532. \***************************************************************************/
  533. BOOL FullExpand(LPCINFO lpCInfo)
  534. {
  535.     LONG lIndex = 0;
  536.  
  537.     if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
  538.             == WAIT_TIMEOUT ){
  539.         ErrorMsg(TEXT("FullExpand: Dir LB Mutex Timeout."));
  540.         return(0);
  541.     }
  542.  
  543.     while( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL,
  544.                         (WPARAM)lIndex,
  545.                         (LPARAM)0) != LB_ERR ){
  546.         if( !ExpDir( lpCInfo ) ){
  547.             ErrorMsg(TEXT("Full Expand: ExpDir failure."));
  548.             ReleaseMutex( lpCInfo->hDirMutex );
  549.  
  550.             // This is in case the ExpDir failed because a change drive
  551.             //  command caused a kill.
  552.             if( !PostMessage(lpCInfo->hwnd, WM_COMMAND, MM_REFRESH,
  553.                              (LPARAM)0) )
  554.                 ErrorMsg(TEXT("ExpDir: MM_REFRESH call failure."));
  555.             return(0);
  556.         }
  557.  
  558.         lIndex++;
  559.     }
  560.  
  561.     //  Set selection in listboxes to first item.
  562.     if( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL,
  563.                         (WPARAM)0,
  564.                         (LPARAM)0) == LB_ERR ){
  565.         ErrorMsg(TEXT("Full Expand: Dir LB Set Selection Error"));
  566.         ReleaseMutex( lpCInfo->hDirMutex );
  567.         return(0);
  568.     }
  569.  
  570.     ReleaseMutex( lpCInfo->hDirMutex );
  571.     return(1);
  572. }
  573.