📄 choosedirectory.cpp
字号:
// ChooseDirectory.cpp : implementation file
//
#include "stdafx.h"
#include "resource.h"
#include "ChooseDirectory.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CChooseDirectory dialog
// DirPath will hold the selected path when the dialog is closed.
// This object is created in the caller function and will not be
// destructed until it goes out of scope in that function.
//
CChooseDirectory::CData::CData()
{
m_DirPath = _T("");
m_OldPath = _T("");
}
// delete the DirPath member
CChooseDirectory::CData::~CData()
{
}
CChooseDirectory::CChooseDirectory(CData& Data, char *Title /*NULL*/, CWnd* pParent /*=NULL*/)
: CDialog(CChooseDirectory::IDD, pParent), m_Data (Data)
{
//{{AFX_DATA_INIT(CChooseDirectory)
m_CurrentSelection = _T("");
//}}AFX_DATA_INIT
m_level = 0;
}
CChooseDirectory::~CChooseDirectory()
{
}
void CChooseDirectory::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CChooseDirectory)
DDX_Control(pDX, IDC_DIRECTORYTREE, m_DirTree);
DDX_Text(pDX, IDC_CURRENTSELECTION, m_CurrentSelection);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CChooseDirectory, CDialog)
//{{AFX_MSG_MAP(CChooseDirectory)
ON_BN_CLICKED(IDHELP, OnHelp)
ON_NOTIFY(TVN_ITEMEXPANDED, IDC_DIRECTORYTREE, OnItemexpandedDirectoryTree)
ON_NOTIFY(TVN_SELCHANGED, IDC_DIRECTORYTREE, OnSelchangedDirectoryTree)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CChooseDirectory message handlers
BOOL CChooseDirectory::OnInitDialog()
{
CDialog::OnInitDialog();
CWaitCursor cw;
// TODO: Add extra initialization here
m_Images.Create (IDB_DRIVEIMAGES, 20, 1, RGB(0x00, 0x80, 0x80));
m_DirTree.SetImageList (&m_Images, TVSIL_NORMAL);
LoadTree ();
// HTREEITEM RootNode = m_DirTree.GetRootItem ();
if (!m_Data.m_OldPath.IsEmpty())
SelectStartDir ((char *)(LPCSTR) m_Data.m_OldPath);
return (true); // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CChooseDirectory::OnOK()
{
char *s, NewPath[_MAX_PATH];
//
// Get the results of our dialog
//
GetDlgItemText (IDC_CURRENTSELECTION, NewPath, _MAX_PATH);
//
// Remove any trailing backslash
//
s = NewPath + strlen (NewPath) - 1;
if (*s == '\\')
*s = '\0';
m_Data.m_DirPath = NewPath;
CDialog::OnOK();
}
void CChooseDirectory::OnHelp()
{
// TODO: Add your control notification handler code here
}
void CChooseDirectory::SelectStartDir(char * StartDir)
{
TCHAR *startDir, Drive[32];
char *s;
HTREEITEM RootNode, node;
if (!strlen (StartDir))
return;
//
// Make a copy of the directory string.
//
startDir = new char [strlen (StartDir) + 1];
strcpy (startDir, (LPCSTR) StartDir);
//
// Make sure the path contains no forward slashes.
//
while ((s = strchr (startDir, '/')) != NULL)
*s = '\\';
s = strtok (startDir, "\\");
if (s == NULL)
{
s = startDir;
}
//
// Make it all upper case. Less confusion that way. Our first node
// should be the device. Append the trailing backslash, which is the
// way the node is returned from BuildPath
//
//
strupr (s);
sprintf (Drive, "%s\\", s);
// Get the root node
RootNode = m_DirTree.GetRootItem ();
// Get the first child item of the root node
node = m_DirTree.GetNextItem(RootNode, TVGN_ROOT);
//
// Loop through the siblings until we find our drive
//
for (; node; node = m_DirTree.GetNextItem(node, TVGN_NEXT))
{
CString ItemText;
BuildPath (ItemText, node);
//
// If the root paths match, we've found it
//
if (!strcmpi (Drive, (LPCSTR) ItemText))
break;
}
//
// Did we find the start for our directory?
//
if (node == NULL)
{
delete [] startDir;
return;
}
//
// Select this item just in case the next strtok is NULL
//
m_DirTree.Select (node, TVGN_CARET);
s = strtok (NULL, "\\"); // Get the next part of the path
if (s == NULL) // If none, we are finished.
{
delete [] startDir;
return;
}
//
// There's more to look for, so expand this item.
//
m_DirTree.Expand (node, TVE_EXPAND); // Always expand
while (s != NULL)
{
for (node = m_DirTree.GetNextItem(node, TVGN_CHILD);
node;
node = m_DirTree.GetNextItem(node, TVGN_NEXT))
{
CString ItemText = m_DirTree.GetItemText (node);
if (!ItemText.CompareNoCase (s))
break;
}
//
// If the node was not found, we can't continue. Leave the current
// selection as it is.
//
if (node == NULL)
break;
//
// Select the current item.
//
m_DirTree.Select (node, TVGN_CARET);
s = strtok (NULL, "\\");
// If the next token is not null, expand the current item.
if (s != NULL)
m_DirTree.Expand (node, TVE_EXPAND); // Always expand
}
delete [] startDir;
}
void CChooseDirectory::OnItemexpandedDirectoryTree(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
// get the node that was expanded or contracted
HTREEITEM node = pNMTreeView->itemNew.hItem;
// Get the Tree Item for the node. Zero out the new structure. Tree control
// functions don't like stray data.
TV_ITEM item;
memcpy ((char *) &item, (char *) &pNMTreeView->itemNew, sizeof (TV_ITEM));
// if the node was contracted then set the item data so that
// later we know not to rebuild the node.
if (pNMTreeView->action == TVE_COLLAPSE)
{
item.mask = TVIF_PARAM;
item.lParam = CChooseDirectory::tmExpanded;
m_DirTree.SetItem(&item);
return;
}
// If the node was already built once then we don't need to do
// it again. If we do, we'll have duplicate entries
if (item.lParam == CChooseDirectory::tmExpanded)
return;
// remove the first child because we're going to add it again
// remember that we have to have already added one child per
// node so that the plus sign gets added to the node
HTREEITEM child = m_DirTree.GetChildItem(node);
if (child)
m_DirTree.DeleteItem(child);
// add this node and all subnodes
AddNode(0, node, 0, CChooseDirectory::tmDetail);
*pResult = 0;
}
void CChooseDirectory::OnSelchangedDirectoryTree(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// get the selected node
HTREEITEM sel = m_DirTree.GetSelectedItem();
CString s;
// build the path name based on the selection
BuildPath(s, sel);
// set the static control's text to the complete path
m_CurrentSelection = s;
UpdateData (FALSE);
*pResult = 0;
}
// BuildPath() builds the path name by recursing backward
// through the tree.
//
void CChooseDirectory::BuildPath(CString& path, HTREEITEM node)
{
// get the parent of the current node
HTREEITEM parent = m_DirTree.GetParentItem(node);
// Get the text of the node. We'll use it to build the path
CString buff = m_DirTree.GetItemText (node);
// add the backslash to the node
buff += "\\";
// Add (prepend) the path to the current directory name, the set the
// path to the new value. This prepends the current directory to the
// path (we're stepping backwards through the directory tree).
//
buff += path;
path = buff;
// If this node has a parent then we recurse until no parent is found.
// If there is no parent, then we are finished building the path.
//
if (parent)
{
BuildPath(path, parent);
}
else
{
// If the parent is found then we need to strip the volume name
// from the string. It is enclosed in parentheses, so look for the
// first open paren and pick up the left side, then find the first
// close paren and pick up the right part.
//
CString dPath = path;
int Length = path.GetLength ();
int pos = dPath.Find("(");
if (pos > 0)
path = dPath.Left (pos - 1);
pos = dPath.Find (")");
if (pos > 0)
path += dPath.Right (Length - pos - 1);
}
}
void CChooseDirectory::LoadTree()
{
char drives [_MAX_PATH];
char *aDrive, *s; // = drives;
HTREEITEM RootNode = m_DirTree.GetRootItem ();
// Get the drive string. This is a double null terminated list, with
// each item terminated with a null.
//
if (::GetLogicalDriveStrings(_MAX_PATH, drives))
{
char CurDrive[64]; // We need the copy because we are going to
// remove any trailing backslash, which would
// screw up our double null terminated list.
aDrive = drives;
// Parse the drives
do
{
UINT type;
// Get a drive
strcpy (CurDrive, aDrive);
type = ::GetDriveType(CurDrive);
if ((s = strchr (CurDrive, '\\')) != NULL)
*s = '\0';
// Make sure the drive letter is caps and add the node for the drive.
strupr (CurDrive);
AddNode(CurDrive, RootNode, type, CChooseDirectory::tmShort);
aDrive += strlen (aDrive) + 1;
} while(strlen (aDrive));
}
}
// AddNode(). This function adds the nodes to the tree. The first
// time we display a node we don't want to add each and every
// sub-directory to the tree. We only want to add nodes when
// necessary or it will take forever for the dialog box to come
// up initially. But, we have to add at least one node so that
// the + sign shows up indicating that sub-directories exist.
void CChooseDirectory::AddNode (const char * path, HTREEITEM node, int type, int mode)
{
WIN32_FIND_DATA fd;
HANDLE hFind;
char buff [_MAX_PATH]; // temporary storage
HTREEITEM newNode = node; // May be used to build pathname without
// adding any new nodes.
// Image 0 is the unselected folder image and Image 1 is the
// selected node image.
int image1 = 0, image2 = 1;
// We need a string object to build the directory path
CString dirPath;
// If a node name was passed to us then we will add the node.
// Otherwise, we need to get a directory name first and then recurse.
//
if (path)
{
// if the mode indicates that we are adding a drive node
// then we need to:
// add the drive label in parens.
// set the image to correspond to the type of drive...
// reset the mode to tmShort (actually, on fast machines this
// doesn't really speed things up).
if (type >= CChooseDirectory::dtRemovable)
{
char VolName[24];
char RootName[10];
DWORD dwCompLen, dwFlags;
sprintf (RootName, "%s\\", path);
memset (VolName, '\0', sizeof (VolName));
GetVolumeInformation (RootName, VolName, 24, NULL, &dwCompLen, &dwFlags, NULL, 0);
if (strlen (VolName))
{
wsprintf(buff, "%s (%s)", path, VolName);
}
else
wsprintf (buff, "%s", path);
if ((type == CChooseDirectory::dtCDRom)
&& !strcmp (VolName, "Audio CD"))
type = CChooseDirectory::dtRamDisk;
image1 = image2 = type;
newNode = InsertChild (node, buff, image1, image2, TVI_LAST);
mode = CChooseDirectory::tmShort;
}
// Otherwise just use the node name passed
else
{
strcpy(buff, path);
// Add the node as a child to the current node using the folder images.
// Use the sort flag to sort the tree as we go.so the list is sorted
// as we go.
newNode = InsertChild (node, buff, image1, image2, TVI_SORT);
}
}
// Build a path name based on the node.
BuildPath(dirPath, newNode);
// Add wildcards
dirPath += "*.*";
//
// Look for the first match. Return if none.
if ((hFind = FindFirstFile(dirPath, &fd)) == INVALID_HANDLE_VALUE)
{
return;
}
// add one to the level we are on
m_level++;
do
{
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
&& (!(fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)))
{
if (!strcmp (fd.cFileName, "."))
{
; // Current directory. Do nothing.
}
else if (!strcmp (fd.cFileName, ".."))
{
; // Parent directory. Do nothing.
}
else
{
// If the are building the intial tree structure (tmShort) or we are
// expanding a level (tmDetail), add the node by recursion.
if (((mode == CChooseDirectory::tmShort)
&& (m_level < 2))
|| (mode == CChooseDirectory::tmDetail))
AddNode(fd.cFileName, newNode, 0, mode); // type, 0); // mode);
// If we're building the initial structure, we want to add only one
// subnode to make the plus symbold appear.
if (mode == CChooseDirectory::tmShort)
break;
// In the detail mode we need to fill this branch completely
// but only one sub-node per node under this branch. Again,
// we have to do this so the + sign shows up next to the node.
if (mode == CChooseDirectory::tmDetail && m_level > 1)
{
break;
}
}
}
} while (::FindNextFile (hFind, &fd)); // Look for the next match
FindClose (hFind);
// decrement the level counter
m_level--;
}
//
// InsertChild() inserts a new node in the tree as a child of the node
// passed as the parent.Images default to 0 and SortType default to TVI_SORT
//
HTREEITEM CChooseDirectory::InsertChild (HTREEITEM parent, char *label, int image, int selimage, HTREEITEM SortType)
{
TV_INSERTSTRUCT TreeItem;
//
// Zero out the new structure. CTreeCtrl doesn't like stray data.
//
memset ((char *) &TreeItem, '\0', sizeof (TV_INSERTSTRUCT));
TreeItem.hParent = parent == NULL ? TVI_ROOT : parent;
TreeItem.hInsertAfter = SortType;
TreeItem.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
TreeItem.item.pszText = label;
TreeItem.item.cchTextMax = strlen (label) + 1;
TreeItem.item.lParam = 0;
TreeItem.item.iImage = image;
TreeItem.item.iSelectedImage = selimage;
return (m_DirTree.InsertItem(&TreeItem));
}
int CChooseDirectory::DoModal(LPCSTR BeginDir)
{
if (BeginDir)
m_Data.m_OldPath = (char *) BeginDir;
return CDialog::DoModal();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -