Mercurial > dwindows
view win/XBrowseForFolder.cpp @ 632:bf3a6d596cd4
Use better directory browser widget.
author | mhessling@81767d24-ef19-dc11-ae90-00e081727c95 |
---|---|
date | Thu, 30 Oct 2008 10:46:03 +0000 |
parents | |
children | 412af8059331 |
line wrap: on
line source
// XBrowseForFolder.cpp Version 1.2 // // Author: Hans Dietrich // hdietrich@gmail.com // // Description: // XBrowseForFolder.cpp implements XBrowseForFolder(), a function that // wraps SHBrowseForFolder(). // // History // Version 1.2 - 2008 February 29 // - Changed API to allow for initial CSIDL. // - Added option to set dialog caption, suggested by SimpleDivX. // - Added option to set root, suggested by Jean-Michel Reghem. // // Version 1.1 - 2003 September 29 (not released) // - Added support for edit box // // Version 1.0 - 2003 September 25 // - Initial public release // // License: // This software is released into the public domain. You are free to use // it in any way you like, except that you may not sell this source code. // // This software is provided "as is" with no expressed or implied warranty. // I accept no liability for any damage or loss of business that this // software may cause. // /////////////////////////////////////////////////////////////////////////////// // if you don't want to use MFC, comment out the following line: //#include "stdafx.h" #ifndef __AFX_H__ #include "windows.h" #include "crtdbg.h" #include "tchar.h" #endif #include "Shlobj.h" #include "io.h" #include "XBrowseForFolder.h" #pragma warning(disable: 4127) // conditional expression is constant (_ASSERTE) #pragma warning(disable : 4996) // disable bogus deprecation warning #ifndef __noop #if _MSC_VER < 1300 #define __noop ((void)0) #endif #endif #undef TRACE #define TRACE __noop //============================================================================= // if you want to see the TRACE output, uncomment this line: //#include "XTrace.h" //============================================================================= //============================================================================= // struct to pass to callback function //============================================================================= struct FOLDER_PROPS { LPCTSTR lpszTitle; LPCTSTR lpszInitialFolder; UINT ulFlags; }; #ifndef __AFX_H__ /////////////////////////////////////////////////////////////////////////////// // CRect - a minimal CRect class class CRect : public tagRECT { public: //CRect() { } CRect(int l = 0, int t = 0, int r = 0, int b = 0) { left = l; top = t; right = r; bottom = b; } int Width() const { return right - left; } int Height() const { return bottom - top; } void SwapLeftRight() { SwapLeftRight(LPRECT(this)); } static void SwapLeftRight(LPRECT lpRect) { LONG temp = lpRect->left; lpRect->left = lpRect->right; lpRect->right = temp; } operator LPRECT() { return this; } }; #endif /////////////////////////////////////////////////////////////////////////////// // ScreenToClientX - helper function in case non-MFC static void ScreenToClientX(HWND hWnd, LPRECT lpRect) { _ASSERTE(::IsWindow(hWnd)); ::ScreenToClient(hWnd, (LPPOINT)lpRect); ::ScreenToClient(hWnd, ((LPPOINT)lpRect)+1); } /////////////////////////////////////////////////////////////////////////////// // MoveWindowX - helper function in case non-MFC static void MoveWindowX(HWND hWnd, CRect& rect, BOOL bRepaint) { _ASSERTE(::IsWindow(hWnd)); ::MoveWindow(hWnd, rect.left, rect.top, rect.Width(), rect.Height(), bRepaint); } /////////////////////////////////////////////////////////////////////////////// // SizeBrowseDialog - resize dialog, move controls static void SizeBrowseDialog(HWND hWnd, FOLDER_PROPS *fp) { TRACE(_T("in void SizeBrowseDialog\n")); // find the folder tree and make dialog larger HWND hwndTree = FindWindowEx(hWnd, NULL, _T("SysTreeView32"), NULL); if (!hwndTree) { // ... this usually means that BIF_NEWDIALOGSTYLE is enabled. // Then the class name is as used in the code below. hwndTree = FindWindowEx(hWnd, NULL, _T("SHBrowseForFolder ShellNameSpace Control"), NULL); TRACE(_T("SHBrowseForFolder ShellNameSpace Control: hwndTree=%X\n"), hwndTree); } CRect rectDlg; _ASSERTE(IsWindow(hwndTree)); if (hwndTree) { // check if edit box int nEditHeight = 0; HWND hwndEdit = FindWindowEx(hWnd, NULL, _T("Edit"), NULL); TRACE(_T("hwndEdit=%x\n"), hwndEdit); CRect rectEdit; if (hwndEdit && (fp->ulFlags & BIF_EDITBOX)) { ::GetWindowRect(hwndEdit, &rectEdit); ScreenToClientX(hWnd, &rectEdit); nEditHeight = rectEdit.Height(); } else if (hwndEdit) { ::MoveWindow(hwndEdit, 20000, 20000, 10, 10, FALSE); ::ShowWindow(hwndEdit, SW_HIDE); hwndEdit = 0; } // make the dialog larger ::GetWindowRect(hWnd, &rectDlg); rectDlg.right += 40; rectDlg.bottom += 30; if (hwndEdit) rectDlg.bottom += nEditHeight + 5; MoveWindowX(hWnd, rectDlg, TRUE); ::GetClientRect(hWnd, &rectDlg); int hMargin = 10; int vMargin = 10; // check if new dialog style - this means that there will be a resizing // grabber in lower right corner if (fp->ulFlags & BIF_NEWDIALOGSTYLE) hMargin = ::GetSystemMetrics(SM_CXVSCROLL); // move the Cancel button CRect rectCancel; HWND hwndCancel = ::GetDlgItem(hWnd, IDCANCEL); if (hwndCancel) ::GetWindowRect(hwndCancel, &rectCancel); ScreenToClientX(hWnd, &rectCancel); int h = rectCancel.Height(); int w = rectCancel.Width(); rectCancel.bottom = rectDlg.bottom - vMargin;//nMargin; rectCancel.top = rectCancel.bottom - h; rectCancel.right = rectDlg.right - hMargin; //(scrollWidth + 2*borderWidth); rectCancel.left = rectCancel.right - w; if (hwndCancel) { //TRACERECT(rectCancel); MoveWindowX(hwndCancel, rectCancel, FALSE); } // move the OK button CRect rectOK(0, 0, 0, 0); HWND hwndOK = ::GetDlgItem(hWnd, IDOK); if (hwndOK) ::GetWindowRect(hwndOK, &rectOK); ScreenToClientX(hWnd, &rectOK); rectOK.bottom = rectCancel.bottom; rectOK.top = rectCancel.top; rectOK.right = rectCancel.left - 10; rectOK.left = rectOK.right - w; if (hwndOK) { MoveWindowX(hwndOK, rectOK, FALSE); } // expand the folder tree to fill the dialog CRect rectTree; ::GetWindowRect(hwndTree, &rectTree); ScreenToClientX(hWnd, &rectTree); if (hwndEdit) { rectEdit.left = hMargin; rectEdit.right = rectDlg.right - hMargin; rectEdit.top = vMargin; rectEdit.bottom = rectEdit.top + nEditHeight; MoveWindowX(hwndEdit, rectEdit, FALSE); rectTree.top = rectEdit.bottom + 5; } else { rectTree.top = vMargin; } rectTree.left = hMargin; rectTree.bottom = rectOK.top - 10;//nMargin; rectTree.right = rectDlg.right - hMargin; //TRACERECT(rectTree); MoveWindowX(hwndTree, rectTree, FALSE); } else { TRACE(_T("ERROR - tree control not found.\n")); //_ASSERTE(hwndTree); } } /////////////////////////////////////////////////////////////////////////////// // BrowseCallbackProc - SHBrowseForFolder callback function static int CALLBACK BrowseCallbackProc(HWND hWnd, // Window handle to the browse dialog box UINT uMsg, // Value identifying the event LPARAM lParam, // Value dependent upon the message LPARAM lpData) // Application-defined value that was // specified in the lParam member of the // BROWSEINFO structure { switch (uMsg) { case BFFM_INITIALIZED: // sent when the browse dialog box has finished initializing. { TRACE(_T("hWnd=%X\n"), hWnd); // remove context help button from dialog caption LONG lStyle = ::GetWindowLong(hWnd, GWL_STYLE); lStyle &= ~DS_CONTEXTHELP; ::SetWindowLong(hWnd, GWL_STYLE, lStyle); lStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE); lStyle &= ~WS_EX_CONTEXTHELP; ::SetWindowLong(hWnd, GWL_EXSTYLE, lStyle); FOLDER_PROPS *fp = (FOLDER_PROPS *) lpData; if (fp) { if (fp->lpszInitialFolder && (fp->lpszInitialFolder[0] != _T('\0'))) { // set initial directory ::SendMessage(hWnd, BFFM_SETSELECTION, TRUE, (LPARAM)fp->lpszInitialFolder); } if (fp->lpszTitle && (fp->lpszTitle[0] != _T('\0'))) { // set window caption ::SetWindowText(hWnd, fp->lpszTitle); } } SizeBrowseDialog(hWnd, fp); } break; case BFFM_SELCHANGED: // sent when the selection has changed { TCHAR szDir[MAX_PATH*2] = { 0 }; // fail if non-filesystem BOOL bRet = SHGetPathFromIDList((LPITEMIDLIST) lParam, szDir); if (bRet) { // fail if folder not accessible if (_taccess(szDir, 00) != 0) { bRet = FALSE; } else { SHFILEINFO sfi; ::SHGetFileInfo((LPCTSTR)lParam, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_ATTRIBUTES); TRACE(_T("dwAttributes=0x%08X\n"), sfi.dwAttributes); // fail if pidl is a link if (sfi.dwAttributes & SFGAO_LINK) { TRACE(_T("SFGAO_LINK\n")); bRet = FALSE; } } } // if invalid selection, disable the OK button if (!bRet) { ::EnableWindow(GetDlgItem(hWnd, IDOK), FALSE); } TRACE(_T("szDir=%s\n"), szDir); } break; } return 0; } /////////////////////////////////////////////////////////////////////////////// // // XBrowseForFolder() // // Purpose: Invoke the SHBrowseForFolder API. If lpszInitialFolder is // supplied, it will be the folder initially selected in the tree // folder list. Otherwise, the initial folder will be set to the // current directory. The selected folder will be returned in // lpszBuf. // // Parameters: hWnd - handle to the owner window for the dialog // lpszInitialFolder - initial folder in tree; if NULL, the initial // folder will be the current directory; if // if this is a CSIDL, must be a real folder. // nRootFolder - optional CSIDL of root folder for tree; // -1 = use default. // lpszCaption - optional caption for folder dialog // lpszBuf - buffer for the returned folder path // dwBufSize - size of lpszBuf in TCHARs // bEditBox - TRUE = include edit box in dialog // // Returns: BOOL - TRUE = success; FALSE = user hit Cancel // BOOL _cdecl XBrowseForFolder(HWND hWnd, LPCTSTR lpszInitialFolder, int nRootFolder, LPCTSTR lpszCaption, LPTSTR lpszBuf, DWORD dwBufSize, BOOL bEditBox /*= FALSE*/) { _ASSERTE(lpszBuf); _ASSERTE(dwBufSize >= MAX_PATH); if (lpszBuf == NULL || dwBufSize < MAX_PATH) return FALSE; ZeroMemory(lpszBuf, dwBufSize*sizeof(TCHAR)); BROWSEINFO bi = { 0 }; // check if there is a special root folder LPITEMIDLIST pidlRoot = NULL; if (nRootFolder != -1) { if (SUCCEEDED(SHGetSpecialFolderLocation(hWnd, nRootFolder, &pidlRoot))) bi.pidlRoot = pidlRoot; } TCHAR szInitialPath[MAX_PATH*2] = { _T('\0') }; if (lpszInitialFolder) { // is this a folder path string or a csidl? if (HIWORD(lpszInitialFolder) == 0) { // csidl int nFolder = LOWORD((UINT)(UINT_PTR)lpszInitialFolder); TRACE(_T("csidl: nFolder=0x%X\n"), nFolder); SHGetSpecialFolderPath(hWnd, szInitialPath, nFolder, FALSE); } else { // string _tcsncpy(szInitialPath, lpszInitialFolder, sizeof(szInitialPath)/sizeof(TCHAR)-2); } TRACE(_T("szInitialPath=<%s>\n"), szInitialPath); } if ((szInitialPath[0] == _T('\0')) && (bi.pidlRoot == NULL)) { // no initial folder and no root, set to current directory ::GetCurrentDirectory(sizeof(szInitialPath)/sizeof(TCHAR)-2, szInitialPath); } FOLDER_PROPS fp; bi.hwndOwner = hWnd; bi.ulFlags = BIF_RETURNONLYFSDIRS; // do NOT use BIF_NEWDIALOGSTYLE, // or BIF_STATUSTEXT if (bEditBox) bi.ulFlags |= BIF_EDITBOX; bi.ulFlags |= BIF_NONEWFOLDERBUTTON; bi.lpfn = BrowseCallbackProc; bi.lParam = (LPARAM) &fp; fp.lpszInitialFolder = szInitialPath; fp.lpszTitle = lpszCaption; fp.ulFlags = bi.ulFlags; BOOL bRet = FALSE; LPITEMIDLIST pidlFolder = SHBrowseForFolder(&bi); if (pidlFolder) { TCHAR szBuffer[MAX_PATH*2] = { _T('\0') }; if (SHGetPathFromIDList(pidlFolder, szBuffer)) { _tcsncpy(lpszBuf, szBuffer, dwBufSize-1); bRet = TRUE; } else { TRACE(_T("SHGetPathFromIDList failed\n")); } } // free up pidls IMalloc *pMalloc = NULL; if (SUCCEEDED(SHGetMalloc(&pMalloc)) && pMalloc) { if (pidlFolder) pMalloc->Free(pidlFolder); if (pidlRoot) pMalloc->Free(pidlRoot); pMalloc->Release(); } return bRet; }