📄 dirdialog.cpp
字号:
// DirDialog.cpp - implements class RxDirDialog// The RxDirDialog class implements a directory selection dialog by deriving from the file// selection dialog class (CFileDialog). This dialog has the advantages of the standard// file open dialog (resizeable, ability to create/delete directories etc) but is// customized for the selection of a directory rather than a file.//// USER INTERFACE// --------------//// For example, the user can double click on a directory name or type it into the// edit control to open it in the directory listing display. To say that this is// the directory they want the user must click the "Open" button. When the user// enters a backslash (\) as the last character in the edit control the display is// changed to show the contents of the directory if it exists. The same is done if// the user presses the Enter key except that if the directory does not exist the// user is asked if they want to create it.//// When the user enters other characters and the contents don't end in a backslash// then automatic directory name completion is attempted. If the contents of the// edit box are the first character(s) of one unique existing directory then the// rest of the directory name is added to the end of the edit box. These characters// are selected so that the user can type something else and it they will be ignored.//// When selecting a directory you would normally not want to see files, but you may on// occasion. The normal "Files of Type:" drop down list is available but it has an// extra entry "Show Folders Only" that is selected by default. When files are// displayed double clicking of them is ignored (unlike the normal File Open dialog).// Double-clicking a directory name changes to that directory as normal.//// PROGRAMMER INTERFACE// --------------------//// Add DirDialog.cpp and DirDialog.h to your project. Include DirDialog.h// in the source file(s) where you want to use the RxDirDialog class.//// Create a RxDirDialog object using the constructor described below. If necessary// you may then modify values in the m_ofn member of the CFileDialog base class// (see the Win32 documentation for OPENFILENAME). For example, to change the// text that appears in the title bar of the dialog use m_ofn.lpstrTitle.//// Call DoModal() to activate the dialog. If DoModal() return IDOK you can then// call GetPath() to obtain the name of the directory that the user selected.////// RxDirDialog::RxDirDialog(LPCTSTR lpstrInitialDir = NULL,// LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL);// // lpstrInitialDir// The initial directory. If NULL then the current directory is used.// See lpstrInitialDir in the Win32 documentation for OPENFILENAME for more info.//// lpszFilter// The string pairs that specify the file filters to use. See lpszFilter// in the documentation for the CFileDialog constructor for more info.// Note that an extra entry is always added that allows the user to hide the// display of all files. If NULL is used (the default) then only the// "no files" entry and an "all files" entry are provided.// // pParentWnd// A pointer to the dialog parent window.//// virtual int RxDirDialog::DoModal()//// see CFileDialog::DoModal().//// CString RxDirDialog::GetPath()//// return value// The full path name chosen by the user.//// Example://// // Called when the Browse button is clicked in CMyDialog// void CMyDialog::OnBrowseDir() // {// if (!UpdateData(TRUE)) // Update current value of m_dir from control// return;//// RxDirDialog dlg(m_dir,// "JPEG Files (*.jpg; *.jpeg)|*.jpg;*.jpeg|All Files (*.*)|*.*||",// this);// dlg.m_ofn.lpstrTitle = "Select Folder to Store Images";//// if (dlg.DoModal() == IDOK)// {// m_dir = dlg.GetPath(); // Store selected directory name back into the control// UpdateData(FALSE);// }// }////// INTERNAL DESIGN// ---------------//// The following changes are made to the controls in the standard file open dialog://// The "Open" button is hidden and replaced with another button (IDC_OPEN).// The normal edit control (edt1) where the file name is entered is hidden and replaced // by a "subclassed" edit control (IDC_DIR) of class RxDirEdit (derived from CEdit).// By hiding and replacing these controls we can manipulate the behaviour// of the dialog in ways not provided for in any other way. For example, by changing// the contents of the hidden edit control (edt1) and simulating a click of the hidden// Open button (IDOK) we can force the contents of a directory to be displayed.//// An extra entry is added to the file types drop down combo called "Show Folders Only"// that causes no files to be displayed. (If no filters are supplied at all by using// the default value of NULL, then an "All Files" filter type is also added.)// The filter string is a single dot (full-stop) which will match no files.//// The new edit control (IDC_DIR) is subclassed so that the contents are monitored and// the some keys can be intercepted. When the contents are changed and they end with// a backslash the current display is changed to point to the directory entered (if it// exists). When return is pressed the directory is also changed, but if it doesn't// exist then the user is asked if he wants to create it. The way the directory is// changed (ie. the files of that directory are shown in the display) is by putting the// directory name into the original edit control (edt1) and simulating a click of the// original Open button (IDOK). Directory name completion is also performed as the// user types in a directory name.//// The IDC_OPEN button is used as a replacement for the IDOK button while still allowing// the hidden IDOK button to be used to change the displayed directory.//// The RxDirDialog class is derived from CFileDialog. The following base class members// are overridden:// - OnInitDone: so that the dialog controls can be reorganized// - OnFolderChange: so that when the user clicks on a folder name the edit control can// be updated to reflect the name of the currently selected directory// - OnFileNameOK: always returns TRUE so that the user can't select files by// double-clicking them (this is a DIRECTORY selection dialog after all)#include "stdafx.h"// If you don't want this as part of your project (eg to put in a library) remove// the above #include "stdafx.h" and uncomment the following 3 lines://#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers//#include <afxwin.h> // MFC core and standard components//#include <afxext.h> // MFC extensions#include <Dlgs.h> // For file dialog control IDs#include <imagehlp.h> // For ::MakeSureDirectoryPathExists()#include "DirDialog.h" // Our own header file#include "UnicodeFunc.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif#define IDC_DIR 181 // New edit control for entering the directory name#define IDC_OPEN 182 // New "Open" button// Class RxDlgWndBEGIN_MESSAGE_MAP(RxDlgWnd, CWnd) ON_BN_CLICKED(IDC_OPEN, OnOpen) ON_WM_SIZE()END_MESSAGE_MAP()void RxDlgWnd::OnOpen(){ m_iType = ((CComboBox*)(GetDlgItem(cmb1)))->GetCurSel(); // Get the text and check whether it is a valid directory CString ss; CEdit *pEdit = (CEdit *)GetDlgItem(IDC_DIR); ASSERT(pEdit != NULL); pEdit->GetWindowText(ss); int len = ss.GetLength(); if (len == 2 && ss[0] == '\\' && ss[1] == '\\') { AfxMessageBox(ss + _T("\nThis is not a valid folder.")); pEdit->SetFocus(); return; } else if (len == 0 || len == 1 && ss[0] == '\\') { // Current directory or root of the current drive (therefore must be valid) ; } else if ((len == 2 && ss[1] == ':') || (len == 3 && ss[1] == ':' && ss[2] == '\\') ) { _TCHAR rootdir[4] = _T("?:\\"); rootdir[0] = ss[0]; if (GetDriveType(rootdir) <= DRIVE_NO_ROOT_DIR) { AfxMessageBox(ss + _T("\nThe drive is invalid.")); pEdit->SetFocus(); return; } } else { // Check that it's a valid directory if (ss[len-1] == '\\') ss = ss.Left(--len); DWORD attr = GetFileAttributes(ss); if (attr == 0xFFFFFFFF) { const TCHAR *ss2; // Directory not found but maybe it's an invalid drive _TCHAR rootdir[4] = _T("?:\\"); rootdir[0] = ss[0]; if (len > 1 && ss[1] == ':' && GetDriveType(rootdir) <= DRIVE_NO_ROOT_DIR) { AfxMessageBox(ss + _T("\nThe drive is invalid.")); pEdit->SetFocus(); return; } else if (len >= 2 && ss[0] == '\\' && ss[1] == '\\' && ( (ss2 = _tcschr((const TCHAR *)ss+2, '\\')) == NULL || _tcschr(ss2+1, '\\') == NULL) ) { AfxMessageBox(ss + _T("\nThis is not a valid folder.")); pEdit->SetFocus(); return; } else { // Appears to be a valid drive (or relative path) CString mess(ss); mess += _T("\nThis folder does not exist.\n\nDo you want to create it?"); if (AfxMessageBox(mess, MB_YESNO) == IDYES) { // MakeSureDirectoryPathExists is not part of Windows but is // in the IMAGHLP.DLL which is always present. This call // requires linking with IMAGHLP.LIB. PSTR pstrPath = ToMultiByte(ss + _T("\\")); if (!::MakeSureDirectoryPathExists(pstrPath)) { switch (GetDriveType(rootdir)) { case DRIVE_CDROM: AfxMessageBox(_T("You cannot create this folder\nas the CD ROM medium is read-only.")); break; case DRIVE_REMOVABLE: AfxMessageBox(_T("You cannot create this folder.\nThe medium may be write-protected.")); break; case DRIVE_REMOTE: AfxMessageBox(_T("You do not have permission to create\nthis folder on the network.")); break; default: AfxMessageBox(_T("You do not have permission\nto create this folder.")); break; } pEdit->SetFocus(); return; // Directory could not be created } delete[] pstrPath; // directory was created, so continue } else { pEdit->SetFocus(); return; // User did not want to create directory } } } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { AfxMessageBox(ss + _T("\nThis is a file not a directory.")); pEdit->SetFocus(); return; } } // We have now selected a directory and will return from the dialog CheckDir(ss); ::EndDialog(m_hWnd, IDOK);}// This routine updates the directory/file list display using the directory// name given. It does this by putting the name in the (hidden) edit control// and simulating a press of the (hidden) IDOK button. If the directory is// invalid in some way the currently displayed list will not be changed and// some sort of error message may be displayed.void RxDlgWnd::CheckDir(const CString &ss){ // Put the new directory into the old (hidden) edit box CEdit *pOld = (CEdit *)GetDlgItem(edt1); ASSERT(pOld != NULL); pOld->SetWindowText(ss); // Save the current text/selection in the edit control CString strSaved; // Current text in edit box int start, end; // Current selection in edit box CEdit *pEdit = (CEdit *)GetDlgItem(IDC_DIR); ASSERT(pEdit != NULL); pEdit->GetWindowText(strSaved); pEdit->GetSel(start, end); CWnd *pOK = GetDlgItem(IDOK); pOK->SendMessage(WM_LBUTTONDOWN); pOK->SendMessage(WM_LBUTTONUP); CString strNew; pEdit->GetWindowText(strNew); // Usually we want to keep what the user has typed (strSaved) rather than what has been // put in the edit control due to OnFolderChange. One exception is if the user has // used "..", "..." etc to change to an ancestor directory in which case we don't want to // leave this the same as it will cause repeated changes to ancestor directories whenever // the user types backslash (\). Also don;t set the edit string back to what the user // typed if it would be empty or unchanged except for case (as the case probably looks // better the way it was filled in). if (strSaved.IsEmpty() || strSaved[0] == '.' || strNew.CompareNoCase(strSaved) == 0 || strNew.CompareNoCase(strSaved + '\\') == 0) { pEdit->SetSel(strNew.GetLength(), -1); } else { // Restore the edit control the way the user typed it pEdit->SetWindowText(strSaved); pEdit->SetSel(start, end); }}// --- class RxDlgWnd ---// RxDirEdit control classBEGIN_MESSAGE_MAP(RxDirEdit, CEdit) ON_WM_CHAR() ON_WM_KEYDOWN() ON_WM_GETDLGCODE()END_MESSAGE_MAP()void RxDirEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { RxDlgWnd *pp; // Parent = the dialog itself VERIFY(pp = (RxDlgWnd *)GetParent()); if (nChar == '\t') { // Because we are getting all keys (see OnGetDlgCode()) so that we can get the Return key, // we also get the tab key as a side-effect. This means that the tabbing between controls // in the dialog will stop at the edit control unless we force it to go to the next control. CWnd *pWnd = pp->GetDlgItem(IDC_OPEN); ASSERT(pWnd != NULL); pWnd->SetFocus(); // Set focus to Open button } else if (nChar == '\r' || nChar == '\n') { // If return key is pressed we change to the directory specified OR // if the directory name appears valid but does not exist we ask the // user if they want to create it. Note that the string is not // validated (although some validation may be done by Windows // via the CheckDir() call). The name is only checked to see if // it is possible that a directory needs to be created. // Full validation is deferred till the "Open" button is clicked. CString ss; GetWindowText(ss); int len = ss.GetLength(); // Remove trailing backslash unless root directory or network root if (lstrcmp(ss,_T("\\")) != 0 && lstrcmp(ss,_T("\\\\")) != 0 && lstrcmp((const TCHAR *)ss+1, _T(":\\")) != 0 && len > 0 && ss[len-1] == '\\' ) { ss = ss.Left(--len); } if (len == 0 || len == 1 && ss[0] == '\\' || len >= 2 && ss[0] == '\\' && ss[1] == '\\' && _tcschr((const TCHAR *)ss+2, '\\') == NULL || len == 2 && ss[1] == ':' ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -