Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

631 lines
19 KiB

// BrowseDlg.cpp
// Dialog box to enable user to select a directory and/or files.
// Author: t-michkr (June 22, 2000)
#include <windows.h>
#include <commctrl.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <tchar.h>
#include <assert.h>
#include "main.h"
#include "filebrowser.h"
#include "resource.h"
// Display browse dialog box, and return dir string.
PSTR BrowseForFolder(HWND hwnd, PSTR szInitialPath, UINT uiFlags);
// Expand a tree item to include sub items.
void AddTreeSubItems(HWND hwTree, HTREEITEM hParent);
// Remove a tree item's subitems
void RemoveTreeSubItems(HWND hwTree, HTREEITEM hParent);
void CheckTreeSubItems(HWND hwTree, HTREEITEM hChild);
// Given a path, select the appropriate item in the tree.
// If path is invalid, it will expand as much as possible
// (until invalid element appears)
void SelectItemFromFullPath(HWND hwTree, PTSTR szPath);
// Get full item path. Assumes szPath is a buffer of MAX_PATH size,
// initialized with '\0'.
void GetItemPath(HWND hwTree, HTREEITEM hItem, PTSTR szPath);
// Browse dialog proc
BOOL CALLBACK BrowseDialogProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
// Browse dialog box message handlers.
BOOL HandleInitBrowse(HWND hwnd);
void HandleBrowseCommand(HWND hwnd, UINT uiCtrlID, UINT uiNotify, HWND hwChild);
void HandleBrowseNotify(HWND hwnd, void* pvArg);
// Buffer to hold returned path
static TCHAR s_szPathBuffer[MAX_PATH];
static PTSTR s_szInitialPath = 0;
static UINT s_uiFlags;
static HIMAGELIST s_himlSystem = 0;
// Create browse dialog box, and return a path string, or
// NULL if cancel was selected.
PTSTR BrowseForFolder(HWND hwnd, PTSTR szInitialPath, UINT uiFlags)
{
CoInitialize(0);
s_szInitialPath = szInitialPath;
s_uiFlags = uiFlags;
PTSTR szRet = reinterpret_cast<TCHAR*>(DialogBox(GetModuleHandle(0),
MAKEINTRESOURCE(IDD_BROWSE), hwnd, BrowseDialogProc));
CoUninitialize();
return szRet;
}
// Browse dialog box proc.
BOOL CALLBACK BrowseDialogProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch(uiMsg)
{
case WM_INITDIALOG:
return HandleInitBrowse(hwnd);
break;
case WM_COMMAND:
HandleBrowseCommand(hwnd, LOWORD(wParam), HIWORD(wParam),
reinterpret_cast<HWND>(lParam));
break;
case WM_NOTIFY:
HandleBrowseNotify(hwnd, reinterpret_cast<void*>(lParam));
break;
default:
return FALSE;
}
return TRUE;
}
// Dialog box initialization, init tree and root tree items.
BOOL HandleInitBrowse(HWND hwnd)
{
// Get the treeview control
HWND hwTree = GetDlgItem(hwnd, IDC_DIRTREE);
if(!hwTree)
return FALSE;
SHFILEINFO sfi;
TreeView_SetImageList(hwTree, reinterpret_cast<HIMAGELIST>(SHGetFileInfo(TEXT("C:\\"),
0,&sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON)),
TVSIL_NORMAL);
// Get all user drives
DWORD dwLength = GetLogicalDriveStrings(0,0);
if(dwLength == 0)
return FALSE;
TCHAR* szDrives = new TCHAR[dwLength+1];
if(!szDrives)
return FALSE;
GetLogicalDriveStrings(dwLength, szDrives);
TCHAR* szCurrDrive = szDrives;
// Go through each drive
while(*szCurrDrive)
{
// Only pay attention to fixed drives (non-network, non-CD, non-floppy)
if(((GetDriveType(szCurrDrive) == DRIVE_FIXED) && (s_uiFlags & BF_HARDDRIVES))
|| ((GetDriveType(szCurrDrive) == DRIVE_REMOVABLE) && (s_uiFlags & BF_FLOPPYDRIVES))
|| ((GetDriveType(szCurrDrive) == DRIVE_CDROM) && (s_uiFlags & BF_CDROMDRIVES))
|| ((GetDriveType(szCurrDrive) == DRIVE_REMOTE) && (s_uiFlags & BF_NETWORKDRIVES)))
{
SHGetFileInfo(szCurrDrive, 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX);
// Get rid of the terminating '\'
szCurrDrive[lstrlen(szCurrDrive)-1] = TEXT('\0');
// Insert a disk drive item into the tree root.
TVINSERTSTRUCT tvis;
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
tvis.itemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE| TVIF_TEXT;
tvis.itemex.iImage = sfi.iIcon;
tvis.itemex.iSelectedImage = sfi.iIcon;
tvis.itemex.pszText = szCurrDrive;
tvis.itemex.cchTextMax = lstrlen(szCurrDrive);
HTREEITEM hTreeItem = TreeView_InsertItem(hwTree, &tvis);
assert(hTreeItem);
// Add subitems to the item
AddTreeSubItems(hwTree, hTreeItem);
// Move to next drive
szCurrDrive += lstrlen(szCurrDrive) + 2;
}
else
// Move to next drive.
szCurrDrive += lstrlen(szCurrDrive) + 1;
}
delete szDrives;
// Select the first element.
HTREEITEM hItem = TreeView_GetChild(hwTree, TVI_ROOT);
TreeView_SelectItem(hwTree, hItem);
// Force tree to update, and restore original focus
SetFocus(hwTree);
SetFocus(GetDlgItem(hwnd, IDOK));
return TRUE;
}
// Catch notification messages, so we can control expansion/collapsing.
void HandleBrowseNotify(HWND hwnd, void* pvArg)
{
// Get tree control
HWND hwTree = GetDlgItem(hwnd, IDC_DIRTREE);
HWND hwFileList = GetDlgItem(hwnd, IDC_FILELISTCOMBO);
if(!hwTree || !hwFileList)
{
DestroyWindow(GetParent(hwnd));
return;
}
HTREEITEM hItem;
TCHAR szPath[MAX_PATH] = TEXT("\0");
// Get notification headers
NMHDR* pHdr = reinterpret_cast<NMHDR*>(pvArg);
LPNMTREEVIEW pnmTreeView = reinterpret_cast<LPNMTREEVIEW>(pvArg);
switch(pHdr->code)
{
// Expanding or collapsing, called for each child.
case TVN_ITEMEXPANDED:
// If we're expanding, get the sub items of all children
if(pnmTreeView->action & TVE_EXPAND)
{
// Switch our parent to an open folder icon.
if(TreeView_GetParent(hwTree, pnmTreeView->itemNew.hItem))
{
szPath[0] = TEXT('\0');
GetItemPath(hwTree, pnmTreeView->itemNew.hItem, szPath);
SHFILEINFO sfi;
SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX | SHGFI_OPENICON);
TVITEMEX tvitemex;
tvitemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_HANDLE;
tvitemex.hItem = pnmTreeView->itemNew.hItem;
tvitemex.iImage = sfi.iIcon;
tvitemex.iSelectedImage = sfi.iIcon;
TreeView_SetItem(hwTree, &tvitemex);
}
// Add all sub-items to this item.
AddTreeSubItems(hwTree, pnmTreeView->itemNew.hItem);
// Go through each child, and and check if expansion should be allowed
HTREEITEM hChild = TreeView_GetChild(hwTree, pnmTreeView->itemNew.hItem);
while(hChild != NULL)
{
CheckTreeSubItems(hwTree, hChild);
hChild = TreeView_GetNextSibling(hwTree, hChild);
}
}
else if(pnmTreeView->action & TVE_COLLAPSE)
{
// Switch parent to a closed icon.
if(TreeView_GetParent(hwTree, pnmTreeView->itemNew.hItem))
{
szPath[0] = TEXT('\0');
GetItemPath(hwTree, pnmTreeView->itemNew.hItem, szPath);
SHFILEINFO sfi;
SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX | SHGFI_OPENICON);
TVITEMEX tvitemex;
tvitemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_HANDLE;
tvitemex.hItem = pnmTreeView->itemNew.hItem;
tvitemex.iImage = sfi.iIcon;
tvitemex.iSelectedImage = sfi.iIcon;
TreeView_SetItem(hwTree, &tvitemex);
}
// Remove all subitems for every child.
RemoveTreeSubItems(hwTree, pnmTreeView->itemNew.hItem);
CheckTreeSubItems(hwTree, pnmTreeView->itemNew.hItem);
}
break;
case TVN_SELCHANGED:
// Only bother updating edit box if the tree has the focus
if(GetFocus() == hwTree)
{
GetItemPath(hwTree, pnmTreeView->itemNew.hItem, szPath);
SetWindowText(hwFileList, szPath);
}
break;
// When treeview gains focus, make sure file list and tree view
// selection are in sync.
case NM_SETFOCUS:
hItem = TreeView_GetSelection(hwTree);
GetItemPath(hwTree, hItem, szPath);
SetWindowText(hwFileList, szPath);
break;
}
}
// Handle a command message.
void HandleBrowseCommand(HWND hwnd, UINT uiCtrlID, UINT uiNotify, HWND hwCtrl)
{
HWND hwTree = GetDlgItem(hwnd, IDC_DIRTREE);
HTREEITEM hSelected;
TVITEMEX tvItem;
TCHAR szPath[MAX_PATH];
switch(uiCtrlID)
{
// Get path of item, and return it.
case IDOK:
// Retrieve item from tree view.
hSelected = TreeView_GetSelection(hwTree);
if(!hSelected)
{
MessageBeep(0);
break;
}
s_szPathBuffer[0] = TEXT('\0');
GetItemPath(hwTree, hSelected, s_szPathBuffer);
if(s_szPathBuffer[lstrlen(s_szPathBuffer)-1]== TEXT('\\'))
s_szPathBuffer[lstrlen(s_szPathBuffer)-1] = TEXT('\0');
// Validate the path
if(GetFileAttributes(s_szPathBuffer)==static_cast<DWORD>(-1))
Error(hwnd, IDS_INVALIDPATH);
else
EndDialog(hwnd, reinterpret_cast<INT_PTR>(s_szPathBuffer));
break;
case IDCANCEL:
// User selected cancel, just return null.
EndDialog(hwnd, 0);
break;
case IDC_FILELISTCOMBO:
switch(uiNotify)
{
case CBN_EDITCHANGE:
SendMessage(hwCtrl, WM_GETTEXT, MAX_PATH,
reinterpret_cast<LPARAM>(szPath));
SelectItemFromFullPath(hwTree, szPath);
break;
case CBN_DROPDOWN:
// clear the combo box.
SendMessage(hwCtrl, CB_RESETCONTENT, 0, 0);
// Fill the combo box with all the lowest level items under
// treeview selection
hSelected = TreeView_GetSelection(hwTree);
tvItem.mask = TVIF_STATE | TVIF_HANDLE;
tvItem.hItem = hSelected;
TreeView_GetItem(hwTree, &tvItem);
if(tvItem.state & TVIS_EXPANDED)
{
szPath[0] = TEXT('\0');
GetItemPath(hwTree, hSelected, szPath);
SendMessage(hwCtrl, CB_ADDSTRING, 0,
reinterpret_cast<LPARAM>(szPath));
HTREEITEM hItem = TreeView_GetChild(hwTree, tvItem.hItem);
while(hItem)
{
szPath[0] = TEXT('\0');
GetItemPath(hwTree, hItem, szPath);
SendMessage(hwCtrl, CB_ADDSTRING, 0,
reinterpret_cast<LPARAM>(szPath));
hItem = TreeView_GetNextSibling(hwTree, hItem);
}
}
else
{
HTREEITEM hItem;
hItem = TreeView_GetParent(hwTree, tvItem.hItem);
hItem = TreeView_GetChild(hwTree, hItem);
while(hItem)
{
szPath[0] = TEXT('\0');
GetItemPath(hwTree, hItem, szPath);
SendMessage(hwCtrl, CB_ADDSTRING, 0,
reinterpret_cast<LPARAM>(szPath));
hItem = TreeView_GetNextSibling(hwTree, hItem);
}
}
break;
}
break;
};
}
// Expand an item to get its full path.
void GetItemPath(HWND hwTree, HTREEITEM hItem, PTSTR szPath)
{
assert(hwTree);
assert(hItem);
assert(szPath);
assert(szPath[0] == TEXT('\0'));
// Recurse to get parent's path.
HTREEITEM hParent = TreeView_GetParent(hwTree, hItem);
if(hParent)
{
GetItemPath(hwTree, hParent, szPath);
lstrcat(szPath, TEXT("\\"));
}
// Get item text, concatenate on current path..
TVITEMEX tvItem;
tvItem.mask = TVIF_TEXT | TVIF_HANDLE;
tvItem.hItem = hItem;
tvItem.pszText = szPath + lstrlen(szPath);
tvItem.cchTextMax = MAX_PATH - lstrlen(szPath);
TreeView_GetItem(hwTree, &tvItem);
}
// Remove all subitems below an element.
void RemoveTreeSubItems(HWND hwTree, HTREEITEM hParent)
{
assert(hwTree);
// Go through each child and delete.
HTREEITEM hChild = TreeView_GetChild(hwTree, hParent);
while(hChild != NULL)
{
HTREEITEM hSibling = TreeView_GetNextSibling(hwTree, hChild);
// Recursively delete all subitems in this child.
RemoveTreeSubItems(hwTree, hChild);
// Remove this item.
TreeView_DeleteItem(hwTree, hChild);
// Move to next.
hChild = hSibling;
}
}
// Add items below an element.
void AddTreeSubItems(HWND hwTree, HTREEITEM hParent)
{
assert(hwTree);
// Clear-out (to ensure we don't add items twice)
RemoveTreeSubItems(hwTree, hParent);
// Do an early out if the item has already been expanded
TVITEMEX tvitem;
tvitem.mask = TVIF_CHILDREN | TVIF_HANDLE;
tvitem.hItem = hParent;
TreeView_GetItem(hwTree, &tvitem);
if(tvitem.cChildren)
return;
// Do a search on all directories
TCHAR szPath[MAX_PATH] = TEXT("");
GetItemPath(hwTree, hParent, szPath);
WIN32_FIND_DATA findData;
lstrcat(szPath, TEXT("\\*.*"));
HANDLE hSearch = FindFirstFile(szPath, &findData);
if(hSearch == INVALID_HANDLE_VALUE)
return;
do
{
// Ignore if a relative directory (. or ..)
// or if no select files were selected and it is not a directory
// otherwise
if((findData.cFileName[0] != TEXT('.')) &&
((!(s_uiFlags & BF_SELECTFILES) &&
(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) ||
(s_uiFlags & BF_SELECTFILES)))
{
SHFILEINFO sfi;
szPath[0] = TEXT('\0');
GetItemPath(hwTree, hParent, szPath);
lstrcat(szPath, TEXT("\\"));
lstrcat(szPath, findData.cFileName);
SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX);
// Insert an item representing this directory.
TVINSERTSTRUCT tvis;
tvis.hParent = hParent;
tvis.hInsertAfter = TVI_SORT;
tvis.itemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
tvis.itemex.iImage = sfi.iIcon;
tvis.itemex.iSelectedImage = sfi.iIcon;
tvis.itemex.pszText = findData.cFileName;
tvis.itemex.cchTextMax = lstrlen(findData.cFileName);
TreeView_InsertItem(hwTree, &tvis);
}
// Move to next file.
} while(FindNextFile(hSearch, &findData));
FindClose(hSearch);
}
void CheckTreeSubItems(HWND hwTree, HTREEITEM hParent)
{
assert(hwTree);
// Do a search on all directories
TCHAR szPath[MAX_PATH] = TEXT("");
GetItemPath(hwTree, hParent, szPath);
WIN32_FIND_DATA findData;
lstrcat(szPath, TEXT("\\*.*"));
HANDLE hSearch = FindFirstFile(szPath, &findData);
if(hSearch == INVALID_HANDLE_VALUE)
return;
do
{
// Ignore if a relative directory (. or ..)
// or if no select files were selected and it is not a directory
// otherwise
if((findData.cFileName[0] != TEXT('.')) &&
((!(s_uiFlags & BF_SELECTFILES) &&
(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) ||
(s_uiFlags & BF_SELECTFILES)))
{
SHFILEINFO sfi;
szPath[0] = TEXT('\0');
GetItemPath(hwTree, hParent, szPath);
lstrcat(szPath, TEXT("\\"));
lstrcat(szPath, findData.cFileName);
SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX);
// Insert an item representing this directory.
TVINSERTSTRUCT tvis;
tvis.hParent = hParent;
tvis.hInsertAfter = TVI_SORT;
tvis.itemex.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
tvis.itemex.iImage = sfi.iIcon;
tvis.itemex.iSelectedImage = sfi.iIcon;
tvis.itemex.pszText = findData.cFileName;
tvis.itemex.cchTextMax = lstrlen(findData.cFileName);
TreeView_InsertItem(hwTree, &tvis);
FindClose(hSearch);
return;
}
// Move to next file.
} while(FindNextFile(hSearch, &findData));
FindClose(hSearch);
}
// Given a relative path and a tree item, select a subitem from the relative path.
// Returns true if item successfully selected, false otherwise.
bool SelectSubitemFromPartialPath(HWND hwTree, HTREEITEM hItem, PTSTR szPath)
{
bool fExpandIt = false;
TCHAR* szPathDelim = _tcschr(szPath, TEXT('\\'));
if(szPathDelim)
{
if(szPathDelim == szPath)
return false;
*szPathDelim = TEXT('\0');
if(szPathDelim[1] == TEXT('\0'))
{
szPathDelim = 0;
fExpandIt = true;
}
}
// Find this path.
HTREEITEM hClosestChild = 0;
HTREEITEM hChild = TreeView_GetChild(hwTree, hItem);
while(hChild)
{
TCHAR szItemPath[MAX_PATH];
TVITEMEX tvitem;
tvitem.mask = TVIF_HANDLE | TVIF_TEXT;
tvitem.hItem = hChild;
tvitem.pszText = szItemPath;
tvitem.cchTextMax = MAX_PATH;
TreeView_GetItem(hwTree, &tvitem);
if(lstrcmpi(szPath,tvitem.pszText) == 0)
break;
else if((StrStrI(tvitem.pszText, szPath) == tvitem.pszText) && !fExpandIt)
{
hClosestChild = hChild;
break;
}
hChild = TreeView_GetNextSibling(hwTree, hChild);
}
if(!hChild)
{
if(!hClosestChild)
return false;
else
{
hChild = hClosestChild;
szPathDelim = 0;
}
}
// If nothing more on the path, select this item,
// or expand and continue
if(szPathDelim == 0)
{
if(fExpandIt)
TreeView_Expand(hwTree, hChild, TVE_EXPAND);
TreeView_SelectItem(hwTree, hChild);
}
else
{
if(fExpandIt)
TreeView_Expand(hwTree, hChild, TVE_EXPAND);
if(!SelectSubitemFromPartialPath(hwTree, hChild, szPathDelim+1))
return false;
}
return true;
}
// Given a path, select the appropriate item in the tree.
// If path is invalid, it will expand as much as possible
// (until invalid element appears)
// szPath is trashed.
void SelectItemFromFullPath(HWND hwTree, PTSTR szPath)
{
if(!SelectSubitemFromPartialPath(hwTree, 0, szPath))
TreeView_SelectItem(hwTree, 0);
}