core/vgui/impl/mfc/vgui_dir_dialog.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \brief  creates a directory browsing dialog, it allows to choose both directories and files
00004 // \author Gamze Tunali, LEMS, Brown University
00005 // \date   16 Nov 2007
00006 //
00007 // The CDirDialog class implements a directory selection dialog by deriving from the file
00008 // selection dialog class (CFileDialog).  This dialog has the advantages of the standard
00009 // file open dialog (resizeable, ability to create/delete directories etc) but is
00010 // customized for the selection of a directory rather than a file.
00011 //
00012 // USER INTERFACE
00013 // --------------
00014 //
00015 // For example, the user can double click on a directory name or type it into the
00016 // edit control to open it in the directory listing display.  To say that this is
00017 // the directory they want the user must click the "Open" button.  When the user
00018 // enters a backslash (\) as the last character in the edit control the display is
00019 // changed to show the contents of the directory if it exists.  The same is done if
00020 // the user presses the Enter key except that if the directory does not exist the
00021 // user is asked if they want to create it.
00022 //
00023 // When the user enters other characters and the contents don't end in a backslash
00024 // then automatic directory name completion is attempted.  If the contents of the
00025 // edit box are the first character(s) of one unique existing directory then the
00026 // rest of the directory name is added to the end of the edit box.  These characters
00027 // are selected so that the user can type something else and it they will be ignored.
00028 //
00029 // When selecting a directory you would normally not want to see files, but you may on
00030 // occasion.  The normal "Files of Type:" drop down list is available but it has an
00031 // extra entry "Show Folders Only" that is selected by default.  When files are
00032 // displayed double clicking of them is ignored (unlike the normal File Open dialog).
00033 // Double-clicking a directory name changes to that directory as normal.
00034 //
00035 // PROGRAMMER INTERFACE
00036 // --------------------
00037 //
00038 // Add DirDialog.cpp and DirDialog.h to your project.  Include DirDialog.h
00039 // in the source file(s) where you want to use the CDirDialog class.
00040 //
00041 // Create a CDirDialog object using the constructor described below.  If necessary
00042 // you may then modify values in the m_ofn member of the CFileDialog base class
00043 // (see the Win32 documentation for OPENFILENAME).  For example, to change the
00044 // text that appears in the title bar of the dialog use m_ofn.lpstrTitle.
00045 //
00046 // Call DoModal() to activate the dialog.  If DoModal() return IDOK you can then
00047 // call GetPath() to obtain the name of the directory that the user selected.
00048 //
00049 //
00050 // CDirDialog::CDirDialog(LPCTSTR lpstrInitialDir = NULL,
00051 //                        LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL);
00052 //
00053 //      lpstrInitialDir
00054 //          The initial directory.  If NULL then the current directory is used.
00055 //          See lpstrInitialDir in the Win32 documentation for OPENFILENAME for more info.
00056 //
00057 //      lpszFilter
00058 //          The string pairs that specify the file filters to use.  See lpszFilter
00059 //          in the documentation for the CFileDialog constructor for more info.
00060 //          Note that an extra entry is always added that allows the user to hide the
00061 //          display of all files.  If NULL is used (the default) then only the
00062 //          "no files" entry and an "all files" entry are provided.
00063 //
00064 //      pParentWnd
00065 //          A pointer to the dialog parent window.
00066 //
00067 // virtual int CDirDialog::DoModal()
00068 //
00069 //      see CFileDialog::DoModal().
00070 //
00071 // CString CDirDialog::GetPath()
00072 //
00073 //      return value
00074 //          The full path name chosen by the user.
00075 //
00076 // Example:
00077 //
00078 //      // Called when the Browse button is clicked in CMyDialog
00079 //      void CMyDialog::OnBrowseDir()
00080 //      {
00081 //          if (!UpdateData(TRUE))          // Update current value of m_dir from control
00082 //              return;
00083 //
00084 //          CDirDialog dlg(m_dir,
00085 //                        "JPEG Files (*.jpg; *.jpeg)|*.jpg;*.jpeg|All Files (*.*)|*.*||",
00086 //                        this);
00087 //          dlg.m_ofn.lpstrTitle = "Select Folder to Store Images";
00088 //
00089 //          if (dlg.DoModal() == IDOK)
00090 //          {
00091 //              m_dir = dlg.GetPath();      // Store selected directory name back into the control
00092 //              UpdateData(FALSE);
00093 //          }
00094 //      }
00095 //
00096 //
00097 // INTERNAL DESIGN
00098 // ---------------
00099 //
00100 // The following changes are made to the controls in the standard file open dialog:
00101 //
00102 // The "Open" button is hidden and replaced with another button (IDC_OPEN).
00103 // The normal edit control (edt1) where the file name is entered is hidden and replaced
00104 // by a "subclassed" edit control (IDC_DIR) of class CDirEdit (derived from CEdit).
00105 // By hiding and replacing these controls we can manipulate the behaviour
00106 // of the dialog in ways not provided for in any other way.  For example, by changing
00107 // the contents of the hidden edit control (edt1) and simulating a click of the hidden
00108 // Open button (IDOK) we can force the contents of a directory to be displayed.
00109 //
00110 // An extra entry is added to the file types drop down combo called "Show Folders Only"
00111 // that causes no files to be displayed.  (If no filters are supplied at all by using
00112 // the default value of NULL, then an "All Files" filter type is also added.)
00113 // The filter string is a single dot (full-stop) which will match no files.
00114 //
00115 // The new edit control (IDC_DIR) is subclassed so that the contents are monitored and
00116 // the some keys can be intercepted.  When the contents are changed and they end with
00117 // a backslash the current display is changed to point to the directory entered (if it
00118 // exists).  When return is pressed the directory is also changed, but if it doesn't
00119 // exist then the user is asked if he wants to create it.  The way the directory is
00120 // changed (ie. the files of that directory are shown in the display) is by putting the
00121 // directory name into the original edit control (edt1) and simulating a click of the
00122 // original Open button (IDOK).  Directory name completion is also performed as the
00123 // user types in a directory name.
00124 //
00125 // The IDC_OPEN button is used as a replacement for the IDOK button while still allowing
00126 // the hidden IDOK button to be used to change the displayed directory.
00127 //
00128 // The CDirDialog class is derived from CFileDialog. The following base class members
00129 // are overridden:
00130 // - OnInitDone: so that the dialog controls can be reorganized
00131 // - OnFolderChange: so that when the user clicks on a folder name the edit control can
00132 //   be updated to reflect the name of the currently selected directory
00133 // - OnFileNameOK: always returns TRUE so that the user can't select files by
00134 //   double-clicking them (this is a DIRECTORY selection dialog after all)
00135 
00136 
00137 #include "stdafx.h"
00138 
00139 // If you don't want this as part of your project (eg to put in a library) remove
00140 // the above #include "stdafx.h" and uncomment the following 3 lines:
00141 //#define VC_EXTRALEAN        // Exclude rarely-used stuff from Windows headers
00142 //#include <afxwin.h>         // MFC core and standard components
00143 //#include <afxext.h>         // MFC extensions
00144 
00145 #include <Dlgs.h>           // For file dialog control IDs
00146 #include <imagehlp.h>       // For ::MakeSureDirectoryPathExists()
00147 
00148 #include "vgui_dir_dialog.h"      // Our own header file
00149 #include <vcl_cstring.h>
00150 #include <vcl_cctype.h>
00151 
00152 
00153 #ifdef _DEBUG
00154 #define new DEBUG_NEW
00155 #undef THIS_FILE
00156 static char THIS_FILE[] = __FILE__;
00157 #endif
00158 
00159 #define IDC_DIR 181         // New edit control for entering the directory name
00160 #define IDC_OPEN 182        // New "Open" button
00161 
00162 // Class CDlgWnd
00163 BEGIN_MESSAGE_MAP(CDlgWnd, CWnd)
00164         ON_BN_CLICKED(IDC_OPEN, OnOpen)
00165 END_MESSAGE_MAP()
00166 
00167 
00168 void CDlgWnd::OnOpen()
00169 {
00170     // Get the text and check whether it is a valid directory
00171     CString ss;
00172     CEdit *pEdit = (CEdit *)GetDlgItem(IDC_DIR);
00173     ASSERT(pEdit != NULL);
00174     pEdit->GetWindowText(ss);
00175     int len = ss.GetLength();
00176 
00177     if (len == 2 && ss[0] == '\\' && ss[1] == '\\')
00178     {
00179         AfxMessageBox(ss + _T("\nThis is not a valid folder."));
00180         pEdit->SetFocus();
00181         return;
00182     }
00183     else if (len == 0 || len == 1 && ss[0] == '\\')
00184     {
00185         // Current directory or root of the current drive (therefore must be valid)
00186         ;
00187     }
00188     else if ((len == 2 && ss[1] == ':') ||
00189              (len == 3 && ss[1] == ':' && ss[2] == '\\') )
00190     {
00191         _TCHAR rootdir[4] = _T("?:\\");
00192         rootdir[0] = ss[0];
00193 
00194         if (GetDriveType(rootdir) <= DRIVE_NO_ROOT_DIR)
00195         {
00196             AfxMessageBox(ss + _T("\nThe drive is invalid."));
00197             pEdit->SetFocus();
00198             return;
00199         }
00200     }
00201     else
00202     {
00203         // Check that it's a valid directory
00204         if (ss[len-1] == '\\')
00205             ss = ss.Left(--len);
00206         DWORD attr = GetFileAttributes(ss);
00207         if (attr == 0xFFFFFFFF)
00208         {
00209             const char *ss2;
00210 
00211             // Directory not found but maybe it's an invalid drive
00212             _TCHAR rootdir[4] = _T("?:\\");
00213             rootdir[0] = ss[0];
00214 
00215             if (len > 1 && ss[1] == ':' && GetDriveType(rootdir) <= DRIVE_NO_ROOT_DIR)
00216             {
00217                 AfxMessageBox(ss + _T("\nThe drive is invalid."));
00218                 pEdit->SetFocus();
00219                 return;
00220             }
00221             else if (len >= 2 && ss[0] == '\\' && ss[1] == '\\' &&
00222                      ( (ss2 = vcl_strchr((const char *)ss+2, '\\')) == NULL || vcl_strchr(ss2+1, '\\') == NULL) )
00223             {
00224                 AfxMessageBox(ss + _T("\nThis is not a valid folder."));
00225                 pEdit->SetFocus();
00226                 return;
00227             }
00228             else
00229             {
00230                 // Appears to be a valid drive (or relative path)
00231                 CString mess(ss);
00232                 mess += _T("\nThis folder does not exist.\n\nDo you want to create it?");
00233                 if (AfxMessageBox(mess, MB_YESNO) == IDYES)
00234                 {
00235                     // MakeSureDirectoryPathExists is not part of Windows but is
00236                     // in the IMAGHLP.DLL which is always present.  This call
00237                     // requires linking with IMAGHLP.LIB.
00238                     if (!::MakeSureDirectoryPathExists(ss + _T("\\")))
00239                     {
00240                         switch (GetDriveType(rootdir))
00241                         {
00242                           case DRIVE_CDROM:
00243                             AfxMessageBox(_T("You cannot create this folder\n"
00244                                           "as the CD ROM medium is read-only."));
00245                             break;
00246                           case DRIVE_REMOVABLE:
00247                             AfxMessageBox(_T("You cannot create this folder.\n"
00248                                           "The medium may be write-protected."));
00249                             break;
00250                           case DRIVE_REMOTE:
00251                             AfxMessageBox(_T("You do not have permission to create\n"
00252                                           "this folder on the network."));
00253                             break;
00254                           default:
00255                             AfxMessageBox(_T("You do not have permission\n"
00256                                           "to create this folder."));
00257                             break;
00258                         }
00259                         pEdit->SetFocus();
00260                         return;         // Directory could not be created
00261                     }
00262                     // directory was created, so continue
00263                 }
00264                 else
00265                 {
00266                     pEdit->SetFocus();
00267                     return;             // User did not want to create directory
00268                 }
00269             }
00270         }
00271         else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
00272         {
00273            // AfxMessageBox(ss + _T("\nThis is a file not a directory."));
00274             //pEdit->SetFocus();
00275             //return;
00276         }
00277     }
00278 
00279     // We have now selected a directory and will return from the dialog
00280     CheckDir(ss);
00281 
00282     ::EndDialog(m_hWnd, IDOK);
00283 }
00284 
00285 // This routine updates the directory/file list display using the directory
00286 // name given.  It does this by putting the name in the (hidden) edit control
00287 // and simulating a press of the (hidden) IDOK button.  If the directory is
00288 // invalid in some way the currently displayed list will not be changed and
00289 // some sort of error message may be displayed.
00290 void CDlgWnd::CheckDir(const CString &ss)
00291 {
00292     // Put the new directory into the old (hidden) edit box
00293     CEdit *pOld = (CEdit *)GetDlgItem(edt1);
00294     ASSERT(pOld != NULL);
00295     pOld->SetWindowText(ss);
00296 
00297     // Save the current text/selection in the edit control
00298     CString strSaved;                       // Current text in edit box
00299     int start, end;                         // Current selection in edit box
00300     CEdit *pEdit = (CEdit *)GetDlgItem(IDC_DIR);
00301     ASSERT(pEdit != NULL);
00302     pEdit->GetWindowText(strSaved);
00303     pEdit->GetSel(start, end);
00304 
00305     CWnd *pOK = GetDlgItem(IDOK);
00306     pOK->SendMessage(WM_LBUTTONDOWN);
00307     pOK->SendMessage(WM_LBUTTONUP);
00308 
00309     CString strNew;
00310     pEdit->GetWindowText(strNew);
00311 
00312     // Usually we want to keep what the user has typed (strSaved) rather than what has been
00313     // put in the edit control due to OnFolderChange.  One exception is if the user has
00314     // used "..", "..." etc to change to an ancestor directory in which case we don't want to
00315     // leave this the same as it will cause repeated changes to ancestor directories whenever
00316     // the user types backslash (\).  Also don't set the edit string back to what the user
00317     // typed if it would be empty or unchanged except for case (as the case probably looks
00318     // better the way it was filled in).
00319     if (strSaved.IsEmpty() || strSaved[0] == '.' ||
00320         strNew.CompareNoCase(strSaved) == 0 || strNew.CompareNoCase(strSaved + '\\') == 0)
00321     {
00322         pEdit->SetSel(strNew.GetLength(), -1);
00323     }
00324     else
00325     {
00326         // Restore the edit control the way the user typed it
00327         pEdit->SetWindowText(strSaved);
00328         pEdit->SetSel(start, end);
00329     }
00330 }
00331 // --- class CDlgWnd ---
00332 
00333 // CDirEdit control class
00334 BEGIN_MESSAGE_MAP(CDirEdit, CEdit)
00335     ON_WM_CHAR()
00336     ON_WM_KEYDOWN()
00337     ON_WM_GETDLGCODE()
00338 END_MESSAGE_MAP()
00339 
00340 void CDirEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
00341 {
00342     CDlgWnd *pp;                           // Parent = the dialog itself
00343     VERIFY(pp = (CDlgWnd *)GetParent());
00344 
00345     if (nChar == '\t')
00346     {
00347         // Because we are getting all keys (see OnGetDlgCode()) so that we can get the Return key,
00348         // we also get the tab key as a side-effect.  This means that the tabbing between controls
00349         // in the dialog will stop at the edit control unless we force it to go to the next control.
00350         CWnd *pWnd = pp->GetDlgItem(IDC_OPEN);
00351         ASSERT(pWnd != NULL);
00352         pWnd->SetFocus();                       // Set focus to Open button
00353     }
00354     else if (nChar == '\r' || nChar == '\n')
00355     {
00356         // If return key is pressed we change to the directory specified OR
00357         // if the directory name appears valid but does not exist we ask the
00358         // user if they want to create it.  Note that the string is not
00359         // validated (although some validation may be done by Windows
00360         // via the CheckDir() call).  The name is only checked to see if
00361         // it is possible that a directory needs to be created.
00362         // Full validation is deferred till the "Open" button is clicked.
00363 
00364         CString ss;
00365         GetWindowText(ss);
00366         int len = ss.GetLength();
00367 
00368         // Remove trailing backslash unless root directory or network root
00369         if (vcl_strcmp(ss,"\\") != 0 && vcl_strcmp(ss,"\\\\") != 0 && vcl_strcmp((const char *)ss+1,":\\") != 0 &&
00370             len > 0 && ss[len-1] == '\\' )
00371         {
00372             ss = ss.Left(--len);
00373         }
00374 
00375         if (len == 0 ||
00376             len == 1 && ss[0] == '\\' ||
00377             len >= 2 && ss[0] == '\\' && ss[1] == '\\' && vcl_strchr((const char *)ss+2, '\\') == NULL ||
00378             len == 2 && ss[1] == ':' ||
00379             len == 3 && ss[1] == ':' && ss[2] == '\\' )
00380         {
00381             // Definitely not a createable directory
00382             pp->CheckDir(ss);
00383         }
00384         else
00385         {
00386             // Check if it's an existing directory
00387             CFileStatus fs;
00388 
00389             DWORD attr = GetFileAttributes(ss);
00390             if (attr == 0xFFFFFFFF)
00391             {
00392                 // Directory not found but maybe it's an invalid drive
00393                 _TCHAR rootdir[4] = _T("?:\\");
00394                 rootdir[0] = ss[0];
00395 
00396                 if (len == 1 || (len > 1 && ss[1] != ':') ||
00397                     GetDriveType(rootdir) > DRIVE_NO_ROOT_DIR)
00398                 {
00399                     // Appears to be a valid drive (or relative path)
00400                     CString mess(ss);
00401                     mess += _T("\nThis folder does not exist.\n\nDo you want to create it?");
00402                     if (AfxMessageBox(mess, MB_YESNO) == IDYES)
00403                     {
00404                         if (!::MakeSureDirectoryPathExists(ss + _T("\\")))
00405                         {
00406                             switch (GetDriveType(rootdir))
00407                             {
00408                               case DRIVE_CDROM:
00409                                 AfxMessageBox(_T("You cannot create this folder\n"
00410                                               "as the CD ROM medium is read-only."));
00411                                 break;
00412                               case DRIVE_REMOVABLE:
00413                                 AfxMessageBox(_T("You cannot create this folder.\n"
00414                                               "The medium may be write-protected."));
00415                                 break;
00416                               case DRIVE_REMOTE:
00417                                 AfxMessageBox(_T("You do not have permission to create\n"
00418                                               "this folder on the network."));
00419                                 break;
00420                               default:
00421                                 AfxMessageBox(_T("You do not have permission or\n"
00422                                               "otherwise cannot create this folder."));
00423                                 break;
00424                             }
00425                             return;
00426                         }
00427                     }
00428                     else
00429                         return;
00430                 }
00431             }
00432             pp->CheckDir(ss);
00433             // Make sure the directory name ends with backslash so user can type sub-directory name
00434             GetWindowText(ss);
00435             if (ss[ss.GetLength()-1] != '\\')
00436             {
00437                 ss += "\\";
00438                 SetWindowText(ss);
00439             }
00440             SetSel(ss.GetLength(), -1);
00441         }
00442         SetFocus();                         // Make sure caret stays in this edit control
00443     }
00444     else
00445     {
00446         CEdit::OnChar(nChar, nRepCnt, nFlags);
00447 
00448         // Get the text and check whether it is a valid directory
00449         CString ss;                         // Current text in the edit control
00450         GetWindowText(ss);
00451 
00452         int len = ss.GetLength();
00453         int start, end;                     // Current selection
00454         GetSel(start, end);
00455 
00456         if (ss.Compare(_T("\\\\")) == 0)
00457         {
00458             // Don't check \\ else we get a message about "\\" being an invalid filename
00459             ;
00460         }
00461         else if (ss.Compare(_T("\\")) == 0)
00462         {
00463             // Show root directory
00464             pp->CheckDir(ss);
00465         }
00466         else if (len == 3 && ss[1] == ':' && ss[2] == '\\')
00467         {
00468             // Check that it's a valid drive
00469             if (GetDriveType(ss) > DRIVE_NO_ROOT_DIR)
00470             {
00471                 pp->CheckDir(ss);
00472             }
00473         }
00474         else if (len > 0 && ss[len-1] == '\\')
00475         {
00476             // Check that it's a valid directory
00477             // xxx does not handle "\\anwar\"
00478             DWORD attr = GetFileAttributes(ss);
00479             if (attr != 0xFFFFFFFF && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
00480             {
00481                 pp->CheckDir(ss);
00482             }
00483         }
00484         else if (start == len && nChar != '\b')
00485         {
00486             // Try to do completion of the directory name
00487             CFileFind ff;                   // Used to find directory names that start with ss
00488             int count = 0;                  // Number of matching directory names
00489             CString strMatch;               // The last directory found that matches
00490 
00491             BOOL bContinue = ff.FindFile(ss + "*");
00492 
00493             while (bContinue)
00494             {
00495                 // At least one match - check them all
00496                 bContinue = ff.FindNextFile();
00497 
00498                 if (ff.IsDirectory())
00499                 {
00500                     // Found a matching directory
00501                     ++count;
00502                     strMatch = ff.GetFileName();
00503                 }
00504             }
00505 
00506             // If there was exactly one matching directory use it
00507             if (count == 1)
00508             {
00509                 int ii;
00510                 // The file open dialog changes all uppercase names to lower case with an initial
00511                 // capital (eg WINDOWS displays as Windows).  We do the same so things look nicer.
00512                 for (ii = 0; ii < strMatch.GetLength(); ++ii)
00513                 {
00514                     // Don't change if it contains spaces or lowercase letters
00515                     if (vcl_isspace(strMatch[ii]) || vcl_islower(strMatch[ii]))
00516                         break;
00517                 }
00518 
00519                 ASSERT(ii <= strMatch.GetLength());
00520                 if (!strMatch.IsEmpty() && ii == strMatch.GetLength())
00521                 {
00522                     CString temp = strMatch.Mid(1);
00523                     temp.MakeLower();
00524                     strMatch = strMatch.Left(1) + temp;
00525                 }
00526 
00527                 // Get the bit of the directory name that the user has not yet typed
00528                 int lb_len;             // Length of last bit (after \ or :)
00529                 lb_len = ss.ReverseFind('\\');
00530                 if (lb_len == -1) lb_len = ss.ReverseFind('/');
00531                 if (lb_len == -1) lb_len = ss.ReverseFind(':');
00532                 if (lb_len == -1)
00533                     lb_len = ss.GetLength();
00534                 else
00535                     lb_len = ss.GetLength() - (lb_len+1);
00536 
00537                 // Check if the last char is the same case as the same char in the matched name
00538                 if (!ss.IsEmpty() && lb_len > 0 && strMatch[lb_len-1] != ss[ss.GetLength()-1])
00539                 {
00540                     // The user used different case to that of the corresponding character in
00541                     // the matched directory so change the matched name to be the user's case.
00542                     if (vcl_isupper(ss[ss.GetLength()-1]))
00543                         strMatch.MakeUpper();
00544                     else
00545                         strMatch.MakeLower();
00546                 }
00547 
00548 #ifdef _DEBUG
00549                 CString temp = strMatch.Left(lb_len);
00550                 ASSERT(temp.CompareNoCase(ss.Right(lb_len)) == 0);
00551 #endif
00552                 end += strMatch.GetLength() - lb_len;
00553                 SetWindowText(ss + strMatch.Mid(lb_len));
00554                 SetSel(start, end);
00555             }
00556 
00557             // else if (count > 1) pop-up some sort of selection list???
00558         }
00559         SetFocus();                         // Make sure caret stays in this edit control
00560     }
00561 }
00562 
00563 void CDirEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
00564 {
00565     CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
00566 
00567     if (nChar != VK_DELETE)
00568         return;
00569 
00570     CDlgWnd *pp;                           // Parent = the dialog itself
00571     VERIFY(pp = (CDlgWnd *)GetParent());
00572 
00573     // Get the current text and check whether it is a valid directory
00574     CString ss;
00575     GetWindowText(ss);
00576     int len = ss.GetLength();
00577 
00578     if (ss.Compare(_T("\\\\")) == 0)
00579     {
00580         // Don't check \\ else we get a message about "\\" being an invalid filename
00581         ;
00582     }
00583     else if (ss.Compare(_T("\\")) == 0)
00584     {
00585         // Show root directory
00586         pp->CheckDir(ss);
00587     }
00588     else if (len == 3 && ss[1] == ':' && ss[2] == '\\')
00589     {
00590         // Check that it's a valid drive
00591         if (GetDriveType(ss) > DRIVE_NO_ROOT_DIR)
00592         {
00593             pp->CheckDir(ss);
00594         }
00595     }
00596     else if (len > 0 && ss[len-1] == '\\')
00597     {
00598         // Check that it's a valid directory
00599         DWORD attr = GetFileAttributes(ss);
00600         if (attr != 0xFFFFFFFF && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
00601         {
00602             pp->CheckDir(ss);
00603         }
00604     }
00605     SetFocus();                         // Make sure caret stays in this edit control
00606 }
00607 
00608 UINT CDirEdit::OnGetDlgCode()
00609 {
00610     // Get all keys so that we see CR
00611     return CEdit::OnGetDlgCode() | DLGC_WANTALLKEYS;
00612 }
00613 // --- class CDirEdit ---
00614 
00615 // class vgui_dir_dialog
00616 vgui_dir_dialog::vgui_dir_dialog(LPCTSTR initial, LPCTSTR filter, CWnd* pParentWnd)
00617 #if WINVER >= 0x0600
00618     : CFileDialog(TRUE, NULL, NULL,
00619                   OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST,
00620                   NULL, pParentWnd,0,0), // Set bVistaStyle to FALSE 
00621 #else
00622     : CFileDialog(TRUE, NULL, NULL,
00623                   OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST,
00624                   NULL, pParentWnd),
00625 #endif
00626       m_strPath(initial)
00627 {
00628     // Note: m_strFilter is a member variable so it doesn't disappear because
00629     // it is used later internally by the file open dialog (via m_ofn.lpstrFilter).
00630     if (filter != NULL)
00631       m_strFilter = filter + CString(_T("Show Folders Only|.||"));
00632     else
00633         m_strFilter = _T("All Files (*.*)|*.*||Show Folders Only|.|");
00634     m_strFilter.Replace('|', '\0');
00635     m_ofn.lpstrFilter = m_strFilter;
00636 
00637     m_ofn.lpstrInitialDir = initial;
00638 
00639     m_ofn.lpstrTitle = _T("Select Folder");
00640 
00641     m_ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
00642 }
00643 
00644 void vgui_dir_dialog::OnInitDone()
00645 {
00646     CRect rct;                          // Used to move/resize controls
00647     CWnd *pp;                           // Parent = the dialog window itself
00648     VERIFY(pp = GetParent());
00649 
00650     ASSERT(pp->GetDlgItem(stc3) != NULL);
00651     pp->GetDlgItem(stc3)->SetWindowText(_T("Folder:"));
00652 
00653     // Create a new CDlgWnd so we can catch dialog control notifications
00654     VERIFY(m_DlgWnd.SubclassWindow(pp->m_hWnd));
00655 
00656     // Create a new edit control where edt1 now is
00657     CWnd *w = pp->GetDlgItem(edt1);
00658     ASSERT(pp->GetDlgItem(edt1) != NULL);
00659     pp->GetDlgItem(edt1)->GetWindowRect(rct); //Get edt1 rectangle
00660     pp->ScreenToClient(rct);
00661 
00662     VERIFY(m_Edit.Create(WS_TABSTOP | WS_VISIBLE | WS_CHILD,
00663                            rct, pp, IDC_DIR));
00664     if (m_ofn.lpstrInitialDir  != NULL)
00665         m_Edit.SetWindowText(m_ofn.lpstrInitialDir);
00666     m_Edit.SetFont(pp->GetDlgItem(edt1)->GetFont());
00667     m_Edit.ModifyStyleEx(0, WS_EX_CLIENTEDGE, SWP_DRAWFRAME);
00668     m_Edit.SetWindowPos(pp->GetDlgItem(stc3), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
00669 //  m_Edit.SetSel(0, strlen(m_ofn.lpstrInitialDir));
00670 
00671     CWnd *pCancel = pp->GetDlgItem(IDCANCEL);
00672     ASSERT(pCancel != NULL);
00673 
00674     // Create a new button where the OK button now is
00675     ASSERT(pp->GetDlgItem(IDOK) != NULL);
00676     pp->GetDlgItem(IDOK)->GetWindowRect(rct); //Get OK button rectangle
00677     pp->ScreenToClient(rct);
00678 
00679     m_Open.Create(_T("Open"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
00680                   rct, pp, IDC_OPEN);
00681     m_Open.SetFont(pp->GetDlgItem(IDOK)->GetFont());
00682     m_Open.SetWindowPos(&m_Edit, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
00683 
00684     pCancel->SetWindowPos(&m_Open, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
00685 
00686     // Change default push button
00687     pp->GetDlgItem(IDOK)->ModifyStyle(BS_DEFPUSHBUTTON, 0);
00688     pp->SendMessage(DM_SETDEFID, IDC_OPEN);
00689 
00690 #ifdef DIRDIALOG_TESTING
00691     // Move controls (rather than hide them) for testing
00692 
00693     // Increase size of dialog
00694     pp->GetWindowRect(rct);
00695     pp->SetWindowPos(NULL, 0, 0, rct.Width(), rct.Height() + 70, SWP_NOZORDER | SWP_NOMOVE);
00696 
00697     // Move the replaced controls down
00698     ASSERT(pp->GetDlgItem(IDOK) != NULL);
00699     pp->GetDlgItem(IDOK)->GetWindowRect(rct);
00700     pp->ScreenToClient(rct);
00701     pp->GetDlgItem(IDOK)->SetWindowPos(NULL, rct.left, rct.top+70,
00702                    0, 0, SWP_NOZORDER | SWP_NOSIZE);
00703 
00704     ASSERT(pp->GetDlgItem(edt1) != NULL);
00705     pp->GetDlgItem(edt1)->GetWindowRect(rct);
00706     pp->ScreenToClient(rct);
00707     pp->GetDlgItem(edt1)->SetWindowPos(NULL, rct.left, rct.top+70,
00708                    0, 0, SWP_NOZORDER | SWP_NOSIZE);
00709 
00710 #else
00711     // Hide the controls we don't want the user to use
00712     HideControl(IDOK);
00713     HideControl(edt1);
00714 #endif
00715 
00716     CFileDialog::OnInitDone();
00717 }
00718 
00719 void vgui_dir_dialog::OnFolderChange()
00720 {
00721     CWnd *pp;                           // Parent window = the dialog itself
00722     VERIFY(pp = GetParent());
00723     pp = GetParent();
00724     ASSERT(::IsWindow(pp->m_hWnd));
00725 
00726     ASSERT(pp->GetDlgItem(IDC_DIR) != NULL);
00727     m_strPath = GetFolderPath();
00728     int len = m_strPath.GetLength();
00729     if (len > 0 && m_strPath[len-1] != '\\')
00730     {
00731         m_strPath += "\\";
00732         ++len;
00733     }
00734     pp->GetDlgItem(IDC_DIR)->SetWindowText(m_strPath);
00735     m_Edit.SetSel(len, len);
00736 
00737     CFileDialog::OnFolderChange();
00738 
00739     m_Edit.SetFocus();
00740 }
00741 
00742 BOOL vgui_dir_dialog::OnFileNameOK()
00743 {
00744     CWnd *pp; // Parent window = the dialog itself
00745     VERIFY(pp = GetParent());
00746     ASSERT(::IsWindow(pp->m_hWnd));
00747 
00748     ASSERT(pp->GetDlgItem(IDC_DIR) != NULL);
00749 
00750     m_strPath = GetPathName();
00751     int len = m_strPath.GetLength();
00752 
00753     pp->GetDlgItem(IDC_DIR)->SetWindowText(m_strPath);
00754     m_Edit.SetSel(len, len);
00755 
00756     CFileDialog::OnFolderChange();
00757 
00758     m_Edit.SetFocus();
00759 
00760     return TRUE;
00761 }