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