/*++ Copyright (c) 1990-1998, Microsoft Corporation All rights reserved. Module Name: filenew.cpp Abstract: This module implements the Win32 explorer fileopen dialogs. --*/ // // Include Files. // // precompiled headers #include "precomp.h" #pragma hdrstop #include "cdids.h" #include "fileopen.h" #include "d32tlog.h" #include "filenew.h" #include "filemru.h" #include "util.h" #include "uxtheme.h" #ifndef ASSERT #define ASSERT Assert #endif // // Constant Declarations. // #define IDOI_SHARE 1 #define CDM_SETSAVEBUTTON (CDM_LAST + 100) #define CDM_FSNOTIFY (CDM_LAST + 101) #define CDM_SELCHANGE (CDM_LAST + 102) #define TIMER_FSCHANGE 100 #define NODE_DESKTOP 0 #define NODE_DRIVES 1 #define DEREFMACRO(x) x #define FILE_PADDING 10 #define MAX_URL_STRING INTERNET_MAX_URL_LENGTH #define MAXDOSFILENAMELEN (12 + 1) // 8.3 filename + 1 for NULL // // IShellView::MenuHelp flags. // #define MH_DONE 0x0001 // MH_LONGHELP #define MH_MERGEITEM 0x0004 #define MH_SYSITEM 0x0008 #define MH_POPUP 0x0010 #define MH_TOOLBAR 0x0020 #define MH_TOOLTIP 0x0040 // // IShellView::MenuHelp return values. // #define MH_NOTHANDLED 0 #define MH_STRINGFILLED 1 #define MH_ALLHANDLED 2 #define MYCBN_DRAW 0x8000 #define MAX_DRIVELIST_STRING_LEN (64 + 4) #define REGSTR_PATH_PLACESBAR TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\comdlg32\\Placesbar") #define MAXPLACESBARITEMS 5 // // Macro Definitions. // #define IsServer(psz) (IsUNC(psz) && !StrChr((psz) + 2, CHAR_BSLASH)) #define LPIDL_GetIDList(_pida,n) \ (LPCITEMIDLIST)(((LPBYTE)(_pida)) + (_pida)->aoffset[n]) #define RECTWIDTH(_rc) ((_rc).right - (_rc).left) #define RECTHEIGHT(_rc) ((_rc).bottom - (_rc).top) #define IsVisible(_hwnd) (GetWindowLong(_hwnd, GWL_STYLE) & WS_VISIBLE) #define HwndToBrowser(_hwnd) (CFileOpenBrowser *)GetWindowLongPtr(_hwnd, DWLP_USER) #define StoreBrowser(_hwnd, _pbrs) \ SetWindowLongPtr(_hwnd, DWLP_USER, (LONG_PTR)_pbrs); // // Typedef Declarations. // typedef struct _OFNINITINFO { LPOPENFILEINFO lpOFI; BOOL bSave; BOOL bEnableSizing; HRESULT hrOleInit; } OFNINITINFO, *LPOFNINITINFO; #define VC_NEWFOLDER 0 #define VC_VIEWLIST 1 #define VC_VIEWDETAILS 2 // // Global Variables. // HWND gp_hwndActiveOpen = NULL; HACCEL gp_haccOpen = NULL; HACCEL gp_haccOpenView = NULL; HHOOK gp_hHook = NULL; int gp_nHookRef = -1; UINT gp_uQueryCancelAutoPlay = 0; static int g_cxSmIcon = 0 ; static int g_cySmIcon = 0 ; static int g_cxGrip; static int g_cyGrip; const LPCSTR c_szCommandsA[] = { CMDSTR_NEWFOLDERA, CMDSTR_VIEWLISTA, CMDSTR_VIEWDETAILSA, }; const LPCWSTR c_szCommandsW[] = { CMDSTR_NEWFOLDERW, CMDSTR_VIEWLISTW, CMDSTR_VIEWDETAILSW, }; extern "C" { extern RECT g_rcDlg; } // // Function Prototypes. // LRESULT CALLBACK OKSubclass( HWND hOK, UINT msg, WPARAM wParam, LPARAM lParam); void GetControlsArea( HWND hDlg, HWND hwndExclude, HWND hwndGrip, POINT *pPtSize, LPINT pTop); BOOL_PTR CALLBACK OpenDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); // // Context Help IDs. // DWORD aFileOpenHelpIDs[] = { stc2, IDH_OPEN_FILETYPE, // The positions of these array elements cmb1, IDH_OPEN_FILETYPE, // shouldn't be changed without updating stc4, IDH_OPEN_LOCATION, // InitSaveAsControls(). cmb2, IDH_OPEN_LOCATION, stc1, IDH_OPEN_FILES32, lst2, IDH_OPEN_FILES32, // defview stc3, IDH_OPEN_FILENAME, edt1, IDH_OPEN_FILENAME, cmb13, IDH_OPEN_FILENAME, chx1, IDH_OPEN_READONLY, IDOK, IDH_OPEN_BUTTON, ctl1, IDH_OPEN_SHORTCUT_BAR, 0, 0 }; DWORD aFileSaveHelpIDs[] = { stc2, IDH_SAVE_FILETYPE, // The positions of these array elements cmb1, IDH_SAVE_FILETYPE, // shouldn't be changed without updating stc4, IDH_OPEN_LOCATION, // InitSaveAsControls(). cmb2, IDH_OPEN_LOCATION, stc1, IDH_OPEN_FILES32, lst2, IDH_OPEN_FILES32, // defview stc3, IDH_OPEN_FILENAME, edt1, IDH_OPEN_FILENAME, cmb13, IDH_OPEN_FILENAME, chx1, IDH_OPEN_READONLY, IDOK, IDH_SAVE_BUTTON, ctl1, IDH_OPEN_SHORTCUT_BAR, 0, 0 }; //////////////////////////////////////////////////////////////////////////// // // CD_SendShareMsg // //////////////////////////////////////////////////////////////////////////// WORD CD_SendShareMsg( HWND hwnd, LPTSTR szFile, UINT ApiType) { if (ApiType == COMDLG_ANSI) { CHAR szFileA[MAX_PATH + 1]; SHUnicodeToAnsi(szFile,szFileA,SIZECHARS(szFileA)); return ((WORD)SendMessage(hwnd, msgSHAREVIOLATIONA, 0, (LONG_PTR)(LPSTR)(szFileA))); } else { return ((WORD)SendMessage(hwnd, msgSHAREVIOLATIONW, 0, (LONG_PTR)(LPTSTR)(szFile))); } } //////////////////////////////////////////////////////////////////////////// // // CD_SendHelpMsg // //////////////////////////////////////////////////////////////////////////// VOID CD_SendHelpMsg( LPOPENFILENAME pOFN, HWND hwndDlg, UINT ApiType) { if (ApiType == COMDLG_ANSI) { if (msgHELPA && pOFN->hwndOwner) { SendMessage(pOFN->hwndOwner, msgHELPA, (WPARAM)hwndDlg, (LPARAM)pOFN); } } else { if (msgHELPW && pOFN->hwndOwner) { SendMessage(pOFN->hwndOwner, msgHELPW, (WPARAM)hwndDlg, (LPARAM)pOFN); } } } //////////////////////////////////////////////////////////////////////////// // // CD_SendOKMsg // //////////////////////////////////////////////////////////////////////////// LRESULT CD_SendOKMsg( HWND hwnd, LPOPENFILENAME pOFN, LPOPENFILEINFO pOFI) { LRESULT Result; if (pOFI->ApiType == COMDLG_ANSI) { ThunkOpenFileNameW2A(pOFI); Result = SendMessage(hwnd, msgFILEOKA, 0, (LPARAM)(pOFI->pOFNA)); // // For apps that side-effect pOFNA stuff and expect it to // be preserved through dialog exit, update internal // struct after the hook proc is called. // ThunkOpenFileNameA2W(pOFI); } else { Result = SendMessage(hwnd, msgFILEOKW, 0, (LPARAM)(pOFN)); } return (Result); } //////////////////////////////////////////////////////////////////////////// // // CD_SendLBChangeMsg // //////////////////////////////////////////////////////////////////////////// LRESULT CD_SendLBChangeMsg( HWND hwnd, int Id, short Index, short Code, UINT ApiType) { if (ApiType == COMDLG_ANSI) { return (SendMessage(hwnd, msgLBCHANGEA, Id, MAKELONG(Index, Code))); } else { return (SendMessage(hwnd, msgLBCHANGEW, Id, MAKELONG(Index, Code))); } } //////////////////////////////////////////////////////////////////////////// // // Macro calls to SendOFNotify // //////////////////////////////////////////////////////////////////////////// #define CD_SendShareNotify(_hwndTo, _hwndFrom, _szFile, _pofn, _pofi) \ (WORD)SendOFNotify(_hwndTo, _hwndFrom, CDN_SHAREVIOLATION, _szFile, _pofn, _pofi) #define CD_SendHelpNotify(_hwndTo, _hwndFrom, _pofn, _pofi) \ SendOFNotify(_hwndTo, _hwndFrom, CDN_HELP, NULL, _pofn, _pofi) #define CD_SendOKNotify(_hwndTo, _hwndFrom, _pofn, _pofi) \ SendOFNotify(_hwndTo, _hwndFrom, CDN_FILEOK, NULL, _pofn, _pofi) #define CD_SendTypeChangeNotify(_hwndTo, _hwndFrom, _pofn, _pofi) \ SendOFNotify(_hwndTo, _hwndFrom, CDN_TYPECHANGE, NULL, _pofn, _pofi) #define CD_SendInitDoneNotify(_hwndTo, _hwndFrom, _pofn, _pofi) \ SendOFNotify(_hwndTo, _hwndFrom, CDN_INITDONE, NULL, _pofn, _pofi) #define CD_SendSelChangeNotify(_hwndTo, _hwndFrom, _pofn, _pofi) \ SendOFNotify(_hwndTo, _hwndFrom, CDN_SELCHANGE, NULL, _pofn, _pofi) #define CD_SendFolderChangeNotify(_hwndTo, _hwndFrom, _pofn, _pofi) \ SendOFNotify(_hwndTo, _hwndFrom, CDN_FOLDERCHANGE, NULL, _pofn, _pofi) #define CD_SendIncludeItemNotify(_hwndTo, _hwndFrom, _psf, _pidl, _pofn, _pofi) \ SendOFNotifyEx(_hwndTo, _hwndFrom, CDN_INCLUDEITEM, (void *)_psf, (void *)_pidl, _pofn, _pofi) //////////////////////////////////////////////////////////////////////////// // // SendOFNotifyEx // //////////////////////////////////////////////////////////////////////////// LRESULT SendOFNotifyEx( HWND hwndTo, HWND hwndFrom, UINT code, void * psf, void * pidl, LPOPENFILENAME pOFN, LPOPENFILEINFO pOFI) { OFNOTIFYEX ofnex; if (pOFI->ApiType == COMDLG_ANSI) { OFNOTIFYEXA ofnexA; LRESULT Result; ofnexA.psf = psf; ofnexA.pidl = pidl; // // Convert the OFN from Unicode to Ansi. // ThunkOpenFileNameW2A(pOFI); ofnexA.lpOFN = pOFI->pOFNA; #ifdef NEED_WOWGETNOTIFYSIZE_HELPER ASSERT(WOWGetNotifySize(code) == sizeof(OFNOTIFYEXA)); #endif Result = SendNotify(hwndTo, hwndFrom, code, &ofnexA.hdr); // // For apps that side-effect pOFNA stuff and expect it to // be preserved through dialog exit, update internal // struct after the hook proc is called. // ThunkOpenFileNameA2W(pOFI); return (Result); } else { ofnex.psf = psf; ofnex.pidl = pidl; ofnex.lpOFN = pOFN; #ifdef NEED_WOWGETNOTIFYSIZE_HELPER ASSERT(WOWGetNotifySize(code) == sizeof(OFNOTIFYEXW)); #endif return (SendNotify(hwndTo, hwndFrom, code, &ofnex.hdr)); } } //////////////////////////////////////////////////////////////////////////// // // SendOFNotify // //////////////////////////////////////////////////////////////////////////// LRESULT SendOFNotify( HWND hwndTo, HWND hwndFrom, UINT code, LPTSTR szFile, LPOPENFILENAME pOFN, LPOPENFILEINFO pOFI) { OFNOTIFY ofn; if (pOFI->ApiType == COMDLG_ANSI) { OFNOTIFYA ofnA; LRESULT Result; // // Convert the file name from Unicode to Ansi. // if (szFile) { CHAR szFileA[MAX_PATH + 1]; SHUnicodeToAnsi(szFile,szFileA,SIZECHARS(szFileA)); ofnA.pszFile = szFileA; } else { ofnA.pszFile = NULL; } // // Convert the OFN from Unicode to Ansi. // ThunkOpenFileNameW2A(pOFI); ofnA.lpOFN = pOFI->pOFNA; #ifdef NEED_WOWGETNOTIFYSIZE_HELPER ASSERT(WOWGetNotifySize(code) == sizeof(OFNOTIFYA)); #endif Result = SendNotify(hwndTo, hwndFrom, code, &ofnA.hdr); // // For apps that side-effect pOFNA stuff and expect it to // be preserved through dialog exit, update internal // struct after the hook proc is called. // ThunkOpenFileNameA2W(pOFI); return (Result); } else { ofn.pszFile = szFile; ofn.lpOFN = pOFN; #ifdef NEED_WOWGETNOTIFYSIZE_HELPER ASSERT(WOWGetNotifySize(code) == sizeof(OFNOTIFY)); #endif return (SendNotify(hwndTo, hwndFrom, code, &ofn.hdr)); } } //////////////////////////////////////////////////////////////////////////// // // TEMPMEM::Resize // //////////////////////////////////////////////////////////////////////////// BOOL TEMPMEM::Resize( UINT cb) { UINT uOldSize = m_uSize; m_uSize = cb; if (!cb) { if (m_pMem) { LocalFree(m_pMem); m_pMem = NULL; } return TRUE; } if (!m_pMem) { m_pMem = LocalAlloc(LPTR, cb); return (m_pMem != NULL); } void * pTemp = LocalReAlloc(m_pMem, cb, LHND); if (pTemp) { m_pMem = pTemp; return TRUE; } m_uSize = uOldSize; return FALSE; } //////////////////////////////////////////////////////////////////////////// // // TEMPSTR::TSStrCpy // //////////////////////////////////////////////////////////////////////////// BOOL TEMPSTR::TSStrCpy( LPCTSTR pszText) { if (!pszText) { TSStrSize(0); return TRUE; } UINT uNewSize = lstrlen(pszText) + 1; if (!TSStrSize(uNewSize)) { return FALSE; } EVAL(SUCCEEDED(StringCchCopy(*this, uNewSize, pszText))); return TRUE; } //////////////////////////////////////////////////////////////////////////// // // TEMPSTR::TSStrCat // //////////////////////////////////////////////////////////////////////////// BOOL TEMPSTR::TSStrCat( LPCTSTR pszText) { if (!(LPTSTR)*this) { // // This should 0 init. // if (!TSStrSize(MAX_PATH)) { return FALSE; } } UINT uNewSize = lstrlen(*this) + lstrlen(pszText) + 1; if (m_uSize < uNewSize * sizeof(TCHAR)) { // // Add on some more so we do not ReAlloc too often. // uNewSize += MAX_PATH; if (!TSStrSize(uNewSize)) { return FALSE; } } EVAL(SUCCEEDED(StringCchCat(*this, uNewSize, pszText))); return TRUE; } //////////////////////////////////////////////////////////////////////////// // // IsVolumeLFN // //////////////////////////////////////////////////////////////////////////// BOOL IsVolumeLFN(LPCTSTR pszRoot) { DWORD dwVolumeSerialNumber; DWORD dwMaximumComponentLength; DWORD dwFileSystemFlags; // // We need to find out what kind of a drive we are running // on in order to determine if spaces are valid in a filename // or not. // if (GetVolumeInformation(pszRoot, NULL, 0, &dwVolumeSerialNumber, &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0)) { if (dwMaximumComponentLength != (MAXDOSFILENAMELEN - 1)) return TRUE; } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CDMessageBox // //////////////////////////////////////////////////////////////////////////// int _cdecl CDMessageBox( HWND hwndParent, UINT idText, UINT uFlags, ...) { TCHAR szText[MAX_PATH + WARNINGMSGLENGTH]; TCHAR szTitle[WARNINGMSGLENGTH]; va_list ArgList; CDLoadString(g_hinst, idText, szTitle, ARRAYSIZE(szTitle)); va_start(ArgList, uFlags); StringCchVPrintf(szText, ARRAYSIZE(szText), szTitle, ArgList); // for display, ignoring return value va_end(ArgList); GetWindowText(hwndParent, szTitle, ARRAYSIZE(szTitle)); return (MessageBox(hwndParent, szText, szTitle, uFlags)); } int OFErrFromHresult(HRESULT hr) { switch (hr) { case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): return OF_FILENOTFOUND; case E_ACCESSDENIED: return OF_ACCESSDENIED; default: return -1; } } BOOL CFileOpenBrowser::_SaveAccessDenied(LPCTSTR pszFile) { if (CDMessageBox(_hwndDlg, iszDirSaveAccessDenied, MB_YESNO | MB_ICONEXCLAMATION, pszFile) == IDYES) { LPITEMIDLIST pidl; if (SUCCEEDED(SHGetFolderLocation(_hwndDlg, CSIDL_PERSONAL, NULL, 0, &pidl))) { JumpToIDList(pidl); ILFree(pidl); } } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // InvalidFileWarningNew // //////////////////////////////////////////////////////////////////////////// VOID InvalidFileWarningNew( HWND hWnd, LPCTSTR pszFile, int wErrCode) { int isz; BOOL bDriveLetter = FALSE; switch (wErrCode) { case (OF_ACCESSDENIED) : { isz = iszFileAccessDenied; break; } case (ERROR_NOT_READY) : { isz = iszNoDiskInDrive; bDriveLetter = TRUE; break; } case (OF_NODRIVE) : { isz = iszDriveDoesNotExist; bDriveLetter = TRUE; break; } case (OF_NOFILEHANDLES) : { isz = iszNoFileHandles; break; } case (OF_PATHNOTFOUND) : { isz = iszPathNotFound; break; } case (OF_FILENOTFOUND) : { isz = iszFileNotFound; break; } case (OF_DISKFULL) : case (OF_DISKFULL2) : { isz = iszDiskFull; bDriveLetter = TRUE; break; } case (OF_WRITEPROTECTION) : { isz = iszWriteProtection; bDriveLetter = TRUE; break; } case (OF_SHARINGVIOLATION) : { isz = iszSharingViolation; break; } case (OF_CREATENOMODIFY) : { isz = iszCreateNoModify; break; } case (OF_NETACCESSDENIED) : { isz = iszNetworkAccessDenied; break; } case (OF_PORTNAME) : { isz = iszPortName; break; } case (OF_LAZYREADONLY) : { isz = iszReadOnly; break; } case (OF_INT24FAILURE) : { isz = iszInt24Error; break; } case (OF_BUFFERTRUNCATED) : // Due to limitations of the fileopen dialog - however, this means it was over MAX_PATH default : { isz = iszInvalidFileName; break; } } if (bDriveLetter) { CDMessageBox(hWnd, isz, MB_OK | MB_ICONEXCLAMATION, *pszFile); } else { CDMessageBox(hWnd, isz, MB_OK | MB_ICONEXCLAMATION, pszFile); } if (isz == iszInvalidFileName) { CFileOpenBrowser *pDlgStruct = HwndToBrowser(hWnd); if (pDlgStruct && pDlgStruct->_bUseCombo) { PostMessage(hWnd, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hWnd, cmb13), 1); } else { PostMessage(hWnd, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hWnd, edt1), 1); } } } //////////////////////////////////////////////////////////////////////////// // // GetControlRect // //////////////////////////////////////////////////////////////////////////// void GetControlRect( HWND hwndDlg, UINT idOldCtrl, LPRECT lprc) { HWND hwndOldCtrl = GetDlgItem(hwndDlg, idOldCtrl); GetWindowRect(hwndOldCtrl, lprc); MapWindowRect(HWND_DESKTOP, hwndDlg, lprc); } //////////////////////////////////////////////////////////////////////////// // // HideControl // // Subroutine to hide a dialog control. // // WARNING WARNING WARNING: Some code in the new look depends on hidden // controls remaining where they originally were, even when disabled, // because they're templates for where to create new controls (the toolbar, // or the main list). Therefore, HideControl() must not MOVE the control // being hidden - it may only hide and disable it. If this needs to change, // there must be a separate hiding subroutine used for template controls. // //////////////////////////////////////////////////////////////////////////// void HideControl( HWND hwndDlg, UINT idControl) { HWND hCtrl = ::GetDlgItem(hwndDlg, idControl); ::ShowWindow(hCtrl, SW_HIDE); ::EnableWindow(hCtrl, FALSE); } //////////////////////////////////////////////////////////////////////////// // // SelectEditText // //////////////////////////////////////////////////////////////////////////// void SelectEditText( HWND hwndDlg) { CFileOpenBrowser *pDlgStruct = HwndToBrowser(hwndDlg); if (pDlgStruct && pDlgStruct->_bUseCombo) { HWND hwndEdit = (HWND)SendMessage(GetDlgItem(hwndDlg, cmb13), CBEM_GETEDITCONTROL, 0, 0L); Edit_SetSel(hwndEdit, 0, -1); } else { Edit_SetSel(GetDlgItem(hwndDlg, edt1), 0, -1); } } //////////////////////////////////////////////////////////////////////////// // // GetPathFromLocation // //////////////////////////////////////////////////////////////////////////// BOOL GetPathFromLocation( MYLISTBOXITEM *pLocation, LPTSTR pszBuf) { BOOL fRet = FALSE; // // Zero out the return buffer in case of error. // *pszBuf = 0; // // Try normal channels first. // //See if the IShellFolder we have is a shorcut if so get path from shortcut if (pLocation->psfSub) { IShellLink *psl; if (SUCCEEDED(pLocation->psfSub->QueryInterface(IID_PPV_ARG(IShellLink, &psl)))) { fRet = SUCCEEDED(psl->GetPath(pszBuf, MAX_PATH, 0, 0)); psl->Release(); } } if (!fRet) fRet = SHGetPathFromIDList(pLocation->pidlFull, pszBuf); if (!fRet) { // // Call GetDisplayNameOf with empty pidl. // if (pLocation->psfSub) { STRRET str; ITEMIDLIST idNull = {0}; if (SUCCEEDED(pLocation->psfSub->GetDisplayNameOf(&idNull, SHGDN_FORPARSING, &str))) { fRet = TRUE; StrRetToBuf(&str, &idNull, pszBuf, MAX_PATH); } } } // // Return the result. // return (fRet); } inline _IsSaveContainer(SFGAOF f) { return ((f & (SFGAO_FOLDER | SFGAO_FILESYSANCESTOR)) == (SFGAO_FOLDER | SFGAO_FILESYSANCESTOR)); } inline _IsOpenContainer(SFGAOF f) { return ((f & SFGAO_FOLDER) && (f & (SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR))); } inline _IncludeSaveItem(SFGAOF f) { return (f & (SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM)); } inline _IncludeOpenItem(SFGAOF f) { return (f & (SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_STREAM | SFGAO_FILESYSTEM)); } inline _IsFolderShortcut(SFGAOF f) { return ((f & (SFGAO_FOLDER | SFGAO_LINK)) == (SFGAO_FOLDER | SFGAO_LINK)); } inline _IsStream(SFGAOF f) { return ((f & SFGAO_STREAM) || ((f & SFGAO_FILESYSTEM) && !(f & SFGAO_FILESYSANCESTOR))); } inline _IsCollection(SFGAOF f) { return ((f & (SFGAO_STREAM | SFGAO_FOLDER)) == (SFGAO_STREAM | SFGAO_FOLDER)); } #define MLBI_PERMANENT 0x0001 #define MLBI_PSFFROMPARENT 0x0002 MYLISTBOXITEM::MYLISTBOXITEM() : _cRef(1) { } // This is a special Case Init Function for Initializing Recent Files folder at the top // of namespace in the look in control. BOOL MYLISTBOXITEM::Init( HWND hwndCmb, IShellFolder *psf, LPCITEMIDLIST pidl, DWORD c, DWORD f, DWORD dwAttribs, int iImg, int iSelImg) { _hwndCmb = hwndCmb; cIndent = c; dwFlags = f; pidlThis = ILClone(pidl); pidlFull = ILClone(pidl); psfSub = psf; psfSub->AddRef(); dwAttrs = dwAttribs; iImage = iImg; iSelectedImage = iSelImg; if (pidlThis && pidlFull) { return TRUE; } else { return FALSE; } } BOOL MYLISTBOXITEM::Init( HWND hwndCmb, MYLISTBOXITEM *pParentItem, IShellFolder *psf, LPCITEMIDLIST pidl, DWORD c, DWORD f, IShellTaskScheduler* pScheduler) { if (psf == NULL) { // Invalid parameter passed. return FALSE; } _hwndCmb = hwndCmb; cIndent = c; dwFlags = f; pidlThis = ILClone(pidl); if (pParentItem == NULL) { pidlFull = ILClone(pidl); } else { pidlFull = ILCombine(pParentItem->pidlFull, pidl); } if (pidlThis == NULL || pidlFull == NULL) { psfSub = NULL; } if (dwFlags & MLBI_PSFFROMPARENT) { psfParent = psf; } else { psfSub = psf; } psf->AddRef(); dwAttrs = SHGetAttributes(psf, pidl, SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_STREAM | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_SHARE); AddRef(); if (E_PENDING != SHMapIDListToImageListIndexAsync(pScheduler, psf, pidl, 0, _AsyncIconTaskCallback, this, NULL, &iImage, &iSelectedImage)) { Release(); } if (pidlFull && pidlThis) { return TRUE; } else { return FALSE; } } ULONG MYLISTBOXITEM::AddRef() { return InterlockedIncrement(&_cRef); } ULONG MYLISTBOXITEM::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } MYLISTBOXITEM::~MYLISTBOXITEM() { if (psfSub != NULL) { psfSub->Release(); } if (psfParent != NULL) { psfParent->Release(); } if (pidlThis != NULL) { SHFree(pidlThis); } if (pidlFull != NULL) { SHFree(pidlFull); } } void MYLISTBOXITEM::_AsyncIconTaskCallback(LPCITEMIDLIST pidl, void * pvData, void * pvHint, INT iIconIndex, INT iOpenIconIndex) { MYLISTBOXITEM *plbItem = (MYLISTBOXITEM *)pvData; plbItem->iImage = iIconIndex; plbItem->iSelectedImage = iOpenIconIndex; // Make sure the combobox redraws. if (plbItem->_hwndCmb) { RECT rc; if (GetClientRect(plbItem->_hwndCmb, &rc)) { InvalidateRect(plbItem->_hwndCmb, &rc, FALSE); } } plbItem->Release(); } BOOL IsContainer( IShellFolder *psf, LPCITEMIDLIST pidl) { return _IsOpenContainer(SHGetAttributes(psf, pidl, SFGAO_FOLDER | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR)); } BOOL IsLink( IShellFolder *psf, LPCITEMIDLIST pidl) { return SHGetAttributes(psf, pidl, SFGAO_LINK); } IShellFolder *MYLISTBOXITEM::GetShellFolder() { if (!psfSub) { HRESULT hr; if (ILIsEmpty(pidlThis)) // Some caller passes an empty pidl hr = psfParent->QueryInterface(IID_PPV_ARG(IShellFolder, &psfSub)); else hr = psfParent->BindToObject(pidlThis, NULL, IID_PPV_ARG(IShellFolder, &psfSub)); if (FAILED(hr)) { psfSub = NULL; } else { psfParent->Release(); psfParent = NULL; } } return (psfSub); } //////////////////////////////////////////////////////////////////////////// // // MYLISTBOXITEM::SwitchCurrentDirectory // //////////////////////////////////////////////////////////////////////////// void MYLISTBOXITEM::SwitchCurrentDirectory( ICurrentWorkingDirectory * pcwd) { TCHAR szDir[MAX_PATH + 1]; if (!pidlFull) { SHGetSpecialFolderPath(NULL, szDir, CSIDL_DESKTOPDIRECTORY, FALSE); } else { GetPathFromLocation(this, szDir); } if (szDir[0]) { SetCurrentDirectory(szDir); // // Let AutoComplete know our Current Working Directory. // if (pcwd) pcwd->SetDirectory(szDir); } } //////////////////////////////////////////////////////////////////////////// // // ShouldIncludeObject // //////////////////////////////////////////////////////////////////////////// BOOL ShouldIncludeObject( CFileOpenBrowser *that, LPSHELLFOLDER psfParent, LPCITEMIDLIST pidl, DWORD dwFlags) { BOOL fInclude = FALSE; DWORD dwAttrs = SHGetAttributes(psfParent, pidl, SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_STREAM | SFGAO_FILESYSTEM); if (dwAttrs) { if ((dwFlags & OFN_ENABLEINCLUDENOTIFY) && that) { fInclude = BOOLFROMPTR(CD_SendIncludeItemNotify(that->_hSubDlg, that->_hwndDlg, psfParent, pidl, that->_pOFN, that->_pOFI)); } if (!fInclude) { fInclude = that->_bSave ? _IncludeSaveItem(dwAttrs) : _IncludeOpenItem(dwAttrs); } } return (fInclude); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::EnableFileMRU // // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::EnableFileMRU(BOOL fEnable) { HWND hwnd = NULL; if (fEnable) { HWND hwndCombo; //Make sure combobox is there hwndCombo = GetDlgItem(_hwndDlg, cmb13); if (hwndCombo) { // if we are using the combobox then remove the edit box _bUseCombo = TRUE; SetFocus(hwndCombo); hwnd = GetDlgItem(_hwndDlg,edt1); } else { goto UseEdit; } } else { UseEdit: //We are not going to use combobox. _bUseCombo = FALSE; //SetFocus to the edit window SetFocus(GetDlgItem(_hwndDlg,edt1)); //Destroy the combo box hwnd = GetDlgItem(_hwndDlg, cmb13); } if (hwnd) { DestroyWindow(hwnd); } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::CreateToolbar // // CreateToolbar member function. // creates and initializes the places bar in the dialog // // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::CreateToolbar() { TBBUTTON atbButtons[] = { { 0, IDC_BACK, 0, BTNS_BUTTON, { 0, 0 }, 0, -1 }, { VIEW_PARENTFOLDER, IDC_PARENT, TBSTATE_ENABLED, BTNS_BUTTON, { 0, 0 }, 0, -1 }, { VIEW_NEWFOLDER, IDC_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, { 0, 0 }, 0, -1 }, { VIEW_LIST, IDC_VIEWMENU, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, { 0, 0 }, 0, -1 }, }; TBBUTTON atbButtonsNT4[] = { { 0, 0, 0, BTNS_SEP, { 0, 0 }, 0, 0 }, { VIEW_PARENTFOLDER, IDC_PARENT, TBSTATE_ENABLED, BTNS_BUTTON, { 0, 0 }, 0, -1 }, { 0, 0, 0, BTNS_SEP, { 0, 0 }, 0, 0 }, { VIEW_NEWFOLDER, IDC_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, { 0, 0 }, 0, -1 }, { 0, 0, 0, BTNS_SEP, { 0, 0 }, 0, 0 }, { VIEW_LIST, IDC_VIEWLIST, TBSTATE_ENABLED | TBSTATE_CHECKED, BTNS_CHECKGROUP, { 0, 0 }, 0, -1 }, { VIEW_DETAILS, IDC_VIEWDETAILS, TBSTATE_ENABLED, BTNS_CHECKGROUP, { 0, 0 }, 0, -1 } }; LPTBBUTTON lpButton = atbButtons; int iNumButtons = ARRAYSIZE(atbButtons); RECT rcToolbar; BOOL bBogusCtrlID = SHGetAppCompatFlags(ACF_FILEOPENBOGUSCTRLID) & ACF_FILEOPENBOGUSCTRLID; DWORD dwStyle = WS_TABSTOP | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | WS_CHILD | CCS_NORESIZE |WS_GROUP | CCS_NODIVIDER; // If app wants toolbar to have bogus ctrl ID, make it not a tabstop. if (bBogusCtrlID) dwStyle &= ~WS_TABSTOP; BOOL bAppHack = (CDGetAppCompatFlags() & CDACF_NT40TOOLBAR) ? TRUE : FALSE; if (bAppHack) { lpButton = atbButtonsNT4; iNumButtons =ARRAYSIZE(atbButtonsNT4); dwStyle &= ~TBSTYLE_FLAT; } GetControlRect(_hwndDlg, stc1, &rcToolbar); _hwndToolbar = CreateToolbarEx(_hwndDlg, dwStyle, // stc1: use static text ctrlID // For apps that expect the old bad way, use IDOK. bBogusCtrlID ? IDOK : stc1, 12, HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR, lpButton, iNumButtons, 0, 0, 0, 0, sizeof(TBBUTTON)); if (_hwndToolbar) { TBADDBITMAP ab; SendMessage(_hwndToolbar, TB_SETEXTENDEDSTYLE, TBSTYLE_EX_DRAWDDARROWS, TBSTYLE_EX_DRAWDDARROWS); //Documentation says that we need to send TB_BUTTONSTRUCTSIZE before we add bitmaps SendMessage(_hwndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), (LPARAM)0); SendMessage(_hwndToolbar, TB_SETMAXTEXTROWS, (WPARAM)0, (LPARAM)0); if (!bAppHack) { if (!IsRestricted(REST_NOBACKBUTTON)) { //Add the back/forward navigation buttons ab.hInst = HINST_COMMCTRL; ab.nID = IDB_HIST_SMALL_COLOR; int iIndex = (int) SendMessage(_hwndToolbar, TB_ADDBITMAP, 5, (LPARAM)&ab); //Now set the image index for back button TBBUTTONINFO tbbi; tbbi.cbSize = sizeof(TBBUTTONINFO); tbbi.dwMask = TBIF_IMAGE | TBIF_BYINDEX; SendMessage(_hwndToolbar, TB_GETBUTTONINFO, (WPARAM)0, (LPARAM)&tbbi); tbbi.iImage = iIndex + HIST_BACK; SendMessage(_hwndToolbar, TB_SETBUTTONINFO, (WPARAM)0, (LPARAM)&tbbi); } else { //Back button is restricted. Delete the back button from the toolbar SendMessage(_hwndToolbar, TB_DELETEBUTTON, (WPARAM)0, (LPARAM)0); } } ::SetWindowPos(_hwndToolbar, // Place it after its static control (unless app expects old way) bBogusCtrlID ? NULL : GetDlgItem(_hwndDlg, stc1), rcToolbar.left, rcToolbar.top, rcToolbar.right - rcToolbar.left, rcToolbar.bottom - rcToolbar.top, SWP_NOACTIVATE | SWP_SHOWWINDOW | (bBogusCtrlID ? SWP_NOZORDER : 0)); return TRUE; } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_GetPBItemFromCSIDL(DWORD csidl, SHFILEINFO * psfi, LPITEMIDLIST *ppidl) // Gets a SHFileInfo and pidl for a CSIDL which is used in the places bar //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::_GetPBItemFromCSIDL(DWORD csidl, SHFILEINFO * psfi, LPITEMIDLIST *ppidl) { if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl, ppidl))) { // Are there restrictions on mydocuments or mycomputer? Check for SFGAO_NONENUMERATED // This is for the policies that hide mydocs and mycomputer. if ((csidl == CSIDL_PERSONAL) || (csidl == CSIDL_DRIVES)) { DWORD dwAttr = SFGAO_NONENUMERATED; if (SUCCEEDED(SHGetAttributesOf(*ppidl, &dwAttr)) && (dwAttr & SFGAO_NONENUMERATED)) { // We won't create a placesbar item for this guy. ILFree(*ppidl); return FALSE; } } return SHGetFileInfo((LPCTSTR)*ppidl, 0, psfi, sizeof(*psfi), SHGFI_SYSICONINDEX | SHGFI_PIDL | SHGFI_DISPLAYNAME); } return FALSE; } typedef struct { LPCWSTR pszToken; int nFolder; //CSIDL } STRINGTOCSIDLMAP; static const STRINGTOCSIDLMAP g_rgStringToCSIDL[] = { { L"MyDocuments", CSIDL_PERSONAL }, { L"MyMusic", CSIDL_MYMUSIC }, { L"MyPictures", CSIDL_MYPICTURES }, { L"MyVideo", CSIDL_MYVIDEO }, { L"CommonDocuments", CSIDL_COMMON_DOCUMENTS }, { L"CommonPictures", CSIDL_COMMON_PICTURES }, { L"CommonMusic", CSIDL_COMMON_MUSIC }, { L"CommonVideo", CSIDL_COMMON_VIDEO }, { L"Desktop", CSIDL_DESKTOP }, { L"Recent", CSIDL_RECENT }, { L"MyNetworkPlaces", CSIDL_NETHOOD }, { L"MyFavorites", CSIDL_FAVORITES }, { L"MyComputer", CSIDL_DRIVES }, { L"Printers", CSIDL_PRINTERS }, { L"ProgramFiles", CSIDL_PROGRAM_FILES }, }; //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_GetPBItemFromTokenStrings(LPTSTR lpszPath, SHFILEINFO * psfi, LPITEMIDLIST *ppidl) // Gets a SHFileInfo and pidl for a path which is used in the places bar //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::_GetPBItemFromTokenStrings(LPTSTR lpszPath, SHFILEINFO * psfi, LPITEMIDLIST *ppidl) { for (int i = 0; i < ARRAYSIZE(g_rgStringToCSIDL); i++) { if (StrCmpI(lpszPath, g_rgStringToCSIDL[i].pszToken) == 0) { return _GetPBItemFromCSIDL(g_rgStringToCSIDL[i].nFolder, psfi, ppidl); } } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_GetPBItemFromPath(LPTSTR lpszPath, SHFILEINFO * psfi, LPITEMIDLIST *ppidl) // Gets a SHFileInfo and pidl for a path which is used in the places bar //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::_GetPBItemFromPath(LPTSTR lpszPath, size_t cchPath, SHFILEINFO * psfi, LPITEMIDLIST *ppidl) { TCHAR szTemp[MAX_PATH]; BOOL bRet = FALSE; //Expand environment strings if any if (ExpandEnvironmentStrings(lpszPath, szTemp, SIZECHARS(szTemp))) { bRet = SUCCEEDED(StringCchCopy(lpszPath, cchPath, szTemp)); } if (bRet) { SHGetFileInfo(lpszPath,0,psfi,sizeof(*psfi), SHGFI_ICON|SHGFI_LARGEICON | SHGFI_DISPLAYNAME); SHILCreateFromPath(lpszPath, ppidl, NULL); } return bRet; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_EnumPlacesBarItem(HKEY, int, SHFILEINFO) // Enumerates the Place bar item in the registry //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::_EnumPlacesBarItem(HKEY hkey, int i , SHFILEINFO * psfi, LPITEMIDLIST *ppidl) { BOOL bRet = FALSE; if (hkey == NULL) { static const int aPlaces[] = { CSIDL_RECENT, CSIDL_DESKTOP, CSIDL_PERSONAL, CSIDL_DRIVES, CSIDL_NETWORK, }; if (i >= 0 && i < MAXPLACESBARITEMS) { bRet = _GetPBItemFromCSIDL(aPlaces[i], psfi, ppidl); } } else { TCHAR szName[MAX_PATH]; TCHAR szValue[MAX_PATH]; DWORD cbValue; DWORD dwType; cbValue = sizeof(szValue); // Byte size, not character size. StringCchPrintf(szName, ARRAYSIZE(szName), L"Place%d", i); if (SHRegGetValue(hkey, NULL, szName, SRRF_RT_REG_SZ | SRRF_RT_REG_EXPAND_SZ | SRRF_NOEXPAND | SRRF_RT_DWORD, &dwType, (LPBYTE)szValue, &cbValue) == ERROR_SUCCESS) { if ((dwType != REG_DWORD) && (dwType != REG_EXPAND_SZ) && (dwType != REG_SZ)) { return FALSE; } if (dwType == REG_DWORD) { bRet = _GetPBItemFromCSIDL((DWORD)*szValue, psfi, ppidl); } else { if (dwType == REG_SZ) { // Check for special strings that indicate places. bRet = _GetPBItemFromTokenStrings(szValue, psfi, ppidl); } if (!bRet) { bRet = _GetPBItemFromPath(szValue, ARRAYSIZE(szValue), psfi, ppidl); } } } } return bRet; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_GetPlacesBarItemToolTip // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::_GetPlacesBarItemToolTip(int idCmd, LPTSTR pText, DWORD dwSize) { TBBUTTONINFO tbbi; LPITEMIDLIST pidl; BOOL bRet = FALSE; // Return null string in case anything goes wrong pText[0] = TEXT('\0'); tbbi.cbSize = SIZEOF(tbbi); tbbi.lParam = 0; tbbi.dwMask = TBIF_LPARAM; if (SendMessage(_hwndPlacesbar, TB_GETBUTTONINFO, idCmd, (LPARAM)&tbbi) < 0) return FALSE; pidl = (LPITEMIDLIST)tbbi.lParam; if (pidl) { IShellFolder *psf; LPITEMIDLIST pidlLast; HRESULT hres = CDBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), (LPCITEMIDLIST *)&pidlLast); if (SUCCEEDED(hres)) { IQueryInfo *pqi; if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidlLast, IID_IQueryInfo, NULL, (void**)&pqi))) { WCHAR *pwszTip; if (SUCCEEDED(pqi->GetInfoTip(0, &pwszTip)) && pwszTip) { SHUnicodeToTChar(pwszTip, pText, dwSize); SHFree(pwszTip); bRet = TRUE; } pqi->Release(); } psf->Release(); } } return bRet; } /////////////////////////////////////////////////////////////////////////// // // CFileOpenBrorwser::_RecreatePlacesbar // // called when something changes that requires the placesbar be recreated (e.g. icons change) // /////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::_RecreatePlacesbar() { if (_hwndPlacesbar) { // Free any pidls in the places bar _CleanupPlacesbar(); // Remove all buttons in places bar int cButtons = (int)SendMessage(_hwndPlacesbar, TB_BUTTONCOUNT, 0, 0); for (int i = 0; i < cButtons; i++) { SendMessage(_hwndPlacesbar, TB_DELETEBUTTON, 0, 0); } // Put them back in, with potentially new images. _FillPlacesbar(_hwndPlacesbar); } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::CreatePlacesBar // // CreatePlacesBar member function. // creates and initializes the places bar in the dialog // // //////////////////////////////////////////////////////////////////////////// HWND CFileOpenBrowser::CreatePlacesbar(HWND hwndDlg) { HWND hwndTB = GetDlgItem(hwndDlg, ctl1); if (hwndTB) { //Set the version for the toolbar SendMessage(hwndTB, CCM_SETVERSION, COMCTL32_VERSION, 0); // Sets the size of the TBBUTTON structure. SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); SetWindowTheme(hwndTB, L"Placesbar", NULL); SendMessage(hwndTB, TB_SETMAXTEXTROWS, 2, 0); // Try to set toolbar to show 2 rows // For themes, we'll change the default padding, so we need to save it // off in case we need to restore it. _dwPlacesbarPadding = SendMessage(hwndTB, TB_GETPADDING, 0, 0); _FillPlacesbar(hwndTB); } return hwndTB; } void CFileOpenBrowser::_FillPlacesbar(HWND hwndPlacesbar) { HKEY hkey = NULL; int i; TBBUTTON tbb; SHFILEINFO sfi; LPITEMIDLIST pidl; HIMAGELIST himl; //See if Places bar key is available RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_PLACESBAR, 0, KEY_READ, &hkey); Shell_GetImageLists(&himl, NULL); for (i=0; i < MAXPLACESBARITEMS; i++) { if (_EnumPlacesBarItem(hkey, i, &sfi, &pidl)) { //Now Add the item to the toolbar tbb.iBitmap = sfi.iIcon; tbb.fsState = TBSTATE_ENABLED; tbb.fsStyle = BTNS_BUTTON; tbb.idCommand = IDC_PLACESBAR_BASE + _iCommandID; tbb.iString = (INT_PTR)&sfi.szDisplayName; tbb.dwData = (INT_PTR)pidl; SendMessage(hwndPlacesbar, TB_ADDBUTTONS, (UINT)1, (LPARAM)&tbb); //Increment the command ID _iCommandID++; } } //Close the reg key if (hkey) { RegCloseKey(hkey); } HIMAGELIST himlOld = (HIMAGELIST) SendMessage(hwndPlacesbar, TB_SETIMAGELIST, 0, (LPARAM)himl); // Destroy the old imagelist only the first time. After this, the imagelist we get back is the // one we've set, the system imagelist. if ((himlOld != NULL) && _bDestroyPlacesbarImageList) { ImageList_Destroy(himlOld); } _bDestroyPlacesbarImageList = FALSE; OnThemeActive(_hwndDlg, IsAppThemed()); // Add the buttons SendMessage(hwndPlacesbar, TB_AUTOSIZE, (WPARAM)0, (LPARAM)0); } void CFileOpenBrowser::_CleanupPlacesbar() { if (_hwndPlacesbar) { TBBUTTONINFO tbbi; LPITEMIDLIST pidl; for (int i=0; i < MAXPLACESBARITEMS; i++) { tbbi.cbSize = SIZEOF(tbbi); tbbi.lParam = 0; tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX; if (SendMessage(_hwndPlacesbar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi) >= 0) { pidl = (LPITEMIDLIST)tbbi.lParam; if (pidl) { ILFree(pidl); } } } } } // Less padding for themes #define PLACESBAR_THEMEPADDING MAKELPARAM(2, 2) void CFileOpenBrowser::OnThemeActive(HWND hwndDlg, BOOL bActive) { HWND hwndPlacesBar = GetDlgItem(hwndDlg, ctl1); if (hwndPlacesBar) { // For themes, use the default colour scheme for the places toolbar: COLORSCHEME cs; cs.dwSize = SIZEOF(cs); cs.clrBtnHighlight = bActive ? CLR_DEFAULT : GetSysColor(COLOR_BTNHIGHLIGHT); cs.clrBtnShadow = bActive ? CLR_DEFAULT : GetSysColor(COLOR_3DDKSHADOW); SendMessage(hwndPlacesBar, TB_SETCOLORSCHEME, 0, (LPARAM) &cs); // For themes, we have a background, so make the toolbar background non-transparent // (the resource specifies TBSTYLE_FLAT, which includes TBSTYLE_TRANSPARENT) DWORD_PTR dwTBStyle = SendMessage(hwndPlacesBar, TB_GETSTYLE, 0, 0); SendMessage(hwndPlacesBar, TB_SETSTYLE, 0, bActive ? (dwTBStyle & ~TBSTYLE_TRANSPARENT) : (dwTBStyle | TBSTYLE_TRANSPARENT)); // Special padding for themes on comctlv6 only (RAID #424528) if (SendMessage(hwndPlacesBar, CCM_GETVERSION, 0, 0) >= 0x600) { SendMessage(hwndPlacesBar, TB_SETPADDING, 0, bActive? PLACESBAR_THEMEPADDING : _dwPlacesbarPadding); } // Remove the clientedge extended style for themes LONG_PTR dwPlacesExStyle = GetWindowLongPtr(hwndPlacesBar, GWL_EXSTYLE); SetWindowLongPtr(hwndPlacesBar, GWL_EXSTYLE, bActive ? (dwPlacesExStyle & ~WS_EX_CLIENTEDGE) : (dwPlacesExStyle | WS_EX_CLIENTEDGE)); // And apply these frame style changes... SetWindowPos(hwndPlacesBar, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); // Ensure buttons go right to edge of client area (client area has changed) RECT rc; GetClientRect(hwndPlacesBar, &rc); SendMessage(hwndPlacesBar, TB_SETBUTTONWIDTH, 0, (LPARAM)MAKELONG(RECTWIDTH(rc), RECTWIDTH(rc))); } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::CFileOpenBrowser // // CFileOpenBrowser constructor. // Minimal construction of the object. Much more construction in // InitLocation. // //////////////////////////////////////////////////////////////////////////// CFileOpenBrowser::CFileOpenBrowser( HWND hDlg, BOOL fIsSaveAs) : _cRef(1), _iCurrentLocation(-1), _iVersion(OPENFILEVERSION), _pCurrentLocation(NULL), _psv(NULL), _hwndDlg(hDlg), _hwndView(NULL), _hwndToolbar(NULL), _psfCurrent(NULL), _bSave(fIsSaveAs), _iComboIndex(-1), _hwndTips(NULL), _ptlog(NULL), _iCheckedButton(-1), _pidlSelection(NULL), _lpOKProc(NULL) { _iNodeDesktop = NODE_DESKTOP; _iNodeDrives = NODE_DRIVES; _szLastFilter[0] = CHAR_NULL; _bEnableSizing = FALSE; _bUseCombo = TRUE; _hwndGrip = NULL; _ptLastSize.x = 0; _ptLastSize.y = 0; _sizeView.cx = 0; _bUseSizeView = FALSE; _bAppRedrawn = FALSE; _bDestroyPlacesbarImageList = TRUE; HMENU hMenu; hMenu = GetSystemMenu(hDlg, FALSE); DeleteMenu(hMenu, SC_MINIMIZE, MF_BYCOMMAND); DeleteMenu(hMenu, SC_MAXIMIZE, MF_BYCOMMAND); DeleteMenu(hMenu, SC_RESTORE, MF_BYCOMMAND); Shell_GetImageLists(NULL, &_himl); // // This setting could change on the fly, but I really don't care // about that rare case. // SHELLSTATE ss; SHGetSetSettings(&ss, SSF_SHOWEXTENSIONS, FALSE); _fShowExtensions = ss.fShowExtensions; _pScheduler = NULL; CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC, IID_PPV_ARG(IShellTaskScheduler, &_pScheduler)); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::~CFileOpenBrowser // // CFileOpenBrowser destructor. // //////////////////////////////////////////////////////////////////////////// CFileOpenBrowser::~CFileOpenBrowser() { if (_uRegister) { SHChangeNotifyDeregister(_uRegister); _uRegister = 0; } // // Ensure that we discard the tooltip window. // if (_hwndTips) { DestroyWindow(_hwndTips); _hwndTips = NULL; // handle is no longer valid } if (_hwndGrip) { DestroyWindow(_hwndGrip); _hwndGrip = NULL; } _CleanupPlacesbar(); if (_pcwd) { _pcwd->Release(); } if (_ptlog) { _ptlog->Release(); } Pidl_Set(&_pidlSelection,NULL); if (_pScheduler) _pScheduler->Release(); } HRESULT CFileOpenBrowser::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CFileOpenBrowser, IShellBrowser), // IID_IShellBrowser QITABENT(CFileOpenBrowser, ICommDlgBrowser2), // IID_ICommDlgBrowser2 QITABENTMULTI(CFileOpenBrowser, ICommDlgBrowser, ICommDlgBrowser2), // IID_ICommDlgBrowser QITABENT(CFileOpenBrowser, IServiceProvider), // IID_IServiceProvider { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CFileOpenBrowser::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CFileOpenBrowser::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDMETHODIMP CFileOpenBrowser::GetWindow(HWND *phwnd) { *phwnd = _hwndDlg; return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::ContextSensitiveHelp // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::ContextSensitiveHelp( BOOL fEnable) { // // Shouldn't need in a common dialog. // return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SetStatusTextSB // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::SetStatusTextSB( LPCOLESTR pwch) { // // We don't have any status bar. // return S_OK; } //////////////////////////////////////////////////////////////////////////// // // GetFocusedChild // //////////////////////////////////////////////////////////////////////////// HWND GetFocusedChild( HWND hwndDlg, HWND hwndFocus) { HWND hwndParent; if (!hwndDlg) { return (NULL); } if (!hwndFocus) { hwndFocus = ::GetFocus(); } // // Go up the parent chain until the parent is the main dialog. // while ((hwndParent = ::GetParent(hwndFocus)) != hwndDlg) { if (!hwndParent) { return (NULL); } hwndFocus = hwndParent; } return (hwndFocus); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::EnableModelessSB // //////////////////////////////////////////////////////////////////////////// typedef struct { UINT idExcept; BOOL fEnable; } ENABLEKIDS; #define PROP_WASDISABLED TEXT("Comdlg32_WasDisabled") BOOL CALLBACK _EnableKidsEnum(HWND hwnd, LPARAM lp) { ENABLEKIDS *pek = (ENABLEKIDS *)lp; if (pek->idExcept != GetDlgCtrlID(hwnd)) { if (pek->fEnable) { // When re-enabling, don't re-enable windows that were // previously disabled if (!RemoveProp(hwnd, PROP_WASDISABLED)) { EnableWindow(hwnd, TRUE); } } else { // When disabling, remember whether the window was already // disabled so we don't accidentally re-enable it if (EnableWindow(hwnd, pek->fEnable)) { SetProp(hwnd, PROP_WASDISABLED, IntToPtr(TRUE)); } } } return TRUE; } void EnableChildrenWithException(HWND hwndDlg, UINT idExcept, BOOL fEnable) { ENABLEKIDS ek = {idExcept, fEnable}; ::EnumChildWindows(hwndDlg, _EnableKidsEnum, (LPARAM)&ek); } STDMETHODIMP CFileOpenBrowser::EnableModelessSB(BOOL fEnable) { LONG cBefore = _cRefCannotNavigate; if (fEnable) { _cRefCannotNavigate--; } else { _cRefCannotNavigate++; } ASSERT(_cRefCannotNavigate >= 0); if (!cBefore || !_cRefCannotNavigate) { // we changed state if (!fEnable) _hwndModelessFocus = GetFocusedChild(_hwndDlg, NULL); EnableChildrenWithException(_hwndDlg, IDCANCEL, fEnable); if (fEnable && _hwndModelessFocus) SetFocus(_hwndModelessFocus); } return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::TranslateAcceleratorSB // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::TranslateAcceleratorSB( LPMSG pmsg, WORD wID) { // // We don't use the Key Stroke. // return S_FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::BrowseObject // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::BrowseObject( LPCITEMIDLIST pidl, UINT wFlags) { return JumpToIDList(pidl); } BOOL _IsRecentFolder(LPCITEMIDLIST pidl) { ASSERT(pidl); BOOL fRet = FALSE; LPITEMIDLIST pidlRecent = SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE); if (pidlRecent) { fRet = ILIsEqual(pidlRecent, pidl); ILFree(pidlRecent); } return fRet; } // My Pictures or My Videos BOOL CFileOpenBrowser::_IsThumbnailFolder(LPCITEMIDLIST pidl) { BOOL fThumbnailFolder = FALSE; WCHAR szPath[MAX_PATH + 1]; if (SHGetPathFromIDList(pidl, szPath)) { fThumbnailFolder = PathIsEqualOrSubFolder(MAKEINTRESOURCE(CSIDL_MYPICTURES), szPath) || PathIsEqualOrSubFolder(MAKEINTRESOURCE(CSIDL_MYVIDEO), szPath); } return fThumbnailFolder; } static const GUID CLSID_WIA_FOLDER1 = { 0xe211b736, 0x43fd, 0x11d1, { 0x9e, 0xfb, 0x00, 0x00, 0xf8, 0x75, 0x7f, 0xcd} }; static const GUID CLSID_WIA_FOLDER2 = { 0xFB0C9C8A, 0x6C50, 0x11D1, { 0x9F, 0x1D, 0x00, 0x00, 0xf8, 0x75, 0x7f, 0xcd} }; LOCTYPE CFileOpenBrowser::_GetLocationType(MYLISTBOXITEM *pLocation) { if (_IsRecentFolder(pLocation->pidlFull)) return LOCTYPE_RECENT_FOLDER; if (_IsThumbnailFolder(pLocation->pidlFull)) return LOCTYPE_MYPICTURES_FOLDER; IShellFolder *psf = pLocation->GetShellFolder(); // Note: this is a MYLISTBOXITEM member variable, don't need to Release() if (_IsWIAFolder(psf)) { return LOCTYPE_WIA_FOLDER; } return LOCTYPE_OTHERS; } // Is it a windows image acquisition folder? BOOL CFileOpenBrowser::_IsWIAFolder(IShellFolder *psf) { CLSID clsid; return (psf && SUCCEEDED(IUnknown_GetClassID(psf, &clsid)) && (IsEqualGUID(clsid, CLSID_WIA_FOLDER1) || IsEqualGUID(clsid, CLSID_WIA_FOLDER2))); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::GetViewStateStream // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::GetViewStateStream( DWORD grfMode, LPSTREAM *pStrm) { // // FEATURE: We should implement this so there is some persistence // for the file open dailog. // ASSERT(_pCurrentLocation); ASSERT(pStrm); *pStrm = NULL; if ((grfMode == STGM_READ) && _IsRecentFolder(_pCurrentLocation->pidlFull)) { // we want to open the stream from the registry... *pStrm = SHOpenRegStream(HKEY_LOCAL_MACHINE, TEXT("Software\\microsoft\\windows\\currentversion\\explorer\\recentdocs"), TEXT("ViewStream"), grfMode); } return (*pStrm ? S_OK : E_FAIL); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::GetControlWindow // // Get the handles of the various windows in the File Cabinet. // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::GetControlWindow( UINT id, HWND *lphwnd) { if (id == FCW_TOOLBAR) { *lphwnd = _hwndToolbar; return S_OK; } return (E_NOTIMPL); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SendControlMsg // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::SendControlMsg( UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret) { LRESULT lres = 0; if (id == FCW_TOOLBAR) { // // We need to translate messages from defview intended for these // buttons to our own. // switch (uMsg) { case (TB_CHECKBUTTON) : { #if 0 // we don't do this anymore because we use the viewmenu dropdown switch (wParam) { case (SFVIDM_VIEW_DETAILS) : { wParam = IDC_VIEWDETAILS; break; } case (SFVIDM_VIEW_LIST) : { wParam = IDC_VIEWLIST; break; } default : { goto Bail; } } break; #endif } default : { goto Bail; break; } } lres = SendMessage(_hwndToolbar, uMsg, wParam, lParam); } Bail: if (pret) { *pret = lres; } return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::QueryActiveShellView // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::QueryActiveShellView( LPSHELLVIEW *ppsv) { if (_psv) { *ppsv = _psv; _psv->AddRef(); return S_OK; } *ppsv = NULL; return (E_NOINTERFACE); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnViewWindowActive // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::OnViewWindowActive( LPSHELLVIEW _psv) { // // No need to process this. We don't do menus. // return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::InsertMenusSB // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::InsertMenusSB( HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) { return (E_NOTIMPL); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SetMenuSB // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::SetMenuSB( HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject) { return (E_NOTIMPL); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::RemoveMenusSB // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::RemoveMenusSB( HMENU hmenuShared) { return (E_NOTIMPL); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SetToolbarItems // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::SetToolbarItems( LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags) { // // We don't let containers customize our toolbar. // return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnDefaultCommand // // Process a double-click or Enter keystroke in the view control. // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::OnDefaultCommand( struct IShellView *ppshv) { if (ppshv != _psv) { return (E_INVALIDARG); } OnDblClick(FALSE); return S_OK; } /////////////////////////////////// // *** IServiceProvider methods *** /////////////////////////////////// HRESULT CFileOpenBrowser::QueryService(REFGUID guidService, REFIID riid, void **ppvObj) { HRESULT hr = E_FAIL; *ppvObj = NULL; if (IsEqualGUID(guidService, SID_SCommDlgBrowser)) { hr = QueryInterface(riid, ppvObj); } return hr; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SetCurrentFilter // // note: pszFilter must fit in a buffer of MAXPATH+1 //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::SetCurrentFilter( LPCTSTR pszFilter, OKBUTTONFLAGS Flags) { LPTSTR lpNext; // // Don't do anything if it's the same filter. // if (lstrcmp(_szLastFilter, pszFilter) == 0) { return; } EVAL(SUCCEEDED(StringCchCopy(_szLastFilter, ARRAYSIZE(_szLastFilter), pszFilter))); // The filter should always fit in _szLastFilter int nLeft = ARRAYSIZE(_szLastFilter) - lstrlen(_szLastFilter) - 1; // // Do nothing if quoted. // if (Flags & OKBUTTON_QUOTED) { return; } // // If pszFilter matches a filter spec, select that spec. // HWND hCmb = GetDlgItem(_hwndDlg, cmb1); if (hCmb) { int nMax = ComboBox_GetCount(hCmb); int n; BOOL bCustomFilter = _pOFN->lpstrCustomFilter && *_pOFN->lpstrCustomFilter; for (n = 0; n < nMax; n++) { LPTSTR pFilter = (LPTSTR)ComboBox_GetItemData(hCmb, n); if (pFilter && pFilter != (LPTSTR)CB_ERR) { if (!lstrcmpi(pFilter, pszFilter)) { if (n != ComboBox_GetCurSel(hCmb)) { ComboBox_SetCurSel(hCmb, n); } break; } } } } // // For LFNs, tack on a '*' after non-wild extensions. // for (lpNext = _szLastFilter; nLeft > 0;) { // Turning any kind of ';' separated list into a NULL-char separated list. LPTSTR lpSemiColon = StrChr(lpNext, CHAR_SEMICOLON); if (!lpSemiColon) { lpSemiColon = lpNext + lstrlen(lpNext); } TCHAR cTemp = *lpSemiColon; *lpSemiColon = CHAR_NULL; LPTSTR lpDot = StrChr(lpNext, CHAR_DOT); // // See if there is an extension that is not wild. // if (lpDot && *(lpDot + 1) && !IsWild(lpDot)) { // // Tack on a star. // We know there is still enough room because nLeft > 0. // if (cTemp != CHAR_NULL) { MoveMemory(lpSemiColon + 2, lpSemiColon + 1, (lstrlen(lpSemiColon + 1) + 1) * sizeof(TCHAR)); // plus 1 for terminating NULL } *lpSemiColon = CHAR_STAR; ++lpSemiColon; --nLeft; } *lpSemiColon = cTemp; if (cTemp == CHAR_NULL) { break; } else { lpNext = lpSemiColon + 1; } } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SwitchView // // Switch the view control to a new container. // //////////////////////////////////////////////////////////////////////////// HRESULT CFileOpenBrowser::SwitchView( IShellFolder *psfNew, LPCITEMIDLIST pidlNew, FOLDERSETTINGS *pfs, SHELLVIEWID const *pvid, BOOL fUseDefaultView) { IShellView *psvNew; IShellView2 *psv2New; RECT rc; if (!psfNew) { return (E_INVALIDARG); } GetControlRect(_hwndDlg, lst1, &rc); if (_bEnableSizing) { if (_hwndView) { // // Don't directly use the rect but instead use the size as // applications like VB may move the window off the screen. // RECT rcView; GetWindowRect(_hwndView, &rcView); _sizeView.cx = rcView.right - rcView.left; _sizeView.cy = rcView.bottom - rcView.top; rc.right = rc.left + _sizeView.cx; rc.bottom = rc.top + _sizeView.cy; } else if (_bUseSizeView && _sizeView.cx) { // // If we previously failed then use cached size. // rc.right = rc.left + _sizeView.cx; rc.bottom = rc.top + _sizeView.cy; } } HRESULT hres = psfNew->CreateViewObject(_hwndDlg, IID_PPV_ARG(IShellView, &psvNew)); if (FAILED(hres)) { return hres; } IShellView *psvOld; HWND hwndNew; WAIT_CURSOR w(this); // // The view window itself won't take the focus. But we can set // focus there and see if it bounces to the same place it is // currently. If that's the case, we want the new view window // to get the focus; otherwise, we put it back where it was. // BOOL bViewFocus = (GetFocusedChild(_hwndDlg, NULL) == _hwndView); psvOld = _psv; // // We attempt to blow off drawing on the main dialog. Note that // we should leave in SETREDRAW stuff to minimize flicker in case // this fails. // BOOL bLocked = LockWindowUpdate(_hwndDlg); // // We need to kill the current _psv before creating the new one in case // the current one has a background thread going that is trying to // call us back (IncludeObject). // if (psvOld) { SendMessage(_hwndView, WM_SETREDRAW, FALSE, 0); psvOld->DestroyViewWindow(); _hwndView = NULL; _psv = NULL; // // Don't release yet. We will pass this to CreateViewWindow(). // } // // At this point, there should be no background processing happening. // _psfCurrent = psfNew; SHGetPathFromIDList(pidlNew, _szCurDir); // // New windows (like the view window about to be created) show up at // the bottom of the Z order, so I need to disable drawing of the // subdialog while creating the view window; drawing will be enabled // after the Z-order has been set properly. // if (_hSubDlg) { SendMessage(_hSubDlg, WM_SETREDRAW, FALSE, 0); } // // _psv must be set before creating the view window since we // validate it on the IncludeObject callback. // _psv = psvNew; if ((pvid || fUseDefaultView) && SUCCEEDED(psvNew->QueryInterface(IID_PPV_ARG(IShellView2, &psv2New)))) { SV2CVW2_PARAMS cParams; SHELLVIEWID vidCurrent = {0}; cParams.cbSize = SIZEOF(SV2CVW2_PARAMS); cParams.psvPrev = psvOld; cParams.pfs = pfs; cParams.psbOwner = this; cParams.prcView = &rc; if (pvid) cParams.pvid = pvid; // View id; for example, &CLSID_ThumbnailViewExt; else { psv2New->GetView(&vidCurrent, SV2GV_DEFAULTVIEW); // We don't want filmstrip view in fileopen, so we'll switch that to thumbnail. if (IsEqualIID(VID_ThumbStrip, vidCurrent)) cParams.pvid = &VID_Thumbnails; else cParams.pvid = &vidCurrent; } hres = psv2New->CreateViewWindow2(&cParams); hwndNew = cParams.hwndView; psv2New->Release(); } else hres = _psv->CreateViewWindow(psvOld, pfs, this, &rc, &hwndNew); _bUseSizeView = FAILED(hres); if (SUCCEEDED(hres)) { hres = psvNew->UIActivate(SVUIA_INPLACEACTIVATE); } if (psvOld) { psvOld->Release(); } if (_hSubDlg) { // // Turn REDRAW back on before changing the focus in case the // SubDlg has the focus. // SendMessage(_hSubDlg, WM_SETREDRAW, TRUE, 0); } if (SUCCEEDED(hres)) { DWORD dwAttr = SFGAO_STORAGE | SFGAO_READONLY; SHGetAttributesOf(pidlNew, &dwAttr); BOOL bNewFolder = (dwAttr & SFGAO_STORAGE) && !(dwAttr & SFGAO_READONLY); ::SendMessage(_hwndToolbar, TB_ENABLEBUTTON, IDC_NEWFOLDER, bNewFolder); _hwndView = hwndNew; // // Move the view window to the right spot in the Z (tab) order. // SetWindowPos(hwndNew, GetDlgItem(_hwndDlg, lst1), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // // Give it the right window ID for WinHelp. // SetWindowLong(hwndNew, GWL_ID, lst2); ::RedrawWindow(_hwndView, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_UPDATENOW); if (bViewFocus) { ::SetFocus(_hwndView); } } else { _psv = NULL; psvNew->Release(); } // // Let's draw again! // if (bLocked) { LockWindowUpdate(NULL); } return hres; } void CFileOpenBrowser::_WaitCursor(BOOL fWait) { if (fWait) _cWaitCursor++; else _cWaitCursor--; SetCursor(LoadCursor(NULL, _cWaitCursor ? IDC_WAIT : IDC_ARROW)); } BOOL CFileOpenBrowser::OnSetCursor() { if (_cWaitCursor) { SetCursor(LoadCursor(NULL, IDC_WAIT)); return TRUE; } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // JustGetToolTipText // //////////////////////////////////////////////////////////////////////////// void JustGetToolTipText( UINT idCommand, LPTOOLTIPTEXT pTtt) { if (!CDLoadString(::g_hinst, idCommand + MH_TOOLTIPBASE, pTtt->szText, ARRAYSIZE(pTtt->szText))) { *pTtt->lpszText = 0; } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnNotify // // Process notify messages from the view -- for tooltips. // //////////////////////////////////////////////////////////////////////////// LRESULT CFileOpenBrowser::OnNotify( LPNMHDR pnm) { LRESULT lres = 0; switch (pnm->code) { case (TTN_NEEDTEXT) : { HWND hCtrl = GetDlgItem(_hwndDlg, cmb2); LPTOOLTIPTEXT lptt = (LPTOOLTIPTEXT)pnm; int iTemp; // // If this is the combo control which shows the current drive, // then convert this into a suitable tool-tip message giving // the 'full' path to this object. // if (pnm->idFrom == (UINT_PTR)hCtrl) { // // iTemp will contain index of first path element. // GetDirectoryFromLB(_szTipBuf, &iTemp); lptt->lpszText = _szTipBuf; lptt->szText[0] = CHAR_NULL; lptt->hinst = NULL; // no instance needed } else if (IsInRange(pnm->idFrom, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST)) { if (_hwndView) { lres = ::SendMessage(_hwndView, WM_NOTIFY, 0, (LPARAM)pnm); } } else if (IsInRange(pnm->idFrom, IDC_PLACESBAR_BASE, IDC_PLACESBAR_BASE + _iCommandID)) { _GetPlacesBarItemToolTip((int)pnm->idFrom, _szTipBuf, ARRAYSIZE(_szTipBuf)); lptt->lpszText = _szTipBuf; } else { JustGetToolTipText((UINT) pnm->idFrom, lptt); } lres = TRUE; break; } case (NM_STARTWAIT) : case (NM_ENDWAIT) : { // // What we really want is for the user to simulate a mouse // move/setcursor. // _WaitCursor(pnm->code == NM_STARTWAIT); break; } case (TBN_DROPDOWN) : { RECT r; VARIANT v = {VT_INT_PTR}; TBNOTIFY *ptbn = (TBNOTIFY*)pnm; DFVCMDDATA cd; // v.vt = VT_I4; v.byref = &r; SendMessage(_hwndToolbar, TB_GETRECT, ptbn->iItem, (LPARAM)&r); MapWindowRect(_hwndToolbar, HWND_DESKTOP, &r); cd.pva = &v; cd.hwnd = _hwndToolbar; cd.nCmdIDTranslated = 0; SendMessage(_hwndView, WM_COMMAND, SFVIDM_VIEW_VIEWMENU, (LONG_PTR)&cd); break; } case (NM_CUSTOMDRAW) : if (!IsAppThemed()) { LPNMTBCUSTOMDRAW lpcust = (LPNMTBCUSTOMDRAW)pnm; //Make sure its from places bar if (lpcust->nmcd.hdr.hwndFrom == _hwndPlacesbar) { switch (lpcust->nmcd.dwDrawStage) { case (CDDS_PREERASE) : { HDC hdc = (HDC)lpcust->nmcd.hdc; RECT rc; GetClientRect(_hwndPlacesbar, &rc); SHFillRectClr(hdc, &rc, GetSysColor(COLOR_BTNSHADOW)); lres = CDRF_SKIPDEFAULT; SetDlgMsgResult(_hwndDlg, WM_NOTIFY, lres); break; } case (CDDS_PREPAINT) : { lres = CDRF_NOTIFYITEMDRAW; SetDlgMsgResult(_hwndDlg, WM_NOTIFY, lres); break; } case (CDDS_ITEMPREPAINT) : { //Set the text color to window lpcust->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT); lpcust->clrBtnFace = GetSysColor(COLOR_BTNSHADOW); lpcust->nStringBkMode = TRANSPARENT; lres = CDRF_DODEFAULT; if (lpcust->nmcd.uItemState & CDIS_CHECKED) { lpcust->hbrMonoDither = NULL; } SetDlgMsgResult(_hwndDlg, WM_NOTIFY, lres); break; } } } } } return (lres); } // Get the display name of a shell object. void GetViewItemText(IShellFolder *psf, LPCITEMIDLIST pidl, LPTSTR pBuf, UINT cchBuf, DWORD flags = SHGDN_INFOLDER | SHGDN_FORPARSING) { DisplayNameOf(psf, pidl, flags, pBuf, cchBuf); } //////////////////////////////////////////////////////////////////////////// // // GetListboxItem // // Get a MYLISTBOXITEM object out of the location dropdown. // //////////////////////////////////////////////////////////////////////////// MYLISTBOXITEM *GetListboxItem( HWND hCtrl, WPARAM iItem) { MYLISTBOXITEM *p = (MYLISTBOXITEM *)SendMessage(hCtrl, CB_GETITEMDATA, iItem, NULL); if (p == (MYLISTBOXITEM *)CB_ERR) { return NULL; } else { return p; } } //////////////////////////////////////////////////////////////////////////// // // _ReleaseStgMedium // //////////////////////////////////////////////////////////////////////////// HRESULT _ReleaseStgMedium( LPSTGMEDIUM pmedium) { if (pmedium->pUnkForRelease) { pmedium->pUnkForRelease->Release(); } else { switch (pmedium->tymed) { case (TYMED_HGLOBAL) : { GlobalFree(pmedium->hGlobal); break; } default : { // // Not fully implemented. // MessageBeep(0); break; } } } return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SetSaveButton // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::SetSaveButton( UINT idSaveButton) { PostMessage(_hwndDlg, CDM_SETSAVEBUTTON, idSaveButton, 0); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::RealSetSaveButton // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::RealSetSaveButton( UINT idSaveButton) { MSG msg; if (PeekMessage(&msg, _hwndDlg, CDM_SETSAVEBUTTON, CDM_SETSAVEBUTTON, PM_NOREMOVE)) { // // There is another SETSAVEBUTTON message in the queue, so blow off // this one. // return; } if (_bSave) { TCHAR szTemp[40]; LPTSTR pszTemp = _tszDefSave; // // Load the string if not the "Save" string or there is no // app-specified default. // if ((idSaveButton != iszFileSaveButton) || !pszTemp) { CDLoadString(g_hinst, idSaveButton, szTemp, ARRAYSIZE(szTemp)); pszTemp = szTemp; } GetDlgItemText(_hwndDlg, IDOK, _szBuf, ARRAYSIZE(_szBuf)); if (lstrcmp(_szBuf, pszTemp)) { // // Avoid some flicker. // SetDlgItemText(_hwndDlg, IDOK, pszTemp); } } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SetEditFile // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::SetEditFile( LPCTSTR pszFile, LPCTSTR pszFriendlyName, BOOL bShowExt, BOOL bSaveNullExt) { BOOL bHasHiddenExt = FALSE; // // Save the whole file name. // if (!_pszHideExt.TSStrCpy(pszFile)) { _pszHideExt.TSStrCpy(NULL); bShowExt = TRUE; } // // FEATURE: This is bogus -- we only want to hide KNOWN extensions, // not all extensions. // if (!bShowExt && !IsWild(pszFile) && !pszFriendlyName) { LPTSTR pszExt = PathFindExtension(pszFile); if (*pszExt) { // // If there was an extension, hide it. // *pszExt = 0; bHasHiddenExt = TRUE; } } else if (pszFriendlyName) { // A friendly name was provided. Use it. pszFile = pszFriendlyName; // Not technically true, but this bit indicates that an app sends a CDM_GETSPEC, we give the for-parsing // value in _pszHideExt, not the "friendly name" in the edit box bHasHiddenExt = TRUE; } if (_bUseCombo) { HWND hwndEdit = (HWND)SendMessage(GetDlgItem(_hwndDlg, cmb13), CBEM_GETEDITCONTROL, 0, 0L); SetWindowText(hwndEdit, pszFile); } else { SetDlgItemText(_hwndDlg, edt1, pszFile); } // // If the initial file name has no extension, we want to do our normal // extension finding stuff. Any other time we get a file with no // extension, we should not do this. // _bUseHideExt = (LPTSTR)_pszHideExt ? (bSaveNullExt ? TRUE : bHasHiddenExt) : FALSE; } //////////////////////////////////////////////////////////////////////////// // // FindEOF // //////////////////////////////////////////////////////////////////////////// LPWSTR FindEOF( LPWSTR pszFiles) { BOOL bQuoted; LPWSTR pszCurrent = pszFiles; while (*pszCurrent == CHAR_SPACE) { ++pszCurrent; } // // Note that we always assume a quoted string, even if no quotes exist, // so the only file delimiters are '"' and '\0'. This allows somebody to // type or in the edit control and the right // thing happens. // bQuoted = TRUE; if (*pszCurrent == CHAR_QUOTE) { ++pszCurrent; } //Remove the quote from the file list if one exist StringCopyOverlap(pszFiles, pszCurrent); // // Find the end of the filename (first quote or unquoted space). // for (; ; pszFiles = CharNext(pszFiles)) { switch (*pszFiles) { case (CHAR_NULL) : { return (pszFiles); } case (CHAR_SPACE) : { if (!bQuoted) { return (pszFiles); } break; } case (CHAR_QUOTE) : { // // Note we only support '"' at the very beginning and very // end of a file name. // return (pszFiles); } default : { break; } } } } //////////////////////////////////////////////////////////////////////////// // // ConvertToNULLTerm // //////////////////////////////////////////////////////////////////////////// DWORD ConvertToNULLTerm( LPTSTR pchRead) { DWORD cFiles = 0; // The input string is of the form "file1.ext" "file2.ext" ... "filen.ext" // convert this string of this form into doubly null terminated string // ie file1.ext\0file2.ext\0....filen.ext\0\0 for (; ;) { // Finds the end of the first file name in the list of // remaining file names. Also this function removes the initial // quote character, and any preceding spaces (so it generally shifts a portion // of the string to the left by 2 characters) LPTSTR pchEnd = FindEOF(pchRead); // // Mark the end of the filename with a NULL. // if (*pchEnd) { *pchEnd = CHAR_NULL; cFiles++; pchRead = pchEnd + 1; } else { // // Found EOL. Make sure we did not end with spaces. // if (*pchRead) { pchRead = pchEnd + 1; cFiles++; } break; } } // // Double-NULL terminate. // *pchRead = CHAR_NULL; return (cFiles); } //////////////////////////////////////////////////////////////////////////// // // SelFocusEnumCB // //////////////////////////////////////////////////////////////////////////// typedef struct _SELFOCUS { BOOL bSelChange; UINT idSaveButton; int nSel; TEMPSTR sHidden; TEMPSTR sDisplayed; } SELFOCUS; BOOL SelFocusEnumCB( CFileOpenBrowser *that, LPCITEMIDLIST pidl, LPARAM lParam) { if (!pidl) { return TRUE; } SELFOCUS *psf = (SELFOCUS *)lParam; TCHAR szBuf[MAX_PATH + 1]; TCHAR szBufFriendly[MAX_PATH + 1]; DWORD dwAttrs = SHGetAttributes(that->_psfCurrent, pidl, SFGAO_STORAGECAPMASK); if (dwAttrs) { if (_IsOpenContainer(dwAttrs)) { psf->idSaveButton = iszFileOpenButton; } else { if (psf->bSelChange && (((that->_pOFN->Flags & OFN_ENABLEINCLUDENOTIFY) && (that->_bSelIsObject = CD_SendIncludeItemNotify(that->_hSubDlg, that->_hwndDlg, that->_psfCurrent, pidl, that->_pOFN, that->_pOFI))) || (_IsStream(dwAttrs)))) { ++psf->nSel; if (that->_pOFN->Flags & OFN_ALLOWMULTISELECT) { // // Mark if this is an OBJECT we just selected. // if (that->_bSelIsObject) { ITEMIDLIST idl; idl.mkid.cb = 0; // // Get full path to this folder. // GetViewItemText(that->_psfCurrent, &idl, szBuf, ARRAYSIZE(szBuf), SHGDN_FORPARSING); if (szBuf[0]) { that->_pszObjectCurDir.TSStrCpy(szBuf); // Ok if it fails? } // // Get full path to this item (in case we only get one // selection). // GetViewItemText(that->_psfCurrent, pidl, szBuf, ARRAYSIZE(szBuf), SHGDN_FORPARSING); that->_pszObjectPath.TSStrCpy(szBuf); } *szBuf = CHAR_QUOTE; GetViewItemText(that->_psfCurrent, pidl, szBuf + 1, ARRAYSIZE(szBuf) - 3); EVAL(SUCCEEDED(StringCchCat(szBuf, ARRAYSIZE(szBuf), L"\" "))); // Should always be enough room if (!psf->sHidden.TSStrCat(szBuf)) { psf->nSel = -1; return FALSE; } if (!that->_fShowExtensions) { LPTSTR pszExt = PathFindExtension(szBuf + 1); if (*pszExt) { *pszExt = 0; // Get rid of the file extension. EVAL(SUCCEEDED(StringCchCat(szBuf, ARRAYSIZE(szBuf), L"\" "))); // Should always be enough room - see GetViewItemText } } if (!psf->sDisplayed.TSStrCat(szBuf)) { psf->nSel = -1; return FALSE; } } else { SHTCUTINFO info; info.dwAttr = SFGAO_FOLDER; info.fReSolve = FALSE; info.pszLinkFile = NULL; info.cchFile = 0; info.ppidl = NULL; if ((that->GetLinkStatus(pidl, &info)) && (info.dwAttr & SFGAO_FOLDER)) { // This means that the pidl is a link and the link points to a folder // in this case We Should not update the edit box and treat the link like // a directory psf->idSaveButton = iszFileOpenButton; } else { TCHAR *pszFriendlyName = NULL; GetViewItemText(that->_psfCurrent, pidl, szBuf, ARRAYSIZE(szBuf)); // Special case WIA folders. They want friendly names. Might want to do this for all // folders, but that might cause app compat nightmare. if (that->_IsWIAFolder(that->_psfCurrent)) { GetViewItemText(that->_psfCurrent, pidl, szBufFriendly, ARRAYSIZE(szBufFriendly), SHGDN_INFOLDER); pszFriendlyName = szBufFriendly; } else { IShellFolder *psfItem; if (SUCCEEDED(that->_psfCurrent->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfItem)))) { if (that->_IsWIAFolder(psfItem)) { GetViewItemText(that->_psfCurrent, pidl, szBufFriendly, ARRAYSIZE(szBufFriendly), SHGDN_INFOLDER); pszFriendlyName = szBufFriendly; } psfItem->Release(); } } that->SetEditFile(szBuf, pszFriendlyName, that->_fShowExtensions); if (that->_bSelIsObject) { GetViewItemText(that->_psfCurrent, pidl, szBuf, ARRAYSIZE(szBuf), SHGDN_FORPARSING); that->_pszObjectPath.TSStrCpy(szBuf); } } } } } } //if there is an item selected then cache that items pidl Pidl_Set(&that->_pidlSelection,pidl); return TRUE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SelFocusChange // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::SelFocusChange( BOOL bSelChange) { SELFOCUS sf; sf.bSelChange = bSelChange; sf.idSaveButton = iszFileSaveButton; sf.nSel = 0; _bSelIsObject = FALSE; EnumItemObjects(SVGIO_SELECTION, SelFocusEnumCB, (LPARAM)&sf); if (_pOFN->Flags & OFN_ALLOWMULTISELECT) { switch (sf.nSel) { case (-1) : { // // Oops! We ran out of memory. // MessageBeep(0); return; } case (0) : { // // No files selected; do not change edit control. // break; } case (1) : { // // Strip off quotes so the single file case looks OK. // ConvertToNULLTerm(sf.sHidden); LPITEMIDLIST pidlSel = ILClone(_pidlSelection); SetEditFile(sf.sHidden, NULL, _fShowExtensions); if (pidlSel) { // The SetEditFile above will nuke any _pidlSelection that was set as a result // of EnumItemObjects, by causing a CBN_EDITCHANGE notification (edit box changed, so we // think we should nuked the _pidlSelection - doh!). // So here we restore it, if there was one set. Pidl_Set(&_pidlSelection, pidlSel); ILFree(pidlSel); } sf.idSaveButton = iszFileSaveButton; break; } default : { SetEditFile(sf.sDisplayed, NULL, TRUE); _pszHideExt.TSStrCpy(sf.sHidden); sf.idSaveButton = iszFileSaveButton; //More than one item selected so free selected item pidl Pidl_Set(&_pidlSelection,NULL);; break; } } } SetSaveButton(sf.idSaveButton); } //////////////////////////////////////////////////////////////////////////// // // SelRenameCB // //////////////////////////////////////////////////////////////////////////// BOOL SelRenameCB( CFileOpenBrowser *that, LPCITEMIDLIST pidl, LPARAM lParam) { if (!pidl) { return TRUE; } Pidl_Set(&that->_pidlSelection, pidl); if (!SHGetAttributes(that->_psfCurrent, pidl, SFGAO_FOLDER)) { // // If it is not a folder then set the selection to nothing // so that whatever is in the edit box will be used. // that->_psv->SelectItem(NULL, SVSI_DESELECTOTHERS); } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SelRename // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::SelRename(void) { EnumItemObjects(SVGIO_SELECTION, SelRenameCB, NULL); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnStateChange // // Process selection change in the view control. // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::OnStateChange( struct IShellView *ppshv, ULONG uChange) { if (ppshv != _psv) { return (E_INVALIDARG); } switch (uChange) { case (CDBOSC_SETFOCUS) : { if (_bSave) { SelFocusChange(FALSE); } break; } case (CDBOSC_KILLFOCUS) : { SetSaveButton(iszFileSaveButton); break; } case (CDBOSC_SELCHANGE) : { // // Post one of these messages, since we seem to get a whole bunch // of them. // if (!_fSelChangedPending) { _fSelChangedPending = TRUE; PostMessage(_hwndDlg, CDM_SELCHANGE, 0, 0); } break; } case (CDBOSC_RENAME) : { SelRename(); break; } default : { return (E_NOTIMPL); } } return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::IncludeObject // // Tell the view control which objects to include in its enumerations. // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::IncludeObject( struct IShellView *ppshv, LPCITEMIDLIST pidl) { if (ppshv != _psv) { return (E_INVALIDARG); } BOOL bIncludeItem = FALSE; // // See if the callback is enabled. // if (_pOFN->Flags & OFN_ENABLEINCLUDENOTIFY) { // // See what the callback says. // bIncludeItem = BOOLFROMPTR(CD_SendIncludeItemNotify(_hSubDlg, _hwndDlg, _psfCurrent, pidl, _pOFN, _pOFI)); } if (!bIncludeItem) { DWORD dwAttrs = SHGetAttributes(_psfCurrent, pidl, SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_STREAM | SFGAO_FILESYSTEM | SFGAO_FOLDER); bIncludeItem = _bSave ? _IncludeSaveItem(dwAttrs) : _IncludeOpenItem(dwAttrs); if (!bIncludeItem) { return (S_FALSE); } // Apply filter if this thing is filesystem or canmoniker, except: // If it is an item that contains filesystem items (SFGAO_STORAGEANCESTOR - typical folder) // OR if it is a folder that canmoniker (ftp folder) if (bIncludeItem && *_szLastFilter) { BOOL fContainer = _bSave ? _IsSaveContainer(dwAttrs) : _IsOpenContainer(dwAttrs); if (!fContainer) { GetViewItemText(_psfCurrent, (LPITEMIDLIST)pidl, _szBuf, ARRAYSIZE(_szBuf)); if (!LinkMatchSpec(pidl, _szLastFilter) && !PathMatchSpec(_szBuf, _szLastFilter)) { return (S_FALSE); } } } } return S_OK; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::Notify // // Notification to decide whether or not a printer should be selected. // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::Notify( struct IShellView *ppshv, DWORD dwNotify) { return S_FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::GetDefaultMenuText // // Returns the default menu text. // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::GetDefaultMenuText( struct IShellView *ppshv, WCHAR *pszText, INT cchMax) { return S_FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::GetViewFlags // // Returns Flags to customize the view . // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CFileOpenBrowser::GetViewFlags(DWORD *pdwFlags) { DWORD dwFlags = 0; if (pdwFlags) { if (_pOFN->Flags & OFN_FORCESHOWHIDDEN) { dwFlags |= CDB2GVF_SHOWALLFILES; } *pdwFlags = dwFlags; } return S_OK; } // Insert a single item into the location dropdown. BOOL InsertItem(HWND hCtrl, int iItem, MYLISTBOXITEM *pItem, TCHAR *pszName) { LPTSTR pszChar; for (pszChar = pszName; *pszChar != CHAR_NULL; pszChar = CharNext(pszChar)) { if (pszChar - pszName >= MAX_DRIVELIST_STRING_LEN - 1) { *pszChar = CHAR_NULL; break; } } if (SendMessage(hCtrl, CB_INSERTSTRING, iItem, (LPARAM)(LPCTSTR)pszName) == CB_ERR) { return FALSE; } SendMessage(hCtrl, CB_SETITEMDATA, iItem, (LPARAM)pItem); return TRUE; } int CALLBACK LBItemCompareProc(void * p1, void * p2, LPARAM lParam) { IShellFolder *psfParent = (IShellFolder *)lParam; MYLISTBOXITEM *pItem1 = (MYLISTBOXITEM *)p1; MYLISTBOXITEM *pItem2 = (MYLISTBOXITEM *)p2; HRESULT hres = psfParent->CompareIDs(0, pItem1->pidlThis, pItem2->pidlThis); return (short)SCODE_CODE(GetScode(hres)); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::UpdateLevel // // Insert the contents of a shell container into the location dropdown. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::UpdateLevel( HWND hwndLB, int iInsert, MYLISTBOXITEM *pParentItem) { if (!pParentItem) { return; } LPENUMIDLIST penum; HDPA hdpa; DWORD cIndent = pParentItem->cIndent + 1; IShellFolder *psfParent = pParentItem->GetShellFolder(); if (!psfParent) { return; } hdpa = DPA_Create(4); if (!hdpa) { // // No memory: Cannot enum this level. // return; } if (S_OK == psfParent->EnumObjects(hwndLB, SHCONTF_FOLDERS, &penum)) { ULONG celt; LPITEMIDLIST pidl; while (penum->Next(1, &pidl, &celt) == S_OK && celt == 1) { // // Note: We need to avoid creation of pItem if this is not // a file system object (or ancestor) to avoid extra // bindings. // if (ShouldIncludeObject(this, psfParent, pidl, _pOFN->Flags)) { MYLISTBOXITEM *pItem = new MYLISTBOXITEM(); if (pItem) { if (pItem->Init(GetDlgItem(_hwndDlg, cmb2), pParentItem, psfParent, pidl, cIndent, MLBI_PERMANENT | MLBI_PSFFROMPARENT, _pScheduler) && (DPA_AppendPtr(hdpa, pItem) >= 0)) { //empty body } else { pItem->Release(); } } } SHFree(pidl); } penum->Release(); } DPA_Sort(hdpa, LBItemCompareProc, (LPARAM)psfParent); int nLBIndex, nDPAIndex, nDPAItems; BOOL bCurItemGone; nDPAItems = DPA_GetPtrCount(hdpa); nLBIndex = iInsert; bCurItemGone = FALSE; // // Make sure the user is not playing with the selection right now. // ComboBox_ShowDropdown(hwndLB, FALSE); // // We're all sorted, so now we can do a merge. // for (nDPAIndex = 0; ; ++nDPAIndex) { MYLISTBOXITEM *pNewItem; TCHAR szBuf[MAX_DRIVELIST_STRING_LEN]; MYLISTBOXITEM *pOldItem; if (nDPAIndex < nDPAItems) { pNewItem = (MYLISTBOXITEM *)DPA_FastGetPtr(hdpa, nDPAIndex); } else { // // Signal that we got to the end of the list. // pNewItem = NULL; } for (pOldItem = GetListboxItem(hwndLB, nLBIndex); pOldItem != NULL; pOldItem = GetListboxItem(hwndLB, ++nLBIndex)) { int nCmp; if (pOldItem->cIndent < cIndent) { // // We went up a level, so insert here. // break; } else if (pOldItem->cIndent > cIndent) { // // We went down a level so ignore this. // continue; } // // Set this to 1 at the end of the DPA to clear out deleted items // at the end. // nCmp = !pNewItem ? 1 : LBItemCompareProc(pNewItem, pOldItem, (LPARAM)psfParent); if (nCmp < 0) { // // We found the first item greater than the new item, so // add it in. // break; } else if (nCmp > 0) { // // Oops! It looks like this item no longer exists, so // delete it. // for (; ;) { if (pOldItem == _pCurrentLocation) { bCurItemGone = TRUE; _pCurrentLocation = NULL; } pOldItem->Release(); SendMessage(hwndLB, CB_DELETESTRING, nLBIndex, NULL); pOldItem = GetListboxItem(hwndLB, nLBIndex); if (!pOldItem || pOldItem->cIndent <= cIndent) { break; } } // // We need to continue from the current position, not the // next. // --nLBIndex; } else { // // This item already exists, so no need to add it. // Make sure we do not check this LB item again. // pOldItem->dwFlags |= MLBI_PERMANENT; ++nLBIndex; goto NotThisItem; } } if (!pNewItem) { // // Got to the end of the list. // break; } GetViewItemText(psfParent, pNewItem->pidlThis, szBuf, ARRAYSIZE(szBuf), SHGDN_NORMAL); if (szBuf[0] && InsertItem(hwndLB, nLBIndex, pNewItem, szBuf)) { ++nLBIndex; } else { NotThisItem: pNewItem->Release(); } } DPA_Destroy(hdpa); if (bCurItemGone) { // // If we deleted the current selection, go back to the desktop. // ComboBox_SetCurSel(hwndLB, 0); OnSelChange(-1, TRUE); } _iCurrentLocation = ComboBox_GetCurSel(hwndLB); } //////////////////////////////////////////////////////////////////////////// // // ClearListbox // // Clear the location dropdown and delete all entries. // //////////////////////////////////////////////////////////////////////////// void ClearListbox( HWND hwndList) { SendMessage(hwndList, WM_SETREDRAW, FALSE, NULL); int cItems = (int) SendMessage(hwndList, CB_GETCOUNT, NULL, NULL); while (cItems--) { MYLISTBOXITEM *pItem = GetListboxItem(hwndList, 0); if (pItem) pItem->Release(); SendMessage(hwndList, CB_DELETESTRING, 0, NULL); } SendMessage(hwndList, WM_SETREDRAW, TRUE, NULL); InvalidateRect(hwndList, NULL, FALSE); } //////////////////////////////////////////////////////////////////////////// // // InitFilterBox // // Places the double null terminated list of filters in the combo box. // // The list consists of pairs of null terminated strings, with an // additional null terminating the list. // //////////////////////////////////////////////////////////////////////////// DWORD InitFilterBox( HWND hDlg, LPCTSTR lpszFilter) { DWORD nIndex = 0; UINT nLen; HWND hCmb = GetDlgItem(hDlg, cmb1); if (hCmb) { while (*lpszFilter) { // // First string put in as string to show. // nIndex = ComboBox_AddString(hCmb, lpszFilter); nLen = lstrlen(lpszFilter) + 1; lpszFilter += nLen; // // Second string put in as itemdata. // ComboBox_SetItemData(hCmb, nIndex, lpszFilter); // // Advance to next element. // nLen = lstrlen(lpszFilter) + 1; lpszFilter += nLen; } } // // nIndex could be CB_ERR, which could cause problems. // if (nIndex == CB_ERR) { nIndex = 0; } return (nIndex); } //////////////////////////////////////////////////////////////////////////// // // MoveControls // //////////////////////////////////////////////////////////////////////////// void MoveControls( HWND hDlg, BOOL bBelow, int nStart, int nXMove, int nYMove) { HWND hwnd; RECT rcWnd; if (nXMove == 0 && nYMove == 0) { // // Quick out if nothing to do. // return; } for (hwnd = GetWindow(hDlg, GW_CHILD); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT)) { GetWindowRect(hwnd, &rcWnd); MapWindowRect(HWND_DESKTOP, hDlg, &rcWnd); if (bBelow) { if (rcWnd.top < nStart) { continue; } } else { if (rcWnd.left < nStart) { continue; } } SetWindowPos(hwnd, NULL, rcWnd.left + nXMove, rcWnd.top + nYMove, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } } //////////////////////////////////////////////////////////////////////////// // // DummyDlgProc // //////////////////////////////////////////////////////////////////////////// BOOL_PTR CALLBACK DummyDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case (WM_INITDIALOG) : { break; } default : { return FALSE; } } return TRUE; } /* -------- | Cancel | -------- -- | -------- | x Open As Read | Help | | Height by which all controls below view needs to be moved -------- | and also height by which View window height should be increased. -- */ void CFileOpenBrowser::ReAdjustDialog() { int iDelta = 0; RECT rc1,rc2; //Make sure all our assumptions are valid if ((_iVersion < OPENFILEVERSION_NT5) || //if this dialog version is less than NT5 or IsWindowEnabled(GetDlgItem(_hwndDlg, chx1)) || // if Open As Read Only is still enabled or IsWindowEnabled(GetDlgItem(_hwndDlg, pshHelp))) // If the Help button is still enabled then { //Dont do anything return ; } GetWindowRect(GetDlgItem(_hwndDlg, pshHelp), &rc1); GetWindowRect(GetDlgItem(_hwndDlg, IDCANCEL), &rc2); //Add the height of the button iDelta += RECTHEIGHT(rc1); //Add the gap between buttons iDelta += rc1.top - rc2.bottom; RECT rcView; GetWindowRect(GetDlgItem(_hwndDlg, lst1), &rcView); MapWindowRect(HWND_DESKTOP, _hwndDlg, &rcView); HDWP hdwp; hdwp = BeginDeferWindowPos(10); HWND hwnd; RECT rc; hwnd = ::GetWindow(_hwndDlg, GW_CHILD); while (hwnd && hdwp) { GetWindowRect(hwnd, &rc); MapWindowRect(HWND_DESKTOP, _hwndDlg, &rc); switch (GetDlgCtrlID(hwnd)) { case pshHelp: case chx1: break; default : // // See if the control needs to be adjusted. // if (rc.top > rcView.bottom) { //Move Y position of these controls hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left, rc.top + iDelta, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER); } } hwnd = ::GetWindow(hwnd, GW_HWNDNEXT); } //Adjust the size of the view window if (hdwp) { hdwp = DeferWindowPos(hdwp, GetDlgItem(_hwndDlg, lst1), NULL, rcView.left, rcView.top, RECTWIDTH(rcView), RECTHEIGHT(rcView) + iDelta, SWP_NOZORDER); } EndDeferWindowPos(hdwp); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::ResetDialogHeight // // Hack for Borland JBuilder Professional (pah!) // // These guys relied on a bug in Win95/NT4's Comdlg32 that we fixed in IE4. // So instead of reintroducing the bug, we detect that they are relying // on the bug and hack around them. // // These guys do a SetWindowLong(GWL_STYLE) on the dialog box and // then reparent it! Unfortunately, they didn't quite get their // bookkeeping right: They forgot to do a RedrawWindow after removing // the WS_CAPTION style. You see, just editing the style doesn't do // anything - the style changes don't take effect until the next // RedrawWindow. When they scratched their heads ("Hey, why is // the caption still there?"), they decided to brute-force the // solution: They slide the window so the caption goes "off the screen". // // Problem: We fixed a bug for IE4 where ResetDialogHeight would screw // up and not resize the dialog when it should've, if the app did a // SetWindowPos on the window to change its vertical position downward // by more than the amount we needed to grow. // // So now when we resize it properly, this generates an internal // RedrawWindow, which means that Borland's brute-force hack tries // to fix a problem that no longer exists! // // Therefore, ResetDialogHeight now checks if the app has // // 1. Changed the dialog window style, // 2. Moved the dialog downward by more than we needed to grow, // 3. Forgotten to call RedrawWindow. // // If so, then we temporarily restore the original dialog window style, // do the (correct) resize, then restore the window style. Reverting // the window style means that all the non-client stuff retains its old // (incorrect, but what the app is expecting) size. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::ResetDialogHeight( HWND hDlg, HWND hwndExclude, HWND hwndGrip, int nCtlsBottom) { POINT ptCurrent; int topNew; GetControlsArea(hDlg, hwndExclude, hwndGrip, &ptCurrent, &topNew); int nDiffBottom = nCtlsBottom - ptCurrent.y; if (nDiffBottom > 0) { RECT rcFull; int Height; GetWindowRect(hDlg, &rcFull); Height = RECTHEIGHT(rcFull) - nDiffBottom; if (Height >= ptCurrent.y) { // Borland JBuilder hack! This SetWindowPos will generate // a RedrawWindow which the app might not be expecting. // Detect this case and create a set of temporary styles // which will neutralize the frame recalc implicit in the // RedrawWindow. // LONG lStylePrev; BOOL bBorlandHack = FALSE; if (!_bAppRedrawn && // App didn't call RedrawWindow _topOrig + nCtlsBottom <= topNew + ptCurrent.y) // Win95 didn't resize { // Since the app didn't call RedrawWindow, it still // thinks that there is a WS_CAPTION. So put the caption // back while we do frame recalcs. bBorlandHack = TRUE; lStylePrev = GetWindowLong(hDlg, GWL_STYLE); SetWindowLong(hDlg, GWL_STYLE, lStylePrev | WS_CAPTION); } SetWindowPos(hDlg, NULL, 0, 0, RECTWIDTH(rcFull), Height, SWP_NOZORDER | SWP_NOMOVE); if (bBorlandHack) { // Restore the original style after we temporarily // messed with it. SetWindowLong(hDlg, GWL_STYLE, lStylePrev); } } } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::CreateHookDialog // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::CreateHookDialog( POINT *pPtSize) { DWORD Flags = _pOFN->Flags; BOOL bRet = FALSE; HANDLE hTemplate; HINSTANCE hinst; LPCTSTR lpDlg; HWND hCtlCmn; RECT rcReal, rcSub, rcToolbar, rcAppToolbar; int nXMove, nXRoom, nYMove, nYRoom, nXStart, nYStart; DWORD dwStyle; DLGPROC lpfnHookProc; if (!(Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))) { // // No hook or template; nothing to do. // ResetDialogHeight(_hwndDlg, NULL, _hwndGrip, pPtSize->y); GetWindowRect(_hwndDlg, &rcReal); _ptLastSize.x = rcReal.right - rcReal.left; _ptLastSize.y = rcReal.bottom - rcReal.top; return TRUE; } if (Flags & OFN_ENABLETEMPLATEHANDLE) { hTemplate = _pOFN->hInstance; hinst = ::g_hinst; } else { if (Flags & OFN_ENABLETEMPLATE) { if (!_pOFN->lpTemplateName) { StoreExtendedError(CDERR_NOTEMPLATE); return FALSE; } if (!_pOFN->hInstance) { StoreExtendedError(CDERR_NOHINSTANCE); return FALSE; } lpDlg = _pOFN->lpTemplateName; hinst = _pOFN->hInstance; } else { hinst = ::g_hinst; lpDlg = MAKEINTRESOURCE(DUMMYFILEOPENORD); } HRSRC hRes = FindResource(hinst, lpDlg, RT_DIALOG); if (hRes == NULL) { StoreExtendedError(CDERR_FINDRESFAILURE); return FALSE; } if ((hTemplate = LoadResource(hinst, hRes)) == NULL) { StoreExtendedError(CDERR_LOADRESFAILURE); return FALSE; } } if (!LockResource(hTemplate)) { StoreExtendedError(CDERR_LOADRESFAILURE); return FALSE; } dwStyle = ((LPDLGTEMPLATE)hTemplate)->style; if (!(dwStyle & WS_CHILD)) { // // I don't want to go poking in their template, and I don't want to // make a copy, so I will just fail. This also helps us weed out // "old-style" templates that were accidentally used. // StoreExtendedError(CDERR_DIALOGFAILURE); return FALSE; } if (Flags & OFN_ENABLEHOOK) { lpfnHookProc = (DLGPROC)GETHOOKFN(_pOFN); } else { lpfnHookProc = DummyDlgProc; } // // WOW apps are not allowed to get the new explorer look, so there // is no need to do any special WOW checking before calling the create // dialog function. // if (_pOFI->ApiType == COMDLG_ANSI) { ThunkOpenFileNameW2A(_pOFI); _hSubDlg = CreateDialogIndirectParamA(hinst, (LPDLGTEMPLATE)hTemplate, _hwndDlg, lpfnHookProc, (LPARAM)(_pOFI->pOFNA)); ThunkOpenFileNameA2W(_pOFI); } else { _hSubDlg = CreateDialogIndirectParam(hinst, (LPDLGTEMPLATE)hTemplate, _hwndDlg, lpfnHookProc, (LPARAM)_pOFN); } if (!_hSubDlg) { StoreExtendedError(CDERR_DIALOGFAILURE); return FALSE; } // // We reset the height of the dialog after creating the hook dialog so // the hook can hide controls in its WM_INITDIALOG message. // ResetDialogHeight(_hwndDlg, _hSubDlg, _hwndGrip, pPtSize->y); // // Now move all of the controls around. // GetClientRect(_hwndDlg, &rcReal); GetClientRect(_hSubDlg, &rcSub); hCtlCmn = GetDlgItem(_hSubDlg, stc32); if (hCtlCmn) { RECT rcCmn; GetWindowRect(hCtlCmn, &rcCmn); MapWindowRect(HWND_DESKTOP, _hSubDlg, &rcCmn); // // Move the controls in our dialog to make room for the hook's // controls above and to the left. // MoveControls(_hwndDlg, FALSE, 0, rcCmn.left, rcCmn.top); // // Calculate how far sub dialog controls need to move, and how much // more room our dialog needs. // nXStart = rcCmn.right; nYStart = rcCmn.bottom; //See how far part the cotrols are in the template nXMove = (rcReal.right - rcReal.left) - (rcCmn.right - rcCmn.left); nYMove = (rcReal.bottom - rcReal.top) - (rcCmn.bottom - rcCmn.top); //See how much room we need to leave at the bottom and right // for the sub dialog controls at the botton and right nXRoom = rcSub.right - (rcCmn.right - rcCmn.left); nYRoom = rcSub.bottom - (rcCmn.bottom - rcCmn.top); if (nXMove < 0) { // // If the template size is too big, we need more room in the // dialog. // nXRoom -= nXMove; nXMove = 0; } if (nYMove < 0) { // // If the template size is too big, we need more room in the // dialog. // nYRoom -= nYMove; nYMove = 0; } // // Resize the "template" control so the hook knows the size of our // stuff. // SetWindowPos(hCtlCmn, NULL, 0, 0, rcReal.right - rcReal.left, rcReal.bottom - rcReal.top, SWP_NOMOVE | SWP_NOZORDER); } else { // // Extra controls go on the bottom by default. // nXStart = nYStart = nXMove = nXRoom = 0; nYMove = rcReal.bottom; nYRoom = rcSub.bottom; } MoveControls(_hSubDlg, FALSE, nXStart, nXMove, 0); MoveControls(_hSubDlg, TRUE, nYStart, 0, nYMove); // // Resize our dialog and the sub dialog. // FEATURE: We need to check whether part of the dialog is off screen. // GetWindowRect(_hwndDlg, &rcReal); _ptLastSize.x = (rcReal.right - rcReal.left) + nXRoom; _ptLastSize.y = (rcReal.bottom - rcReal.top) + nYRoom; SetWindowPos(_hwndDlg, NULL, 0, 0, _ptLastSize.x, _ptLastSize.y, SWP_NOZORDER | SWP_NOMOVE); // // Note that we are moving this to (0,0) and the bottom of the Z order. // GetWindowRect(_hSubDlg, &rcReal); SetWindowPos(_hSubDlg, HWND_BOTTOM, 0, 0, (rcReal.right - rcReal.left) + nXMove, (rcReal.bottom - rcReal.top) + nYMove, 0); ShowWindow(_hSubDlg, SW_SHOW); CD_SendInitDoneNotify(_hSubDlg, _hwndDlg, _pOFN, _pOFI); // // Make sure the toolbar is still large enough. Apps like Visio move // the toolbar control and may make it too small now that we added the // View Desktop toolbar button. // if (_hwndToolbar && IsVisible(_hwndToolbar)) { LONG Width; // // Get the default toolbar coordinates. // GetControlRect(_hwndDlg, stc1, &rcToolbar); // // Get the app adjusted toolbar coordinates. // GetWindowRect(_hwndToolbar, &rcAppToolbar); MapWindowRect(HWND_DESKTOP, _hwndDlg, &rcAppToolbar); // // See if the default toolbar size is greater than the current // toolbar size. // Width = rcToolbar.right - rcToolbar.left; if (Width > (rcAppToolbar.right - rcAppToolbar.left)) { // // Set rcToolbar to be the new toolbar rectangle. // rcToolbar.left = rcAppToolbar.left; rcToolbar.top = rcAppToolbar.top; rcToolbar.right = rcAppToolbar.left + Width; rcToolbar.bottom = rcAppToolbar.bottom; // // Get the dialog coordinates. // GetWindowRect(_hwndDlg, &rcReal); MapWindowRect(HWND_DESKTOP, _hwndDlg, &rcReal); // // Make sure the new toolbar doesn't go off the end of // the dialog. // if (rcToolbar.right < rcReal.right) { // // Make sure there are no controls to the right of the // toolbar that overlap the new toolbar. // for (hCtlCmn = ::GetWindow(_hwndDlg, GW_CHILD); hCtlCmn; hCtlCmn = ::GetWindow(hCtlCmn, GW_HWNDNEXT)) { if ((hCtlCmn != _hwndToolbar) && IsVisible(hCtlCmn)) { RECT rcTemp; // // Get the coordinates of the window. // GetWindowRect(hCtlCmn, &rcSub); MapWindowRect(HWND_DESKTOP, _hwndDlg, &rcSub); // // If the App's toolbar rectangle does not // intersect the window and the the new toolbar // does intersect the window, then we cannot // increase the size of the toolbar. // if (!IntersectRect(&rcTemp, &rcAppToolbar, &rcSub) && IntersectRect(&rcTemp, &rcToolbar, &rcSub)) { break; } } } // // Reset the size of the toolbar if there were no conflicts. // if (!hCtlCmn) { ::SetWindowPos(_hwndToolbar, NULL, rcToolbar.left, rcToolbar.top, Width, rcToolbar.bottom - rcToolbar.top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW); } } } } bRet = TRUE; return (bRet); } //////////////////////////////////////////////////////////////////////////// // // InitSaveAsControls // // Change the captions of a bunch of controls to say saveas-like things. // //////////////////////////////////////////////////////////////////////////// const struct { UINT idControl; UINT idString; } aSaveAsControls[] = { { (UINT)-1, iszFileSaveTitle }, // -1 means the dialog itself { stc2, iszSaveAsType }, { IDOK, iszFileSaveButton }, { stc4, iszFileSaveIn } }; void InitSaveAsControls( HWND hDlg) { for (UINT iControl = 0; iControl < ARRAYSIZE(aSaveAsControls); iControl++) { HWND hwnd = hDlg; TCHAR szText[80]; if (aSaveAsControls[iControl].idControl != -1) { hwnd = GetDlgItem(hDlg, aSaveAsControls[iControl].idControl); } CDLoadString(g_hinst, aSaveAsControls[iControl].idString, szText, ARRAYSIZE(szText)); SetWindowText(hwnd, szText); } } //////////////////////////////////////////////////////////////////////////// // // GetControlsArea // // Returns the leftmost edge and bottom-most edge of the // controls farthest right and down (in screen coordinates). // //////////////////////////////////////////////////////////////////////////// void GetControlsArea( HWND hDlg, HWND hwndExclude, HWND hwndGrip, POINT *pPtSize, LPINT pTop) { RECT rc; HWND hwnd; int uBottom; int uRight; uBottom = 0x80000000; uRight = 0x80000000; for (hwnd = GetWindow(hDlg, GW_CHILD); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT)) { // // Note we cannot use IsWindowVisible, since the parent is not visible. // We do not want the magic static to be included. // if (!IsVisible(hwnd) || (hwnd == hwndExclude) || (hwnd == hwndGrip)) { continue; } GetWindowRect(hwnd, &rc); if (uRight < rc.right) { uRight = rc.right; } if (uBottom < rc.bottom) { uBottom = rc.bottom; } } GetWindowRect(hDlg, &rc); pPtSize->x = uRight - rc.left; pPtSize->y = uBottom - rc.top; if (pTop) *pTop = rc.top; } // Initializes the Look In Drop Down combobox BOOL CFileOpenBrowser::InitLookIn(HWND hDlg) { TCHAR szScratch[MAX_PATH]; LPITEMIDLIST pidl; IShellFolder *psf; HWND hCtrl = GetDlgItem(hDlg, cmb2); // Add the History Location. if (_iVersion >= OPENFILEVERSION_NT5) { int iImage, iSelectedImage; if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_RECENT, &pidl))) { LPITEMIDLIST pidlLast; IShellFolder *psfParent; HRESULT hr = CDBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psfParent), (LPCITEMIDLIST *)&pidlLast); if (SUCCEEDED(hr)) { DWORD dwAttribs = SHGetAttributes(psfParent, pidlLast, SFGAO_STORAGECAPMASK | SFGAO_SHARE); //Get the image corresponding to this pidl iImage = SHMapPIDLToSystemImageListIndex(psfParent, pidlLast, &iSelectedImage); hr = psfParent->BindToObject(pidlLast, NULL, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { MYLISTBOXITEM *pItem = new MYLISTBOXITEM(); if (pItem) { BOOL bAdded = FALSE; if (pItem->Init(GetDlgItem(_hwndDlg, cmb2), psf, pidl, 0, MLBI_PERMANENT, dwAttribs, iImage, iSelectedImage)) { DisplayNameOf(psfParent, pidlLast, SHGDN_INFOLDER, szScratch, ARRAYSIZE(szScratch)); if (InsertItem(hCtrl, 0, pItem, szScratch)) { //Update the index of Desktop in Look In dropdown from 0 to 1 _iNodeDesktop = 1; bAdded = TRUE; } } if (!bAdded) { pItem->Release(); } } psf->Release(); } psfParent->Release(); } SHFree(pidl); } } BOOL bRet = FALSE; // Insert the Desktop in the Lookin dropdown if (SUCCEEDED(SHGetDesktopFolder(&psf))) { pidl = SHCloneSpecialIDList(hDlg, CSIDL_DESKTOP, FALSE); if (pidl) { // Add the desktop item MYLISTBOXITEM *pItem = new MYLISTBOXITEM(); if (pItem) { if (pItem->Init(GetDlgItem(_hwndDlg, cmb2), NULL, psf, pidl, 0, MLBI_PERMANENT, _pScheduler)) { GetViewItemText(psf, NULL, szScratch, ARRAYSIZE(szScratch)); if (InsertItem(hCtrl, _iNodeDesktop, pItem, szScratch)) { pItem->AddRef(); _pCurrentLocation = pItem; bRet = TRUE; } } pItem->Release(); } SHFree(pidl); } psf->Release(); } if (!bRet) { ClearListbox(hCtrl); } return bRet; } // Main initialization (WM_INITDIALOG phase). BOOL InitLocation(HWND hDlg, LPOFNINITINFO poii) { HWND hCtrl = GetDlgItem(hDlg, cmb2); LPOPENFILENAME lpOFN = poii->lpOFI->pOFN; BOOL fIsSaveAs = poii->bSave; POINT ptSize; GetControlsArea(hDlg, NULL, NULL, &ptSize, NULL); CFileOpenBrowser *pDlgStruct = new CFileOpenBrowser(hDlg, FALSE); if (pDlgStruct == NULL) { StoreExtendedError(CDERR_INITIALIZATION); return FALSE; } StoreBrowser(hDlg, pDlgStruct); if ((poii->lpOFI->iVersion < OPENFILEVERSION_NT5) && (poii->lpOFI->pOFN->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))) { pDlgStruct->_iVersion = OPENFILEVERSION_NT4; } //See if we need to use dropdown combobox or edit box for filename if (pDlgStruct->_iVersion >= OPENFILEVERSION_NT5) { pDlgStruct->EnableFileMRU(!IsRestricted(REST_NOFILEMRU)); } else { pDlgStruct->EnableFileMRU(FALSE); } pDlgStruct->CreateToolbar(); GetControlsArea(hDlg, NULL, NULL, &ptSize, &pDlgStruct->_topOrig); if (!pDlgStruct->InitLookIn(hDlg)) { StoreExtendedError(CDERR_INITIALIZATION); return FALSE; } pDlgStruct->_pOFN = lpOFN; pDlgStruct->_bSave = fIsSaveAs; pDlgStruct->_pOFI = poii->lpOFI; pDlgStruct->_pszDefExt.TSStrCpy(lpOFN->lpstrDefExt); // // Here follows all the caller-parameter-based initialization. // pDlgStruct->_lpOKProc = (WNDPROC)::SetWindowLongPtr(::GetDlgItem(hDlg, IDOK), GWLP_WNDPROC, (LONG_PTR)OKSubclass); if (lpOFN->Flags & OFN_CREATEPROMPT) { lpOFN->Flags |= (OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST); } else if (lpOFN->Flags & OFN_FILEMUSTEXIST) { lpOFN->Flags |= OFN_PATHMUSTEXIST; } // // We need to make sure the Ansi flags are up to date. // if (poii->lpOFI->ApiType == COMDLG_ANSI) { poii->lpOFI->pOFNA->Flags = lpOFN->Flags; } // // Limit the text to the maximum path length instead of limiting it to // the buffer length. This allows users to type ..\..\.. and move // around when the app gives an extremely small buffer. // if (pDlgStruct->_bUseCombo) { SendDlgItemMessage(hDlg, cmb13, CB_LIMITTEXT, MAX_PATH -1, 0); } else { SendDlgItemMessage(hDlg, edt1, EM_LIMITTEXT, MAX_PATH - 1, 0); } SendDlgItemMessage(hDlg, cmb2, CB_SETEXTENDEDUI, 1, 0); SendDlgItemMessage(hDlg, cmb1, CB_SETEXTENDEDUI, 1, 0); // // Save original directory for later restoration, if necessary. // pDlgStruct->_szStartDir[0] = TEXT('\0'); GetCurrentDirectory(ARRAYSIZE(pDlgStruct->_szStartDir), pDlgStruct->_szStartDir); // // Initialize all provided filters. // if (lpOFN->lpstrCustomFilter && *lpOFN->lpstrCustomFilter) { SendDlgItemMessage(hDlg, cmb1, CB_INSERTSTRING, 0, (LONG_PTR)lpOFN->lpstrCustomFilter); SendDlgItemMessage(hDlg, cmb1, CB_SETITEMDATA, 0, (LPARAM)(lpOFN->lpstrCustomFilter + lstrlen(lpOFN->lpstrCustomFilter) + 1)); SendDlgItemMessage(hDlg, cmb1, CB_LIMITTEXT, (WPARAM)(lpOFN->nMaxCustFilter), 0L); } else { // // Given no custom filter, the index will be off by one. // if (lpOFN->nFilterIndex != 0) { lpOFN->nFilterIndex--; } } // // Listed filters next. // if (lpOFN->lpstrFilter) { if (lpOFN->nFilterIndex > InitFilterBox(hDlg, lpOFN->lpstrFilter)) { lpOFN->nFilterIndex = 0; } } else { lpOFN->nFilterIndex = 0; } // // If an entry exists, select the one indicated by nFilterIndex. // if ((lpOFN->lpstrFilter) || (lpOFN->lpstrCustomFilter && *lpOFN->lpstrCustomFilter)) { HWND hCmb1 = GetDlgItem(hDlg, cmb1); ComboBox_SetCurSel(hCmb1, lpOFN->nFilterIndex); pDlgStruct->RefreshFilter(hCmb1); } //Check if this Object Open Dialog if (lpOFN->Flags & OFN_ENABLEINCLUDENOTIFY) { //Yes, change the text so that it looks like a object open TCHAR szTemp[256]; //Change the File &Name: to Object &Name: CDLoadString((HINSTANCE)g_hinst, iszObjectName, (LPTSTR)szTemp, ARRAYSIZE(szTemp)); SetWindowText(GetDlgItem(hDlg, stc3), szTemp); //Change the Files of &type: to Objects of &type: CDLoadString((HINSTANCE)g_hinst, iszObjectType, (LPTSTR)szTemp, ARRAYSIZE(szTemp)); SetWindowText(GetDlgItem(hDlg, stc2), szTemp); } // // Make sure to do this before checking if there is a title specified. // if (fIsSaveAs) { // // Note we can do this even if there is a hook/template. // InitSaveAsControls(hDlg); // In Save As Dialog there is no need for Open As Read Only. HideControl(hDlg, chx1); } if (lpOFN->lpstrTitle && *lpOFN->lpstrTitle) { SetWindowText(hDlg, lpOFN->lpstrTitle); } // BOOL Variables to check whether both the Hide Read only and Help button // are being hidden. if so we need to readjust the dialog to reclaim the space // occupied by these two controls BOOL fNoReadOnly = FALSE; BOOL fNoHelp = FALSE; if (lpOFN->Flags & OFN_HIDEREADONLY) { HideControl(hDlg, chx1); fNoReadOnly = TRUE; } else { CheckDlgButton(hDlg, chx1, (lpOFN->Flags & OFN_READONLY) ? 1 : 0); } if (!(lpOFN->Flags & OFN_SHOWHELP)) { HideControl(hDlg, pshHelp); fNoHelp = TRUE; } if (fNoReadOnly && fNoHelp) { //Readjust the dialog to reclaim space occupied by the Open as Read Only and Help Button controls pDlgStruct->ReAdjustDialog(); } RECT rc; ::GetClientRect(hDlg, &rc); // // If sizing is enabled, then we need to create the sizing grip. // if (pDlgStruct->_bEnableSizing = poii->bEnableSizing) { pDlgStruct->_hwndGrip = CreateWindow(TEXT("Scrollbar"), NULL, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_GROUP | WS_CLIPCHILDREN | SBS_BOTTOMALIGN | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, rc.right - g_cxGrip, rc.bottom - g_cyGrip, g_cxGrip, g_cyGrip, hDlg, (HMENU)-1, g_hinst, NULL); } if (!pDlgStruct->CreateHookDialog(&ptSize)) { return FALSE; } // Create Placebar right after Creating Hook Dialog as we need to get information // from the Hook Procedure if any customization needs to be done if ((pDlgStruct->_iVersion >= OPENFILEVERSION_NT5) && (!IsRestricted(REST_NOPLACESBAR)) && (!IS_NEW_OFN(lpOFN) || !(lpOFN->FlagsEx & OFN_EX_NOPLACESBAR)) ) { pDlgStruct->_hwndPlacesbar = pDlgStruct->CreatePlacesbar(pDlgStruct->_hwndDlg); } else { pDlgStruct->_hwndPlacesbar = NULL; } GetWindowRect(pDlgStruct->_hwndDlg, &rc); pDlgStruct->_ptMinTrack.x = rc.right - rc.left; pDlgStruct->_ptMinTrack.y = rc.bottom - rc.top; if (pDlgStruct->_bUseCombo) { HWND hwndComboBox = GetDlgItem(hDlg, cmb13); if (hwndComboBox) { HWND hwndEdit = (HWND)SendMessage(hwndComboBox, CBEM_GETEDITCONTROL, 0, 0L); AutoComplete(hwndEdit, &(pDlgStruct->_pcwd), 0); // // Explicitly set the focus since this is no longer the first item // in the dialog template and it will start AutoComplete. // SetFocus(hwndComboBox); } } else { HWND hwndEdit = GetDlgItem(hDlg, edt1); if (hwndEdit) { AutoComplete(hwndEdit, &(pDlgStruct->_pcwd), 0); // // Explicitly set the focus since this is no longer the first item // in the dialog template and it will start AutoComplete. // SetFocus(hwndEdit); } } // Before jumping to a particular directory, Create the travel log Create_TravelLog(&pDlgStruct->_ptlog); // jump to the first ShellFolder LPCTSTR lpInitialText = pDlgStruct->JumpToInitialLocation(lpOFN->lpstrInitialDir, lpOFN->lpstrFile); // make sure we jumped somewhere. if (!pDlgStruct->_psv) { // // This would be very bad. // // DO NOT CALL StoreExtendedError() here! Corel Envoy relies // on receiving exactly FNERR_INVALIDFILENAME when it passes // an invalid filename. // ASSERT(GetStoredExtendedError()); return FALSE; } // // Read the cabinet state. If the full title is enabled, then add // the tooltip. Otherwise, don't bother as they obviously don't care. // CABINETSTATE cCabState; // // Will set defaults if cannot read registry. // ReadCabinetState(&cCabState, SIZEOF(cCabState)); if (cCabState.fFullPathTitle) { pDlgStruct->_hwndTips = CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP | WS_GROUP | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hDlg, NULL, ::g_hinst, NULL); if (pDlgStruct->_hwndTips) { TOOLINFO ti; ti.cbSize = sizeof(ti); ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; ti.hwnd = hDlg; ti.uId = (UINT_PTR)hCtrl; ti.hinst = NULL; ti.lpszText = LPSTR_TEXTCALLBACK; SendMessage(pDlgStruct->_hwndTips, TTM_ADDTOOL, 0, (LPARAM)&ti); } } // // Show the window after creating the ShellView so we do not get a // big ugly gray spot. // if we have cached in the size of previously opened dialog then use // the size and position of that window. if (pDlgStruct->_bEnableSizing && (g_rcDlg.right > g_rcDlg.left)) { ::SetWindowPos(hDlg, NULL, g_rcDlg.left, g_rcDlg.top, g_rcDlg.right - g_rcDlg.left, g_rcDlg.bottom - g_rcDlg.top, 0); } else { ::ShowWindow(hDlg, SW_SHOW); ::UpdateWindow(hDlg); } if (lpInitialText) { // // This is the one time I will show a file spec, since it would be // too strange to have "All Files" showing in the Type box, while // only text files are in the view. // pDlgStruct->SetEditFile(lpInitialText, NULL, pDlgStruct->_fShowExtensions, FALSE); SelectEditText(hDlg); } return TRUE; } BOOL _IsValidPathComDlg(LPCTSTR pszPath) { TCHAR szPath[MAX_PATH]; BOOL bRet = FALSE; if (SUCCEEDED(StringCchCopy(szPath, ARRAYSIZE(szPath), pszPath))) { int nFileOffset = ParseFileNew(szPath, NULL, FALSE, TRUE); // // Is the filename valid? // bRet = ((nFileOffset >= 0) || (nFileOffset == PARSE_EMPTYSTRING)); } return bRet; } BOOL CFileOpenBrowser::_IsRestrictedDrive(LPCTSTR pszPath, LPCITEMIDLIST pidl) { TCHAR szDrivePath[5]; // Don't need much... just want a drive letter. BOOL bRet = FALSE; DWORD dwRest = SHRestricted(REST_NOVIEWONDRIVE); if (dwRest) { // There are some drive restrictions. // Convert pidl, if supplied, to full path. if (pidl) { if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szDrivePath, ARRAYSIZE(szDrivePath), NULL))) { pszPath = szDrivePath; } } if (pszPath) { int iDrive = PathGetDriveNumber(pszPath); if (iDrive != -1) { // is the drive restricted if (dwRest & (1 << iDrive)) { bRet = TRUE; } } } } return bRet; } // When the dialog first appears, we want to prevent the the pop message that appears from // CFSFolder if you try to navigate to a drive that has been restricted due to group policy. // So in these cases, we do the group policy check before attempting to navigate there. void CFileOpenBrowser::JumpToLocationIfUnrestricted(LPCTSTR pszPath, LPCITEMIDLIST pidl, BOOL bTranslate) { if (!_IsRestrictedDrive(pszPath, pidl)) { if (pszPath) { JumpToPath(pszPath, bTranslate); } else if (pidl) { JumpToIDList(pidl, bTranslate); } } } LPCTSTR CFileOpenBrowser::JumpToInitialLocation(LPCTSTR pszDir, LPTSTR pszFile) { // // Check out if the filename contains a path. If so, override whatever // is contained in pszDir. Chop off the path and put up only // the filename. // TCHAR szDir[MAX_PATH]; LPCTSTR pszRet = NULL; BOOL fFileIsTemp = PathIsTemporary(pszFile); szDir[0] = 0; //If we have a Directory specified then use that Directory. if (pszDir) { ExpandEnvironmentStrings(pszDir, szDir, ARRAYSIZE(szDir)); } //Check to see if the pszFile contains a Path. if (pszFile && *pszFile) { // clean up the path a little PathRemoveBlanks(pszFile); // WARNING - this must me some kind of APPCOMPAT thing - ZekeL - 13-AUG-98 // Apps that are not UNC-aware often pass and // we want to change it to the prettier <\\server\share>. - raymondc if (DBL_BSLASH(pszFile + 2) && (*(pszFile + 1) == CHAR_COLON)) { StringCopyOverlap(pszFile, pszFile + 2); } pszRet = PathFindFileName(pszFile); if (_IsValidPathComDlg(pszFile)) { if (IsWild(pszRet)) { SetCurrentFilter(pszRet); } if (!fFileIsTemp) { DWORD cch = pszRet ? (unsigned long) (pszRet-pszFile) : ARRAYSIZE(szDir); cch = min(cch, ARRAYSIZE(szDir)); // this will null terminate for us on // the backslash if pszRet was true StringCchCopy(szDir, cch, pszFile); // Don't check return value. Truncation is desired in the case when pszRet != NULL } } else if (!(_pOFN->Flags & OFN_NOVALIDATE)) { // Failed validation and app wanted validation StoreExtendedError(FNERR_INVALIDFILENAME); return NULL; } else { // Failed validation but app suppressed validation, // so continue onward with the "filename" part of the // pszFile (even though it's not valid). } } // if we have a directory then use that directory if (*szDir) { JumpToLocationIfUnrestricted(szDir, NULL, TRUE); } // See if this application contains a entry in the registry for the last visited Directory if (!_psv) { // Change the return value to full incoming name. if (!fFileIsTemp) pszRet = pszFile; if (GetPathFromLastVisitedMRU(szDir, ARRAYSIZE(szDir))) { JumpToLocationIfUnrestricted(szDir, NULL, TRUE); } } // Try Current Directory if (!_psv) { //Does current directory contain any files that match the filter ? if (GetCurrentDirectory(ARRAYSIZE(szDir), szDir) && !PathIsTemporary(szDir) && FoundFilterMatch(_szLastFilter, IsVolumeLFN(NULL))) { //Yes. Jump to Current Directory. JumpToLocationIfUnrestricted(szDir, NULL, TRUE); } } // Try My Documents if (!_psv) { LPITEMIDLIST pidl; if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl))) { JumpToLocationIfUnrestricted(NULL, pidl, FALSE); ILFree(pidl); } } // finally try the desktop - don't check for restriction here. if (!_psv) { ITEMIDLIST idl = { 0 }; // Do not try to translate this. JumpToIDList(&idl, FALSE); } // If nothing worked, then set the error code so our parent knows. if (!_psv) { StoreExtendedError(CDERR_INITIALIZATION); } //Add the initial directory where we jumped to the travel log if (_ptlog && _pCurrentLocation && _pCurrentLocation->pidlFull) { _ptlog->AddEntry(_pCurrentLocation->pidlFull); } return pszRet; } //////////////////////////////////////////////////////////////////////////// // // _CleanupDialog // // Dialog cleanup, memory deallocation. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::_CleanupDialog(BOOL fRet) { ASSERT(!_cRefCannotNavigate); if (_pOFN->lpstrCustomFilter) { UINT len = lstrlen(_pOFN->lpstrCustomFilter) + 1; UINT sCount = lstrlen(_szLastFilter); if (_pOFN->nMaxCustFilter > sCount + len) { EVAL(SUCCEEDED(StringCchCopy(_pOFN->lpstrCustomFilter + len, _pOFN->nMaxCustFilter - len, _szLastFilter))); } } if ((fRet == TRUE) && _hSubDlg && (CD_SendOKNotify(_hSubDlg, _hwndDlg, _pOFN, _pOFI) || CD_SendOKMsg(_hSubDlg, _pOFN, _pOFI))) { // Give the hook a chance to validate the file name. return; } // We need to make sure the IShellBrowser is still around during // destruction. if (_psv) { _psv->DestroyViewWindow(); ATOMICRELEASE(_psv); } if (((_pOFN->Flags & OFN_NOCHANGEDIR) || g_bUserPressedCancel) && (*_szStartDir)) { SetCurrentDirectory(_szStartDir); } ::EndDialog(_hwndDlg, fRet); } //////////////////////////////////////////////////////////////////////////// // // GetParentItem // // Given an item index in the location dropdown, get its parent item. // //////////////////////////////////////////////////////////////////////////// MYLISTBOXITEM *GetParentItem(HWND hwndCombo, int *piItem) { int iItem = *piItem; MYLISTBOXITEM *pItem = GetListboxItem(hwndCombo, iItem); if (pItem) { for (--iItem; iItem >= 0; iItem--) { MYLISTBOXITEM *pPrev = GetListboxItem(hwndCombo, iItem); if (pPrev && pPrev->cIndent < pItem->cIndent) { *piItem = iItem; return (pPrev); } } } return (NULL); } //////////////////////////////////////////////////////////////////////////// // // GetFullPathEnumCB // //////////////////////////////////////////////////////////////////////////// BOOL GetFullPathEnumCB( CFileOpenBrowser *that, LPCITEMIDLIST pidl, LPARAM lParam) { if (pidl) { LPITEMIDLIST pidlFull = ILCombine(that->_pCurrentLocation->pidlFull, pidl); if (pidlFull) { SHGetPathFromIDList(pidlFull, (LPTSTR)lParam); ILFree(pidlFull); } return FALSE; } return TRUE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::GetFullPath // // Calculate the full path to the selected object in the view. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::GetFullPath( LPTSTR pszBuf) { *pszBuf = CHAR_NULL; EnumItemObjects(SVGIO_SELECTION, GetFullPathEnumCB, (LPARAM)pszBuf); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::RemoveOldPath // // Removes old path elements from the location dropdown. *piNewSel is the // listbox index of the leaf item which the caller wants to save. All non- // permanent items that are not ancestors of that item are deleted. The // index is updated appropriately if any items before it are deleted. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::RemoveOldPath( int *piNewSel) { HWND hwndCombo = ::GetDlgItem(_hwndDlg, cmb2); int iStart = *piNewSel; int iItem; UINT cIndent = 0; int iSubOnDel = 0; // // Flush all non-permanent non-ancestor items before this one. // for (iItem = ComboBox_GetCount(hwndCombo) - 1; iItem >= 0; --iItem) { MYLISTBOXITEM *pItem = GetListboxItem(hwndCombo, iItem); if (iItem == iStart) { // // Begin looking for ancestors and adjusting the sel position. // iSubOnDel = 1; cIndent = pItem->cIndent; continue; } if (pItem->cIndent < cIndent) { // // We went back a level, so this must be an ancestor of the // selected item. // cIndent = pItem->cIndent; continue; } // // Make sure to check this after adjusting cIndent. // if (pItem->dwFlags & MLBI_PERMANENT) { continue; } SendMessage(hwndCombo, CB_DELETESTRING, iItem, NULL); pItem->Release(); *piNewSel -= iSubOnDel; } } //////////////////////////////////////////////////////////////////////////// // // FindLocation // // Given a listbox item, find the index. // Just a linear search, but we shouldn't have more than ~10-20 items. // //////////////////////////////////////////////////////////////////////////// int FindLocation( HWND hwndCombo, MYLISTBOXITEM *pFindItem) { int iItem; for (iItem = ComboBox_GetCount(hwndCombo) - 1; iItem >= 0; --iItem) { MYLISTBOXITEM *pItem = GetListboxItem(hwndCombo, iItem); if (pItem == pFindItem) { break; } } return (iItem); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnSelChange // // Process the selection change in the location dropdown. // // Chief useful feature is that it removes the items for the old path. // Returns TRUE only if it was possible to switch to the specified item. // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::OnSelChange( int iItem, BOOL bForceUpdate) { HWND hwndCombo = GetDlgItem(_hwndDlg, cmb2); BOOL bRet = TRUE; if (iItem == -1) { iItem = (int) SendMessage(hwndCombo, CB_GETCURSEL, NULL, NULL); } MYLISTBOXITEM *pNewLocation = GetListboxItem(hwndCombo, iItem); MYLISTBOXITEM *pOldLocation = _pCurrentLocation; BOOL bFirstTry = TRUE; BOOL bSwitchedBack = FALSE; if (bForceUpdate || (pNewLocation != pOldLocation)) { FOLDERSETTINGS fs; if (_psv) { _psv->GetCurrentInfo(&fs); } else { fs.ViewMode = FVM_LIST; fs.fFlags = _pOFN->Flags & OFN_ALLOWMULTISELECT ? 0 : FWF_SINGLESEL; } // we always want the recent folder to come up // in details mode // We also want the My Pictures folder and it's subfolders to comeup in ThumbView. // So, let's detect if the current and new locations are any of these special folders. LOCTYPE NewLocType = (pNewLocation ? _GetLocationType(pNewLocation) : LOCTYPE_OTHERS); LOCTYPE CurLocType = (_pCurrentLocation ? _GetLocationType(_pCurrentLocation) : LOCTYPE_OTHERS); const SHELLVIEWID *pvid = NULL; //Most of the time this will continue to be null; SHELLVIEWID vidCurrent = {0}; BOOL fUseDefaultView = FALSE; switch (NewLocType) { case LOCTYPE_MYPICTURES_FOLDER: if (CurLocType == LOCTYPE_MYPICTURES_FOLDER) { IShellView2 *psv2; //We need to get the current pvid //Note: the end-user could have changed this view. pvid = &VID_Thumbnails; //Assume this by default. if (SUCCEEDED(_psv->QueryInterface(IID_PPV_ARG(IShellView2, &psv2)))) { if (SUCCEEDED(psv2->GetView(&vidCurrent, SV2GV_CURRENTVIEW))) pvid = &vidCurrent; psv2->Release(); } } else { //We are moving to My pictures folder or sub-folder; set the thumb nail view. pvid = &VID_Thumbnails; //If we are moving from other folders, save the ViewMode. if (CurLocType == LOCTYPE_OTHERS) { _CachedViewMode = fs.ViewMode; _fCachedViewFlags = fs.fFlags; } } break; case LOCTYPE_RECENT_FOLDER: //We are moving to Recent folder. if (CurLocType == LOCTYPE_OTHERS) { _CachedViewMode = fs.ViewMode; _fCachedViewFlags = fs.fFlags; } fs.ViewMode = FVM_DETAILS; break; case LOCTYPE_WIA_FOLDER: if (CurLocType == LOCTYPE_OTHERS) { _CachedViewMode = fs.ViewMode; _fCachedViewFlags = fs.fFlags; } // ask view for default view for WIA extentions fUseDefaultView = TRUE; break; case LOCTYPE_OTHERS: //Check if we are coming from Recent, My Pictures, or WIA folders, // and restore the viewmode we had before that. if (CurLocType != LOCTYPE_OTHERS) { fs.ViewMode = _CachedViewMode; fs.fFlags = _fCachedViewFlags; } break; } _iCurrentLocation = iItem; _pCurrentLocation = pNewLocation; OnSelChange_TryAgain: if (!_pCurrentLocation || FAILED(SwitchView(_pCurrentLocation->GetShellFolder(), _pCurrentLocation->pidlFull, &fs, pvid, fUseDefaultView))) { // // We could not create the view for this location. // bRet = FALSE; // // Try the previous folder. // if (bFirstTry) { bFirstTry = FALSE; _pCurrentLocation = pOldLocation; int iOldItem = FindLocation(hwndCombo, pOldLocation); if (iOldItem >= 0) { _iCurrentLocation = iOldItem; ComboBox_SetCurSel(hwndCombo, _iCurrentLocation); if (_psv) { bSwitchedBack = TRUE; goto SwitchedBack; } else { goto OnSelChange_TryAgain; } } } // // Try the parent of the old item. // if (_iCurrentLocation) { _pCurrentLocation = GetParentItem(hwndCombo, &_iCurrentLocation); if (_pCurrentLocation) { ComboBox_SetCurSel(hwndCombo, _iCurrentLocation); goto OnSelChange_TryAgain; } } // // We cannot create the Desktop view. I think we are in // real trouble. We had better bail out. // StoreExtendedError(CDERR_DIALOGFAILURE); _CleanupDialog(FALSE); return FALSE; } //if _iCurrentLocation is _iNodeDesktop then it means we are at Desktop so disable the IDC_PARENT button ::SendMessage(_hwndToolbar, TB_SETSTATE, IDC_PARENT, ((_iCurrentLocation == _iNodeDesktop) || (_iCurrentLocation == 0)) ? 0 :TBSTATE_ENABLED); if (_IsSaveContainer(_pCurrentLocation->dwAttrs)) { _pCurrentLocation->SwitchCurrentDirectory(_pcwd); } TCHAR szFile[MAX_PATH + 1]; int nFileOffset; // // We've changed folders; we'd better strip whatever is in the edit // box down to the file name. // if (_bUseCombo) { HWND hwndEdit = (HWND)SendMessage(GetDlgItem(_hwndDlg, cmb13), CBEM_GETEDITCONTROL, 0, 0L); GetWindowText(hwndEdit, szFile, ARRAYSIZE(szFile)); } else { GetDlgItemText(_hwndDlg, edt1, szFile, ARRAYSIZE(szFile)); } nFileOffset = ParseFileNew(szFile, NULL, FALSE, TRUE); if (nFileOffset > 0 && !IsDirectory(szFile)) { // // The user may have typed an extension, so make sure to show it. // SetEditFile(szFile + nFileOffset, NULL, TRUE); } SetSaveButton(iszFileSaveButton); SwitchedBack: RemoveOldPath(&_iCurrentLocation); } if (!bSwitchedBack && _hSubDlg) { CD_SendFolderChangeNotify(_hSubDlg, _hwndDlg, _pOFN, _pOFI); } return (bRet); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnDotDot // // Process the open-parent-folder button on the toolbar. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::OnDotDot() { HWND hwndCombo = GetDlgItem(_hwndDlg, cmb2); int iItem = ComboBox_GetCurSel(hwndCombo); MYLISTBOXITEM *pItem = GetParentItem(hwndCombo, &iItem); SendMessage(hwndCombo, CB_SETCURSEL, iItem, NULL); // // Delete old path from combo. // OnSelChange(); UpdateNavigation(); } //////////////////////////////////////////////////////////////////////////// // // DblClkEnumCB // //////////////////////////////////////////////////////////////////////////// #define PIDL_NOTHINGSEL (LPCITEMIDLIST)0 #define PIDL_MULTIPLESEL (LPCITEMIDLIST)-1 #define PIDL_FOLDERSEL (LPCITEMIDLIST)-2 BOOL DblClkEnumCB( CFileOpenBrowser *that, LPCITEMIDLIST pidl, LPARAM lParam) { MYLISTBOXITEM *pLoc = that->_pCurrentLocation; LPCITEMIDLIST *ppidl = (LPCITEMIDLIST *)lParam; if (!pidl) { pidl = *ppidl; if (pidl == PIDL_NOTHINGSEL) { // // Nothing selected. // return FALSE; } if (pidl == PIDL_MULTIPLESEL) { // // More than one thing selected. // return FALSE; } // check if the pidl is a container (ie, a folder) if (IsContainer(that->_psfCurrent, pidl)) { LPITEMIDLIST pidlDest = ILCombine(pLoc->pidlFull,pidl); if (pidlDest) { that->JumpToIDList(pidlDest); SHFree(pidlDest); } *ppidl = PIDL_FOLDERSEL; } else if (IsLink(that->_psfCurrent,pidl)) { // // This link might be pointing to a folder in which case // we want to go ahead and open it. If the link points // to a file then its taken care of in ProcessEdit command. // SHTCUTINFO info; LPITEMIDLIST pidlLinkTarget = NULL; info.dwAttr = SFGAO_FOLDER; info.fReSolve = FALSE; info.pszLinkFile = NULL; info.cchFile = 0; info.ppidl = &pidlLinkTarget; //psf can be NULL in which case ResolveLink uses _psfCurrent IShellFolder if (SUCCEEDED(that->ResolveLink(pidl, &info, that->_psfCurrent))) { if (info.dwAttr & SFGAO_FOLDER) { that->JumpToIDList(pidlLinkTarget); *ppidl = PIDL_FOLDERSEL; } Pidl_Set(&pidlLinkTarget, NULL); } } return FALSE; } if (*ppidl) { // // More than one thing selected. // *ppidl = PIDL_MULTIPLESEL; return FALSE; } *ppidl = pidl; return TRUE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnDblClick // // Process a double-click in the view control, either by choosing the // selected non-container object or by opening the selected container. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::OnDblClick( BOOL bFromOKButton) { LPCITEMIDLIST pidlFirst = PIDL_NOTHINGSEL; //if we have a saved pidl then use it instead if (_pidlSelection && _ProcessPidlSelection()) { return; } if (_psv) { EnumItemObjects(SVGIO_SELECTION, DblClkEnumCB, (LPARAM)&pidlFirst); } if (pidlFirst == PIDL_NOTHINGSEL) { // // Nothing selected. // if (bFromOKButton) { // // This means we got an IDOK when the focus was in the view, // but nothing was selected. Let's get the edit text and go // from there. // ProcessEdit(); } } else if (pidlFirst != PIDL_FOLDERSEL) { // // This will change the edit box, but that's OK, since it probably // already has. This should take care of files with no extension. // SelFocusChange(TRUE); // // This part will take care of resolving links. // ProcessEdit(); } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::JumpToPath // // Refocus the entire dialog on a different directory. // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::JumpToPath(LPCTSTR pszDirectory, BOOL bTranslate) { TCHAR szTemp[MAX_PATH + 1]; TCHAR szCurDir[MAX_PATH + 1]; BOOL bRet = FALSE; // // This should do the whole job of canonicalizing the directory. // GetCurrentDirectory(ARRAYSIZE(szCurDir), szCurDir); if (PathCombine(szTemp, szCurDir, pszDirectory)) { LPITEMIDLIST pidlNew = ILCreateFromPath(szTemp); if (pidlNew) { // // Need to make sure the pidl points to a folder. If not, then remove // items from the end until we find one that is. // This must be done before the translation. // DWORD dwAttrib; do { dwAttrib = SFGAO_FOLDER; SHGetAttributesOf(pidlNew, &dwAttrib); if (!(dwAttrib & SFGAO_FOLDER)) { ILRemoveLastID(pidlNew); } } while(!(dwAttrib & SFGAO_FOLDER) && !ILIsEmpty(pidlNew)); if (!(dwAttrib & SFGAO_FOLDER)) { bRet = FALSE; } else { bRet = JumpToIDList(pidlNew, bTranslate); } SHFree(pidlNew); } } return bRet; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::JumpTOIDList // // Refocus the entire dialog on a different IDList. // // Parameter: // bTranslate specifies whether the given pidl should be translated to // logical pidl // bAddToNavStack specifies whether the pidl given for jumping should be // added to the back/forward navigation stack // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::JumpToIDList( LPCITEMIDLIST pidlNew, BOOL bTranslate, BOOL bAddToNavStack) { LPITEMIDLIST pidlLog = NULL; if (bTranslate) { // // Translate IDList's on the Desktop into the appropriate // logical IDList. // pidlLog = SHLogILFromFSIL(pidlNew); if (pidlLog) { pidlNew = pidlLog; } } // // Find the entry in the location dropdown that is the closest parent // to the new location. // HWND hwndCombo = ::GetDlgItem(_hwndDlg, cmb2); MYLISTBOXITEM *pBestParent = GetListboxItem(hwndCombo, 0); int iBestParent = 0; LPCITEMIDLIST pidlRelative = pidlNew; UINT cIndent = 0; BOOL fExact = FALSE; for (UINT iItem = 0; ; iItem++) { MYLISTBOXITEM *pNextItem = GetListboxItem(hwndCombo, iItem); if (pNextItem == NULL) { break; } if (pNextItem->cIndent != cIndent) { // // Not the depth we want. // continue; } if (ILIsEqual(pNextItem->pidlFull, pidlNew)) { // Never treat FTP Pidls as Equal because the username/password may // have changed so we need to do the navigation. The two pidls // still pass ILIsEqual() because the server name is the same. // This is required for a different back compat bug. if (!ILIsFTP(pidlNew)) fExact = TRUE; break; } LPCITEMIDLIST pidlChild = ILFindChild(pNextItem->pidlFull, pidlNew); if (pidlChild != NULL) { pBestParent = pNextItem; iBestParent = iItem; cIndent++; pidlRelative = pidlChild; } } // // The path provided might have matched an existing item exactly. In // that case, just select the item. // if (fExact) { goto FoundIDList; } // // Now, pBestParent is the closest parent to the item, iBestParent is // its index, and cIndent is the next appropriate indent level. Begin // creating new items for the rest of the path. // iBestParent++; // begin inserting after parent item for (; ;) { LPITEMIDLIST pidlFirst = ILCloneFirst(pidlRelative); if (pidlFirst == NULL) { break; } MYLISTBOXITEM *pNewItem = new MYLISTBOXITEM(); if (pNewItem) { if (!pNewItem->Init(GetDlgItem(_hwndDlg, cmb2), pBestParent, pBestParent->GetShellFolder(), pidlFirst, cIndent, MLBI_PSFFROMPARENT, _pScheduler)) { pNewItem->Release(); pNewItem = NULL; //iBestParent is off by 1 in error case . Correct it iBestParent--; break; } } else { //iBestParent is off by 1 in error case . Correct it iBestParent--; break; } GetViewItemText(pBestParent->psfSub, pidlFirst, _szBuf, ARRAYSIZE(_szBuf), SHGDN_NORMAL); InsertItem(hwndCombo, iBestParent, pNewItem, _szBuf); SHFree(pidlFirst); pidlRelative = ILGetNext(pidlRelative); if (ILIsEmpty(pidlRelative)) { break; } cIndent++; // next one is indented one more level iBestParent++; // and inserted after this one pBestParent = pNewItem; // and is a child of the one we just inserted } iItem = iBestParent; FoundIDList: if (pidlLog) { SHFree(pidlLog); } SendMessage(hwndCombo, CB_SETCURSEL, iItem, NULL); BOOL bRet = OnSelChange(iItem, TRUE); //Update our Navigation stack if (bRet && bAddToNavStack) { UpdateNavigation(); } //We naviagated to a new location so invalidate the cached Pidl Pidl_Set(&_pidlSelection,NULL); return bRet; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::ViewCommand // // Process the new-folder button on the toolbar. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::ViewCommand( UINT uIndex) { IContextMenu *pcm; if (SUCCEEDED(_psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pcm)))) { CMINVOKECOMMANDINFOEX ici = {0}; ici.cbSize = sizeof(ici); ici.fMask = 0L; ici.hwnd = _hwndDlg; ici.lpVerb = ::c_szCommandsA[uIndex]; ici.lpParameters = NULL; ici.lpDirectory = NULL; ici.nShow = SW_NORMAL; ici.lpParametersW = NULL; ici.lpDirectoryW = NULL; ici.lpVerbW = ::c_szCommandsW[uIndex]; ici.fMask |= CMIC_MASK_UNICODE; IObjectWithSite *pObjSite = NULL; if (SUCCEEDED(pcm->QueryInterface(IID_IObjectWithSite, (void**)&pObjSite))) { pObjSite->SetSite(SAFECAST(_psv,IShellView*)); } HMENU hmContext = CreatePopupMenu(); pcm->QueryContextMenu(hmContext, 0, 1, 256, 0); pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)(&ici)); if (pObjSite) { pObjSite->SetSite(NULL); pObjSite->Release(); } DestroyMenu(hmContext); pcm->Release(); } } // HRESULT CFileOpenBrowser::ResolveLink(LPCITEMIDLIST pidl, PSHTCUTINFO pinfo, IShellFolder *psf) { BOOL fSetPidl = TRUE; //Do we have IShellFolder passed to us ? if (!psf) { //No use our current shell folder. psf = _psfCurrent; } //Get the IShellLink interface pointer corresponding to given file IShellLink *psl; HRESULT hres = psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IShellLink, 0, &psl)); if (SUCCEEDED(hres)) { //Resolve the link if (pinfo->fReSolve) { hres = psl->Resolve(_hwndDlg, 0); //If the resolve failed then we can't get correct pidl if (hres == S_FALSE) { fSetPidl = FALSE; } } if (SUCCEEDED(hres)) { LPITEMIDLIST pidl; if (SUCCEEDED(psl->GetIDList(&pidl)) && pidl) { if (pinfo->dwAttr) hres = SHGetAttributesOf(pidl, &pinfo->dwAttr); if (SUCCEEDED(hres) && pinfo->pszLinkFile) { // caller wants the path, this may be empty hres = psl->GetPath(pinfo->pszLinkFile, pinfo->cchFile, 0, 0); } if (pinfo->ppidl && fSetPidl) *(pinfo->ppidl) = pidl; else ILFree(pidl); } else hres = E_FAIL; // gota have a pidl } psl->Release(); } if (FAILED(hres)) { if (pinfo->pszLinkFile) *pinfo->pszLinkFile = 0; if (pinfo->ppidl && *pinfo->ppidl) { ILFree(*pinfo->ppidl); *pinfo->ppidl = NULL; } pinfo->dwAttr = 0; } return hres; } // // This function checks to see if the pidl given is a link and if so resolves the // link // PARAMETERS : // // LPCITEMIDLIST pidl - the pidl which we want to check for link // LPTSTR pszLinkFile - if the pidl points to a link then this contains the resolved file // name // UINT cchFile - size of the buffer pointed by the pszLinkFile // // RETURN VALUE : // returns TRUE if the pidl is link and was able to resolve the link successfully // returns FALSE if the pidl is not link or if the link was not able to resolve successfully. // In this case pszLinkFile and pfd are not valid. BOOL CFileOpenBrowser::GetLinkStatus(LPCITEMIDLIST pidl, PSHTCUTINFO pinfo) { if (IsLink(_psfCurrent, pidl)) { return SUCCEEDED(ResolveLink(pidl, pinfo)); } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::LinkMatchSpec // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::LinkMatchSpec(LPCITEMIDLIST pidl, LPCTSTR pszSpec) { TCHAR szFile[MAX_PATH]; SHTCUTINFO info; info.dwAttr = SFGAO_FOLDER; info.fReSolve = FALSE; info.pszLinkFile = szFile; info.cchFile = ARRAYSIZE(szFile); info.ppidl = NULL; if (GetLinkStatus(pidl, &info)) { if ((info.dwAttr & SFGAO_FOLDER) || (szFile[0] && PathMatchSpec(szFile, pszSpec))) { return TRUE; } } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // MeasureDriveItems // // Standard owner-draw code for the location dropdown. // //////////////////////////////////////////////////////////////////////////// #define MINIDRIVE_MARGIN 4 #define MINIDRIVE_WIDTH (g_cxSmIcon) #define MINIDRIVE_HEIGHT (g_cySmIcon) #define DRIVELIST_BORDER 3 void MeasureDriveItems( HWND hwndDlg, MEASUREITEMSTRUCT *lpmi) { HDC hdc; HFONT hfontOld; int dyDriveItem; SIZE siz; hdc = GetDC(NULL); hfontOld = (HFONT)SelectObject(hdc, (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0)); GetTextExtentPoint(hdc, TEXT("W"), 1, &siz); dyDriveItem = siz.cy; if (hfontOld) { SelectObject(hdc, hfontOld); } ReleaseDC(NULL, hdc); dyDriveItem += DRIVELIST_BORDER; if (dyDriveItem < MINIDRIVE_HEIGHT) { dyDriveItem = MINIDRIVE_HEIGHT; } lpmi->itemHeight = dyDriveItem; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::PaintDriveLine // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::PaintDriveLine( DRAWITEMSTRUCT *lpdis) { HDC hdc = lpdis->hDC; RECT rc = lpdis->rcItem; TCHAR szText[MAX_DRIVELIST_STRING_LEN]; int offset = 0; int xString, yString, xMiniDrive, dyString; SIZE siz; if ((int)lpdis->itemID < 0) { return; } MYLISTBOXITEM *pItem = GetListboxItem(lpdis->hwndItem, lpdis->itemID); if (pItem) { // Note: don't need to call CB_GETLBTEXTLEN, we know our buffer is big enough. // The items in the combobox passed through InsertItem() ::SendDlgItemMessage(_hwndDlg, cmb2, CB_GETLBTEXT, lpdis->itemID, (LPARAM)szText); // // Before doing anything, calculate the actual rectangle for the text. // if (!(lpdis->itemState & ODS_COMBOBOXEDIT)) { offset = 10 * pItem->cIndent; } xMiniDrive = rc.left + DRIVELIST_BORDER + offset; rc.left = xString = xMiniDrive + MINIDRIVE_WIDTH + MINIDRIVE_MARGIN; GetTextExtentPoint(hdc, szText, lstrlen(szText), &siz); dyString = siz.cy; rc.right = rc.left + siz.cx; rc.left--; rc.right++; if (lpdis->itemAction != ODA_FOCUS) { FillRect(hdc, &lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW)); yString = rc.top + (rc.bottom - rc.top - dyString) / 2; SetBkColor(hdc, GetSysColor((lpdis->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHT : COLOR_WINDOW)); SetTextColor(hdc, GetSysColor((lpdis->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); if ((lpdis->itemState & ODS_COMBOBOXEDIT) && (rc.right > lpdis->rcItem.right)) { // // Need to clip as user does not! // rc.right = lpdis->rcItem.right; ExtTextOut(hdc, xString, yString, ETO_OPAQUE | ETO_CLIPPED, &rc, szText, lstrlen(szText), NULL); } else { ExtTextOut(hdc, xString, yString, ETO_OPAQUE, &rc, szText, lstrlen(szText), NULL); } ImageList_Draw(_himl, (lpdis->itemID == (UINT)_iCurrentLocation) ? pItem->iSelectedImage : pItem->iImage, hdc, xMiniDrive, rc.top + (rc.bottom - rc.top - MINIDRIVE_HEIGHT) / 2, (pItem->IsShared() ? INDEXTOOVERLAYMASK(IDOI_SHARE) : 0) | ((lpdis->itemState & ODS_SELECTED) ? (ILD_SELECTED | ILD_FOCUS | ILD_TRANSPARENT) : ILD_TRANSPARENT)); } } if (lpdis->itemAction == ODA_FOCUS || (lpdis->itemState & ODS_FOCUS)) { DrawFocusRect(hdc, &rc); } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::RefreshFilter // // Refresh the view given any change in the user's choice of wildcard // filter. // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::RefreshFilter( HWND hwndFilter) { WAIT_CURSOR w(this); _pOFN->Flags &= ~OFN_FILTERDOWN; short nIndex = (short) SendMessage(hwndFilter, CB_GETCURSEL, 0, 0L); if (nIndex < 0) { // // No current selection. // return; } BOOL bCustomFilter = _pOFN->lpstrCustomFilter && *_pOFN->lpstrCustomFilter; _pOFN->nFilterIndex = nIndex; if (!bCustomFilter) { _pOFN->nFilterIndex++; } LPTSTR lpFilter; // // Must also check if filter contains anything. // lpFilter = (LPTSTR)ComboBox_GetItemData(hwndFilter, nIndex); if (*lpFilter) { SetCurrentFilter(lpFilter); // // Provide dynamic _pszDefExt updating when lpstrDefExt is app // initialized. // if (!_bNoInferDefExt && _pOFN->lpstrDefExt) { // // We are looking for "foo*.ext[;...]". We will grab ext as the // default extension. If not of this form, use the default // extension passed in. // LPTSTR lpDot = StrChr(lpFilter, CHAR_DOT); // // Skip past the CHAR_DOT. // if (lpDot && _pszDefExt.TSStrCpy(lpDot + 1)) { LPTSTR lpSemiColon = StrChr(_pszDefExt, CHAR_SEMICOLON); if (lpSemiColon) { *lpSemiColon = CHAR_NULL; } if (IsWild(_pszDefExt)) { _pszDefExt.TSStrCpy(_pOFN->lpstrDefExt); } } else { _pszDefExt.TSStrCpy(_pOFN->lpstrDefExt); } } if (_bUseCombo) { HWND hwndEdit = (HWND)SendMessage(GetDlgItem(_hwndDlg, cmb13), CBEM_GETEDITCONTROL, 0, 0L); GetWindowText(hwndEdit, _szBuf, ARRAYSIZE(_szBuf)); } else { GetDlgItemText(_hwndDlg, edt1, _szBuf, ARRAYSIZE(_szBuf)); } if (IsWild(_szBuf)) { // // We should not show a filter that we are not using. // *_szBuf = CHAR_NULL; SetEditFile(_szBuf, NULL, TRUE); } if (_psv) { _psv->Refresh(); } } if (_hSubDlg) { if (!CD_SendTypeChangeNotify(_hSubDlg, _hwndDlg, _pOFN, _pOFI)) { CD_SendLBChangeMsg(_hSubDlg, cmb1, nIndex, CD_LBSELCHANGE, _pOFI->ApiType); } } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::GetDirectoryFromLB // // Return the dropdown's directory and its length. // Set *pichRoot to the start of the path (C:\ or \\server\share\). // // pszBuf is assumed to be at least MAX_PATH in length // //////////////////////////////////////////////////////////////////////////// UINT CFileOpenBrowser::GetDirectoryFromLB( LPTSTR pszBuf, int *pichRoot) { *pszBuf = 0; if (_pCurrentLocation->pidlFull != NULL) { GetPathFromLocation(_pCurrentLocation, pszBuf); } if (*pszBuf) { if (PathAddBackslash(pszBuf)) { LPTSTR pszBackslash = StrChr(pszBuf + 2, CHAR_BSLASH); if (pszBackslash != NULL) { // // For UNC paths, the "root" is on the next backslash. // if (DBL_BSLASH(pszBuf)) { pszBackslash = StrChr(pszBackslash + 1, CHAR_BSLASH); } UINT cchRet = lstrlen(pszBuf); *pichRoot = (pszBackslash != NULL) ? (int)(pszBackslash - pszBuf) : cchRet; return (cchRet); } } } *pichRoot = 0; return (0); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::EnumItemObjects // //////////////////////////////////////////////////////////////////////////// typedef BOOL (*EIOCALLBACK)( CFileOpenBrowser *that, LPCITEMIDLIST pidl, LPARAM lParam); BOOL CFileOpenBrowser::EnumItemObjects( UINT uItem, EIOCALLBACK pfnCallBack, LPARAM lParam) { FORMATETC fmte = { (CLIPFORMAT) g_cfCIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; BOOL bRet = FALSE; LPCITEMIDLIST pidl; LPIDA pida; int cItems, i; IDataObject *pdtobj; STGMEDIUM medium; if (!_psv || FAILED(_psv->GetItemObject(uItem, IID_PPV_ARG(IDataObject, &pdtobj)))) { goto Error0; } if (FAILED(pdtobj->GetData(&fmte, &medium))) { goto Error1; } pida = (LPIDA)GlobalLock(medium.hGlobal); cItems = pida->cidl; for (i = 1; ; ++i) { if (i > cItems) { // // We got to the end of the list without a failure. // Call back one last time with NULL. // bRet = pfnCallBack(this, NULL, lParam); break; } pidl = LPIDL_GetIDList(pida, i); if (!pfnCallBack(this, pidl, lParam)) { break; } } GlobalUnlock(medium.hGlobal); _ReleaseStgMedium(&medium); Error1: pdtobj->Release(); Error0: return (bRet); } //////////////////////////////////////////////////////////////////////////// // // FindNameEnumCB // //////////////////////////////////////////////////////////////////////////// #define FE_INVALID_VALUE 0x0000 #define FE_OUTOFMEM 0x0001 #define FE_TOOMANY 0x0002 #define FE_CHANGEDDIR 0x0003 #define FE_FILEERR 0x0004 #define FE_FOUNDNAME 0x0005 typedef struct _FINDNAMESTRUCT { LPTSTR pszFile; UINT uRet; LPCITEMIDLIST pidlFound; } FINDNAMESTRUCT; BOOL FindNameEnumCB( CFileOpenBrowser *that, LPCITEMIDLIST pidl, LPARAM lParam) { SHFILEINFO sfi; FINDNAMESTRUCT *pfns = (FINDNAMESTRUCT *)lParam; if (!pidl) { if (!pfns->pidlFound) { return FALSE; } GetViewItemText(that->_psfCurrent, pfns->pidlFound, pfns->pszFile, MAX_PATH); if (IsContainer(that->_psfCurrent, pfns->pidlFound)) { LPITEMIDLIST pidlFull = ILCombine(that->_pCurrentLocation->pidlFull, pfns->pidlFound); if (pidlFull) { if (that->JumpToIDList(pidlFull)) { pfns->uRet = FE_CHANGEDDIR; } else if (!that->_psv) { pfns->uRet = FE_OUTOFMEM; } SHFree(pidlFull); if (pfns->uRet != FE_INVALID_VALUE) { return TRUE; } } } pfns->uRet = FE_FOUNDNAME; return TRUE; } if (!SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME | SHGFI_PIDL)) { // // This will never happen, right? // return TRUE; } if (lstrcmpi(sfi.szDisplayName, pfns->pszFile) != 0) { // // Continue the enumeration. // return TRUE; } if (!pfns->pidlFound) { pfns->pidlFound = pidl; // // Continue looking for more matches. // return TRUE; } // // We already found a match, so select the first one and stop the search. // // The focus must be set to _hwndView before changing selection or // the GetItemObject may not work. // FORWARD_WM_NEXTDLGCTL(that->_hwndDlg, that->_hwndView, 1, SendMessage); that->_psv->SelectItem(pfns->pidlFound, SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED); pfns->pidlFound = NULL; pfns->uRet = FE_TOOMANY; // // Stop enumerating. // return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CDPathQualify // //////////////////////////////////////////////////////////////////////////// BOOL CDPathQualify( LPCTSTR lpFile, LPTSTR pszPathName) { BOOL bRet = FALSE; TCHAR szCurDir[MAX_PATH + 1]; // // This should do the whole job of canonicalizing the directory. // if (GetCurrentDirectory(ARRAYSIZE(szCurDir), szCurDir)) { bRet = PathCombine(pszPathName, szCurDir, lpFile) ? TRUE : FALSE; } return bRet; } //////////////////////////////////////////////////////////////////////////// // // VerifyOpen // // Returns: 0 success // !0 dos error code // //////////////////////////////////////////////////////////////////////////// int VerifyOpen( LPCTSTR lpFile, LPTSTR pszPathName) { HANDLE hf; int nError = OF_BUFFERTRUNCATED; if (CDPathQualify(lpFile, pszPathName)) { hf = CreateFile(pszPathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hf == INVALID_HANDLE_VALUE) { nError = GetLastError(); } else { CloseHandle(hf); nError = 0; } } return nError; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::IsKnownExtension // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::IsKnownExtension( LPCTSTR pszExtension) { if ((LPTSTR)_pszDefExt && lstrcmpi(pszExtension + 1, _pszDefExt) == 0) { // // It's the default extension, so no need to add it again. // return TRUE; } if (lstrcmp(_szLastFilter, szStarDotStar) == 0) { //Current Filter is *.*, so allow whatever extension user enters. return TRUE; } if (RegQueryValue(HKEY_CLASSES_ROOT, pszExtension, NULL, 0) == ERROR_SUCCESS) { // // It's a registered extension, so the user is trying to force // the type. // return TRUE; } if (_pOFN->lpstrFilter) { LPCTSTR pFilter = _pOFN->lpstrFilter; while (*pFilter) { // // Skip visual. // pFilter = pFilter + lstrlen(pFilter) + 1; // // Search extension list. // while (*pFilter) { // // Check extensions of the form '*.ext' only. // if (*pFilter == CHAR_STAR && *(++pFilter) == CHAR_DOT) { LPCTSTR pExt = pszExtension + 1; pFilter++; while (*pExt && *pExt == *pFilter) { pExt++; pFilter++; } if (!*pExt && (*pFilter == CHAR_SEMICOLON || !*pFilter)) { // // We have a match. // return TRUE; } } // // Skip to next extension. // while (*pFilter) { TCHAR ch = *pFilter; pFilter = CharNext(pFilter); if (ch == CHAR_SEMICOLON) { break; } } } // // Skip extension string's terminator. // pFilter++; } } return FALSE; } BOOL CFileOpenBrowser::_IsNoDereferenceLinks(LPCWSTR pszFile, IShellItem *psi) { if (_pOFN->Flags & OFN_NODEREFERENCELINKS) return TRUE; LPWSTR psz = NULL; if (!pszFile) { psi->GetDisplayName(SIGDN_PARENTRELATIVEPARSING, &psz); pszFile = psz; } // if the filter equals what ever we are looking at // we assume the caller is actually looking for // this file. BOOL fRet = (NULL == StrStr(_szLastFilter, TEXT(".*"))) && PathMatchSpec(pszFile, _szLastFilter); if (psz) CoTaskMemFree(psz); return fRet; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::FindNameInView // // We will only resolve a link once. If you have a link to a link, then // we will return the second link. // // If nExtOffset is non-zero, it is the offset to the character following // the dot. // // Note: pszFile buffer must be MAX_PATH in length. // //////////////////////////////////////////////////////////////////////////// #define NUM_LINKLOOPS 1 UINT CFileOpenBrowser::FindNameInView( LPTSTR pszFile, OKBUTTONFLAGS Flags, LPTSTR pszPathName, int nFileOffset, int nExtOffset, int *pnErrCode, BOOL bTryAsDir) { UINT uRet; FINDNAMESTRUCT fns = { pszFile, FE_INVALID_VALUE, NULL, }; BOOL bGetOut = TRUE; BOOL bAddExt = FALSE; BOOL bHasExt = nExtOffset; TCHAR szTemp[MAX_PATH + 1]; int nNewExt = lstrlen(pszFile); // // If no extension, point at the end of the file name. // if (!nExtOffset) { nExtOffset = nNewExt; } // // HACK: We could have a link that points to another link that points to // another link, ..., that points back to the original file. We will not // loop more than NUM_LINKLOOPS times before giving up. int nLoop = NUM_LINKLOOPS; if (Flags & (OKBUTTON_NODEFEXT | OKBUTTON_QUOTED)) { goto VerifyTheName; } if (bHasExt) { if (IsKnownExtension(pszFile + nExtOffset)) { goto VerifyTheName; } // // Don't attempt 2 extensions on SFN volume. // if (!CDPathQualify(pszFile, pszPathName)) // This can fail if we end up with something larger than MAX_PATH { *pnErrCode = OF_BUFFERTRUNCATED; return FE_FILEERR; } if (!IsLFNDrive(pszPathName)) { goto VerifyTheName; } } bGetOut = FALSE; if ((LPTSTR)_pszDefExt && ((DWORD)nNewExt + lstrlen(_pszDefExt) < _pOFN->nMaxFile)) { bAddExt = TRUE; // // Note that we check lpstrDefExt to see if they want an automatic // extension, but actually copy _pszDefExt. // if (!AppendExt(pszFile, MAX_PATH, _pszDefExt, FALSE)) { *pnErrCode = OF_BUFFERTRUNCATED; return (FE_FILEERR); // Not enough buffer room for default extension } // // So we've added the default extension. If there's a directory // that matches this name, all attempts to open/create the file // will fail, so simply change to the directory as if they had // typed it in. Note that by putting this test here, if there // was a directory without the extension, we would have already // switched to it. // VerifyTheName: // // Note that this also works for a UNC name, even on a net that // does not support using UNC's directly. It will also do the // right thing for links to things. We do not validate if we // have not dereferenced any links, since that should have // already been done. // if (bTryAsDir && SetDirRetry(pszFile, nLoop == NUM_LINKLOOPS)) { return (FE_CHANGEDDIR); } *pnErrCode = VerifyOpen(pszFile, pszPathName); if (*pnErrCode == 0 || *pnErrCode == OF_SHARINGVIOLATION) { // // This may be a link to something, so we should try to // resolve it. // if (!_IsNoDereferenceLinks(pszFile, NULL) && nLoop > 0) { --nLoop; LPITEMIDLIST pidl; IShellFolder *psf = NULL; DWORD dwAttr = SFGAO_LINK; HRESULT hRes; // // ILCreateFromPath is slow (especially on a Net path), // so just try to parse the name in the current folder if // possible. // if (nFileOffset || nLoop < NUM_LINKLOOPS - 1) { LPITEMIDLIST pidlTemp; hRes = SHILCreateFromPath(pszPathName, &pidlTemp, &dwAttr); //We are getting a pidl corresponding to a path. Get the IShellFolder corresponding to this pidl // to pass it to ResolveLink if (SUCCEEDED(hRes)) { LPCITEMIDLIST pidlLast; hRes = CDBindToIDListParent(pidlTemp, IID_PPV_ARG(IShellFolder, &psf), (LPCITEMIDLIST *)&pidlLast); if (SUCCEEDED(hRes)) { //Get the child pidl relative to the IShellFolder pidl = ILClone(pidlLast); } ILFree(pidlTemp); } } else { WCHAR wszDisplayName[MAX_PATH + 1]; ULONG chEaten; SHTCharToUnicode(pszFile, wszDisplayName , ARRAYSIZE(wszDisplayName)); hRes = _psfCurrent->ParseDisplayName(NULL, NULL, wszDisplayName, &chEaten, &pidl, &dwAttr); } if (SUCCEEDED(hRes)) { if (dwAttr & SFGAO_LINK) { SHTCUTINFO info; info.dwAttr = 0; info.fReSolve = FALSE; info.pszLinkFile = szTemp; info.cchFile = ARRAYSIZE(szTemp); info.ppidl = NULL; //psf can be NULL in which case ResolveLink uses _psfCurrent IShellFolder if (SUCCEEDED(ResolveLink(pidl, &info, psf)) && szTemp[0]) { // // It was a link, and it "dereferenced" to something, // so we should try again with that new file. // EVAL(SUCCEEDED(StringCchCopy(pszFile, MAX_PATH, szTemp))); if (pidl) { SHFree(pidl); } if (psf) { psf->Release(); psf = NULL; } goto VerifyTheName; } } if (pidl) { SHFree(pidl); } if (psf) { psf->Release(); psf = NULL; } } } return (FE_FOUNDNAME); } else if (*pnErrCode == OF_BUFFERTRUNCATED) { bGetOut = TRUE; } if (bGetOut || (*pnErrCode != OF_FILENOTFOUND && *pnErrCode != OF_PATHNOTFOUND)) { return (FE_FILEERR); } if (_bSave) { // // Do no more work if creating a new file. // return (FE_FOUNDNAME); } } // // Make sure we do not loop forever. // bGetOut = TRUE; if (_bSave) { // // Do no more work if creating a new file. // goto VerifyTheName; } pszFile[nNewExt] = CHAR_NULL; if (bTryAsDir && (nFileOffset > 0)) { TCHAR cSave = *(pszFile + nFileOffset); // Save off the filename. *(pszFile + nFileOffset) = CHAR_NULL; // Chop it off. // // We need to have the view on the dir with the file to do the // next steps. // BOOL bOK = JumpToPath(pszFile); *(pszFile + nFileOffset) = cSave; // Put it back. if (!_psv) { // // We're dead. // return (FE_OUTOFMEM); } if (bOK) { // We've moved to the directory. Now just put the filename in the edit box. StringCopyOverlap(pszFile, pszFile + nFileOffset); nNewExt -= nFileOffset; SetEditFile(pszFile, NULL, TRUE); } else { *pnErrCode = OF_PATHNOTFOUND; return (FE_FILEERR); } } EnumItemObjects(SVGIO_ALLVIEW, FindNameEnumCB, (LPARAM)&fns); switch (fns.uRet) { case (FE_INVALID_VALUE) : { break; } case (FE_FOUNDNAME) : { goto VerifyTheName; } default : { uRet = fns.uRet; goto VerifyAndRet; } } if (bAddExt) { // // Before we fail, check to see if the file typed sans default // extension exists. // *pnErrCode = VerifyOpen(pszFile, pszPathName); if (*pnErrCode == 0 || *pnErrCode == OF_SHARINGVIOLATION) { // // We will never hit this case for links (because they // have registered extensions), so we don't need // to goto VerifyTheName (which also calls VerifyOpen again). // return (FE_FOUNDNAME); } // // I still can't find it? Try adding the default extension and // return failure. // EVAL(AppendExt(pszFile, MAX_PATH, _pszDefExt, FALSE)); } uRet = FE_FILEERR; VerifyAndRet: *pnErrCode = VerifyOpen(pszFile, pszPathName); return (uRet); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::SetDirRetry // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::SetDirRetry( LPTSTR pszDir, BOOL bNoValidate) { if (SetCurrentDirectory(pszDir)) { JumpThere: JumpToPath(TEXT(".")); return TRUE; } if (bNoValidate || !IsUNC(pszDir)) { return FALSE; } // // It may have been a password problem, so try to add the connection. // Note that if we are on a net that does not support CD'ing to UNC's // directly, this call will connect it to a drive letter. // if (!SHValidateUNC(_hwndDlg, pszDir, 0)) { switch (GetLastError()) { case ERROR_CANCELLED: { // // We don't want to put up an error message if they // canceled the password dialog. // return TRUE; } case ERROR_NETWORK_UNREACHABLE: { LPTSTR lpMsgBuf; TCHAR szTitle[MAX_PATH]; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL); GetWindowText(_hwndDlg, szTitle, ARRAYSIZE(szTitle)); MessageBox(NULL, lpMsgBuf, szTitle, MB_OK | MB_ICONINFORMATION); // Free the buffer. LocalFree(lpMsgBuf); return TRUE; } default: { // // Some other error we don't know about. // return FALSE; } } } // // We connected to it, so try to switch to it again. // if (SetCurrentDirectory(pszDir)) { goto JumpThere; } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::MultiSelectOKButton // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::MultiSelectOKButton( LPCTSTR pszFiles, OKBUTTONFLAGS Flags) { TCHAR szPathName[MAX_PATH]; int nErrCode; LPTSTR pchRead, pchWrite, lpCurDir; UINT cch, cchCurDir, cchFiles; WAIT_CURSOR w(this); // // This doesn't really mean anything for multiselection. // _pOFN->nFileExtension = 0; if (!_pOFN->lpstrFile) { return TRUE; } // // Check for space for first full path element. // if ((_pOFN->Flags & OFN_ENABLEINCLUDENOTIFY) && lstrlen(_pszObjectCurDir)) { lpCurDir = _pszObjectCurDir; } else { lpCurDir = _szCurDir; } cchCurDir = lstrlen(lpCurDir) + 1; cchFiles = lstrlen(pszFiles) + 1; cch = cchCurDir + cchFiles; if (cch > (UINT)_pOFN->nMaxFile) { // // Buffer is too small, so return the size of the buffer // required to hold the string. // // cch is not really the number of characters needed, but it // should be close. // StoreFileSizeInOFN(_pOFN, cch); return TRUE; } TEMPSTR psFiles(cchFiles + FILE_PADDING); pchRead = psFiles; int cchRead = cchFiles + FILE_PADDING; if (!pchRead) { // // Out of memory. // FEATURE There should be some sort of error message here. // return FALSE; } // // Copy in the full path as the first element. // EVAL(SUCCEEDED(StringCchCopy(_pOFN->lpstrFile, _pOFN->nMaxFile, lpCurDir))); // // Set nFileOffset to 1st file. // _pOFN->nFileOffset = (WORD) cchCurDir; pchWrite = _pOFN->lpstrFile + cchCurDir; int cchRemaining = _pOFN->nMaxFile - cchCurDir; // // We know there is enough room for the whole string. // EVAL(SUCCEEDED(StringCchCopy(pchRead, cchRead, pszFiles))); // // This should only compact the string (converting to NULL terminated list of strings) // if (!ConvertToNULLTerm(pchRead)) { return FALSE; } for (; *pchRead; pchRead += lstrlen(pchRead) + 1) { int nFileOffset, nExtOffset; TCHAR szBasicPath[MAX_PATH]; EVAL(SUCCEEDED(StringCchCopy(szBasicPath, ARRAYSIZE(szBasicPath), pchRead))); // Impossible for filename to be longer than MAX_PATH nFileOffset = ParseFileNew(szBasicPath, &nExtOffset, FALSE, TRUE); if (nFileOffset < 0) { InvalidFileWarningNew(_hwndDlg, pchRead, nFileOffset); return FALSE; } // // Pass in 0 for the file offset to make sure we do not switch // to another folder. // switch (FindNameInView(szBasicPath, Flags, szPathName, nFileOffset, nExtOffset, &nErrCode, FALSE)) { case (FE_OUTOFMEM) : case (FE_CHANGEDDIR) : { return FALSE; } case (FE_TOOMANY) : { CDMessageBox(_hwndDlg, iszTooManyFiles, MB_OK | MB_ICONEXCLAMATION, pchRead); return FALSE; } default : { break; } } if (nErrCode && ((_pOFN->Flags & OFN_FILEMUSTEXIST) || (nErrCode != OF_FILENOTFOUND)) && ((_pOFN->Flags & OFN_PATHMUSTEXIST) || (nErrCode != OF_PATHNOTFOUND)) && (!(_pOFN->Flags & OFN_SHAREAWARE) || (nErrCode != OF_SHARINGVIOLATION))) { if ((nErrCode == OF_SHARINGVIOLATION) && _hSubDlg) { int nShareCode = CD_SendShareNotify(_hSubDlg, _hwndDlg, szPathName, _pOFN, _pOFI); if (nShareCode == OFN_SHARENOWARN) { return FALSE; } else if (nShareCode == OFN_SHAREFALLTHROUGH) { goto EscapedThroughShare; } else { // // They might not have handled the notification, so try // the registered message. // nShareCode = CD_SendShareMsg(_hSubDlg, szPathName, _pOFI->ApiType); if (nShareCode == OFN_SHARENOWARN) { return FALSE; } else if (nShareCode == OFN_SHAREFALLTHROUGH) { goto EscapedThroughShare; } } } else if (nErrCode == OF_ACCESSDENIED) { szPathName[0] |= 0x60; if (GetDriveType(szPathName) != DRIVE_REMOVABLE) { nErrCode = OF_NETACCESSDENIED; } } // // These will never be set. // if ((nErrCode == OF_WRITEPROTECTION) || (nErrCode == OF_DISKFULL) || (nErrCode == OF_DISKFULL2) || (nErrCode == OF_ACCESSDENIED)) { *pchRead = szPathName[0]; } MultiWarning: InvalidFileWarningNew(_hwndDlg, pchRead, nErrCode); return FALSE; } EscapedThroughShare: if (nErrCode == 0) { if (!_ValidateSelectedFile(szPathName, &nErrCode)) { if (nErrCode) { goto MultiWarning; } else { return FALSE; } } } // // Add some more in case the file name got larger. // cch += lstrlen(szBasicPath) - lstrlen(pchRead); if (cch > (UINT)_pOFN->nMaxFile) { StoreFileSizeInOFN(_pOFN, cch); return TRUE; } // // We already know we have anough room. // EVAL(SUCCEEDED(StringCchCopy(pchWrite, cchRemaining, szBasicPath))); pchWrite += lstrlen(pchWrite) + 1; } // // double-NULL terminate. // *pchWrite = CHAR_NULL; return TRUE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::CheckForRestrictedFolder // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::CheckForRestrictedFolder(LPCTSTR lpszPath, int nFileOffset) { TCHAR szPath[MAX_PATH]; TCHAR szTemp[MAX_PATH]; LPITEMIDLIST pidl; BOOL bPidlAllocated = FALSE; BOOL bRet = FALSE; DWORD dwAttrib = SFGAO_FILESYSTEM; HRESULT hr = S_OK; if (nFileOffset > 0) { //There's a path in the given filename. Get the directory part of the filename. ASSERT(nFileOffset < ARRAYSIZE(szTemp)); StringCchCopy(szTemp, nFileOffset, lpszPath); // Truncation at nFileOffset is desired. //The directory path might be a relative path. Resolve it to get fully qualified path. CDPathQualify(szTemp, szPath); //Create the pidl for this path as well as get the attributes. hr = SHILCreateFromPath(szPath, &pidl, &dwAttrib); if (SUCCEEDED(hr)) { bPidlAllocated = TRUE; } else { // WE are failing b'cos the user might have typed some path which doesn't exist. // if the path doesn't exist then it can't be one of the directory we are trying restrict. // let's bail out and let the code that checks for valid path take care of it return bRet; } } else { IShellLink *psl; pidl = _pCurrentLocation->pidlFull; if (SUCCEEDED(CDGetUIObjectFromFullPIDL(pidl,_hwndDlg, IID_PPV_ARG(IShellLink, &psl)))) { LPITEMIDLIST pidlTarget; if (S_OK == psl->GetIDList(&pidlTarget)) { SHGetAttributesOf(pidlTarget, &dwAttrib); ILFree(pidlTarget); } psl->Release(); } else { SHGetAttributesOf(pidl, &dwAttrib); } } // 1. We cannot save to the non file system folders. // 2. We should not allow user to save in recent files folder as the file might get deleted. if (!(dwAttrib & SFGAO_FILESYSTEM) || _IsRecentFolder(pidl)) { int iMessage = UrlIs(lpszPath, URLIS_URL) ? iszNoSaveToURL : iszSaveRestricted; HCURSOR hcurOld = SetCursor(LoadCursor(NULL, IDC_ARROW)); CDMessageBox(_hwndDlg, iMessage, MB_OK | MB_ICONEXCLAMATION); SetCursor(hcurOld); bRet = TRUE; } if (bPidlAllocated) { ILFree(pidl); } return bRet; } STDAPI_(LPITEMIDLIST) GetIDListFromFolder(IShellFolder *psf) { LPITEMIDLIST pidl = NULL; IPersistFolder2 *ppf; if (psf && SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf)))) { ppf->GetCurFolder(&pidl); ppf->Release(); } return pidl; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OKButtonPressed // // Process the OK button being pressed. This may involve jumping to a path, // changing the filter, actually choosing a file to open or save as, or who // knows what else. // // Note: There are 4 cases for validation of a file name: // 1) OFN_NOVALIDATE Allows invalid characters // 2) No validation flags No invalid characters, but path need not exist // 3) OFN_PATHMUSTEXIST No invalid characters, path must exist // 4) OFN_FILEMUSTEXIST No invalid characters, path & file must exist // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::OKButtonPressed( LPCTSTR pszFile, OKBUTTONFLAGS Flags) { TCHAR szExpFile[MAX_PATH]; TCHAR szPathName[MAX_PATH]; TCHAR szBasicPath[MAX_PATH]; LPTSTR pExpFile = NULL; LPTSTR pFree = NULL; int nErrCode; ECODE eCode = ECODE_S_OK; DWORD cch; int nFileOffset, nExtOffset, nOldExt; TCHAR ch; BOOL bAddExt = FALSE; BOOL bUNCName = FALSE; int nTempOffset; BOOL bIsDir; BOOL bRet = FALSE; WAIT_CURSOR w(this); EnableModelessSB(FALSE); if (_bSelIsObject) { StorePathOrFileSizeInOFN(_pOFN, _pszObjectPath); } // // Expand any environment variables. // cch = _pOFN->nMaxFile; if (cch > MAX_PATH) { pExpFile = pFree = (LPTSTR)LocalAlloc(LPTR, (cch * sizeof(TCHAR))); } if (!pExpFile) { pExpFile = szExpFile; cch = MAX_PATH; } pExpFile[0] = 0; pExpFile[1] = 0; ExpandEnvironmentStrings(pszFile, pExpFile, cch); pExpFile[cch - 1] = 0; // // See if we're in Multi Select mode. // if (StrChr(pExpFile, CHAR_QUOTE) && (_pOFN->Flags & OFN_ALLOWMULTISELECT)) { bRet = MultiSelectOKButton(pExpFile, Flags); goto ReturnFromOKButtonPressed; } // // We've only got a single selection...if we're in // multi-select mode & it's an object, we need to do a little // work before continuing... // if ((_pOFN->Flags & OFN_ALLOWMULTISELECT) && _bSelIsObject) { pExpFile = _pszObjectPath; } if ((pExpFile[1] == CHAR_COLON) || DBL_BSLASH(pExpFile)) { // // If a drive or UNC was specified, use it. // if (FAILED(StringCchCopy(szBasicPath, ARRAYSIZE(szBasicPath) - 1, pExpFile))) // ARRAYSIZE - 1? { // (pExpFile can potentially be larger than ARRAYSIZE(szBasicPAth)) nErrCode = OF_BUFFERTRUNCATED; goto Warning; } nTempOffset = 0; } else { // // Grab the directory from the listbox. // cch = GetDirectoryFromLB(szBasicPath, &nTempOffset); if (pExpFile[0] == CHAR_BSLASH) { // // If a directory from the root was given, put it // immediately off the root (\\server\share or a:). // if (FAILED(StringCchCopy(szBasicPath + nTempOffset, ARRAYSIZE(szBasicPath) - nTempOffset - 1, pExpFile))) { nErrCode = OF_BUFFERTRUNCATED; goto Warning; } } else { // // Tack the file to the end of the path. // if (FAILED(StringCchCopy(szBasicPath + cch, ARRAYSIZE(szBasicPath) - cch - 1, pExpFile))) { nErrCode = OF_BUFFERTRUNCATED; goto Warning; } } } nFileOffset = ParseFileOld(szBasicPath, &nExtOffset, &nOldExt, FALSE, TRUE); if (nFileOffset == PARSE_EMPTYSTRING) { if (_psv) { _psv->Refresh(); } goto ReturnFromOKButtonPressed; } else if ((nFileOffset != PARSE_DIRECTORYNAME) && (_pOFN->Flags & OFN_NOVALIDATE)) { if (_bSelIsObject) { _pOFN->nFileOffset = _pOFN->nFileExtension = 0; } else { _pOFN->nFileOffset = (WORD)(nFileOffset > 0 ? nFileOffset : lstrlen(szBasicPath)); // point at the NULL terminator in error cases _pOFN->nFileExtension = (WORD)nOldExt; } StorePathOrFileSizeInOFN(_pOFN, szBasicPath); bRet = TRUE; goto ReturnFromOKButtonPressed; } else if (nFileOffset == PARSE_DIRECTORYNAME) { // // See if it ends in slash. // if (nExtOffset > 0) { if (ISBACKSLASH(szBasicPath, nExtOffset - 1)) { // // "\\server\share\" and "c:\" keep the trailing backslash, // all other paths remove the trailing backslash. Note that // we don't remove the slash if the user typed the path directly // (nTempOffset is 0 in that case). // if ((nExtOffset != 1) && (szBasicPath[nExtOffset - 2] != CHAR_COLON) && (nExtOffset != nTempOffset + 1)) { szBasicPath[nExtOffset - 1] = CHAR_NULL; } } else if ((szBasicPath[nExtOffset - 1] == CHAR_DOT) && ((szBasicPath[nExtOffset - 2] == CHAR_DOT) || ISBACKSLASH(szBasicPath, nExtOffset - 2)) && IsUNC(szBasicPath)) { // // Add a trailing slash to UNC paths ending with ".." or "\." // szBasicPath[nExtOffset] = CHAR_BSLASH; szBasicPath[nExtOffset + 1] = CHAR_NULL; } } // // Fall through to Directory Checking. // } else if (nFileOffset < 0) { nErrCode = nFileOffset; // // I don't recognize this, so try to jump there. // This is where servers get processed. // if (JumpToPath(szBasicPath)) { goto ReturnFromOKButtonPressed; } // // Fall through to the rest of the processing to warn the user. // Warning: if (bUNCName) { cch = lstrlen(szBasicPath) - 1; if ((szBasicPath[cch] == CHAR_BSLASH) && (szBasicPath[cch - 1] == CHAR_DOT) && (ISBACKSLASH(szBasicPath, cch - 2))) { szBasicPath[cch - 2] = CHAR_NULL; } } // For file names of form c:filename.txt , we hacked and changed it to c:.\filename.txt // check for that hack and if so change the file name back as it was given by user. else if ((nFileOffset == 2) && (szBasicPath[2] == CHAR_DOT)) { StringCchCopyOverlap(szBasicPath + 2, ARRAYSIZE(szBasicPath) - 2, szBasicPath + 4); } // If the disk is not a floppy and they tell me there's no // disk in the drive, don't believe them. Instead, put up the // error message that they should have given us. (Note that the // error message is checked first since checking the drive type // is slower.) // // // I will assume that if we get error 0 or 1 or removable // that we will assume removable. // if (nErrCode == OF_ACCESSDENIED) { TCHAR szD[4]; szPathName[0] |= 0x60; szD[0] = *szBasicPath; szD[1] = CHAR_COLON; szD[2] = CHAR_BSLASH; szD[3] = 0; if (bUNCName || GetDriveType(szD) <= DRIVE_REMOVABLE) { nErrCode = OF_NETACCESSDENIED; } } if ((nErrCode == OF_WRITEPROTECTION) || (nErrCode == OF_DISKFULL) || (nErrCode == OF_DISKFULL2) || (nErrCode == OF_ACCESSDENIED)) { szBasicPath[0] = szPathName[0]; } HRESULT hr = E_FAIL; if (_bSave) { hr = CheckForRestrictedFolder(pszFile, 0) ? S_FALSE : E_FAIL; } // we might only want use ShellItem's for some errors if (FAILED(hr) && nErrCode != OF_BUFFERTRUNCATED/*&& (nErrCode == OF_FILENOTFOUND || (nErrCode == OF_PATHNOTFOUND))*/) { IShellItem *psi; hr = _ParseShellItem(pszFile, &psi, TRUE); if (S_OK == hr) { hr = _ProcessItemAsFile(psi); psi->Release(); } } if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_CANCELLED)) { // Special case // If the error was ACCESS_DENIED in a save dialog. if (_bSave && (nErrCode == OF_ACCESSDENIED)) { // Ask if the user wants to switch to My Documents. _SaveAccessDenied(pszFile); } else { InvalidFileWarningNew(_hwndDlg, pszFile, nErrCode); } } else if (S_OK == hr) { bRet = TRUE; } goto ReturnFromOKButtonPressed; } // // We either have a file pattern or a real file. // If it's a UNC name // (1) Fall through to file name testing // Else if it's a directory // (1) Add on default pattern // (2) Act like it's a pattern (goto pattern (1)) // Else if it's a pattern // (1) Update everything // (2) display files in whatever dir we're now in // Else if it's a file name! // (1) Check out the syntax // (2) End the dialog given OK // (3) Beep/message otherwise // // // Directory ?? this must succeed for relative paths. // NOTE: It won't succeed for relative paths that walk off the root. // bIsDir = SetDirRetry(szBasicPath); // // We need to parse again in case SetDirRetry changed a UNC path to use // a drive letter. // nFileOffset = ParseFileOld(szBasicPath, &nExtOffset, &nOldExt, FALSE, TRUE); nTempOffset = nFileOffset; if (bIsDir) { goto ReturnFromOKButtonPressed; } else if (IsUNC(szBasicPath)) { // // UNC Name. // bUNCName = TRUE; } else if (nFileOffset > 0) { TCHAR szBuf[MAX_PATH]; // // There is a path in the string. // if ((nFileOffset > 1) && (szBasicPath[nFileOffset - 1] != CHAR_COLON) && (szBasicPath[nFileOffset - 2] != CHAR_COLON)) { nTempOffset--; } GetCurrentDirectory(ARRAYSIZE(szBuf), szBuf); ch = szBasicPath[nTempOffset]; szBasicPath[nTempOffset] = 0; if (SetCurrentDirectory(szBasicPath)) { SetCurrentDirectory(szBuf); } else { switch (GetLastError()) { case (ERROR_NOT_READY) : { eCode = ECODE_BADDRIVE; break; } default : { eCode = ECODE_BADPATH; break; } } } szBasicPath[nTempOffset] = ch; } else if (nFileOffset == PARSE_DIRECTORYNAME) { TCHAR szD[4]; szD[0] = *szBasicPath; szD[1] = CHAR_COLON; szD[2] = CHAR_BSLASH; szD[3] = 0; if (PathFileExists(szD)) { eCode = ECODE_BADPATH; } else { eCode = ECODE_BADDRIVE; } } // // Was there a path and did it fail? // if (!bUNCName && nFileOffset && eCode != ECODE_S_OK && (_pOFN->Flags & OFN_PATHMUSTEXIST)) { if (eCode == ECODE_BADPATH) { nErrCode = OF_PATHNOTFOUND; } else if (eCode == ECODE_BADDRIVE) { TCHAR szD[4]; // // We can get here without performing an OpenFile call. As // such the szPathName can be filled with random garbage. // Since we only need one character for the error message, // set szPathName[0] to the drive letter. // szPathName[0] = szD[0] = *szBasicPath; szD[1] = CHAR_COLON; szD[2] = CHAR_BSLASH; szD[3] = 0; switch (GetDriveType(szD)) { case (DRIVE_REMOVABLE) : { nErrCode = ERROR_NOT_READY; break; } case (1) : { // // Drive does not exist. // nErrCode = OF_NODRIVE; break; } default : { nErrCode = OF_PATHNOTFOUND; } } } else { nErrCode = OF_FILENOTFOUND; } goto Warning; } // From here on out, if there's an error, set nFileOffset to some // valid position in szBasicPath, so let's treat the string as one full filename if (nFileOffset < 0) nFileOffset = 0; // nFileOffset still needs to be in range ASSERT(nFileOffset < ARRAYSIZE(szBasicPath)); // // Full pattern? // if (IsWild(szBasicPath + nFileOffset)) { if (!bUNCName) { SetCurrentFilter(szBasicPath + nFileOffset, Flags); if (nTempOffset) { szBasicPath[nTempOffset] = 0; JumpToPath(szBasicPath, TRUE); } else if (_psv) { _psv->Refresh(); } goto ReturnFromOKButtonPressed; } else { SetCurrentFilter(szBasicPath + nFileOffset, Flags); szBasicPath[nFileOffset] = CHAR_NULL; JumpToPath(szBasicPath); goto ReturnFromOKButtonPressed; } } if (PortName(szBasicPath + nFileOffset)) { nErrCode = OF_PORTNAME; goto Warning; } // In save as dialog check to see if the folder user trying to save a file is // a restricted folder (Network Folder). if so bail out if (_bSave && CheckForRestrictedFolder(szBasicPath, nFileOffset)) { goto ReturnFromOKButtonPressed; } // // Check if we've received a string in the form "C:filename.ext". // If we have, convert it to the form "C:.\filename.ext". This is done // because the kernel will search the entire path, ignoring the drive // specification after the initial search. Making it include a slash // causes kernel to only search at that location. // // Note: Only increment nExtOffset, not nFileOffset. This is done // because only nExtOffset is used later, and nFileOffset can then be // used at the Warning: label to determine if this hack has occurred, // and thus it can strip out the ".\" when putting up the error. // if ((nFileOffset == 2) && (szBasicPath[1] == CHAR_COLON)) { if (SUCCEEDED(StringCchCopyOverlap(szBasicPath + 4, ARRAYSIZE(szBasicPath) - 4, szBasicPath + 2))) { szBasicPath[2] = CHAR_DOT; szBasicPath[3] = CHAR_BSLASH; nExtOffset += 2; } else { // Not enough room in our buffer. nErrCode = OF_BUFFERTRUNCATED; goto Warning; } } // // Add the default extension unless filename ends with period or no // default extension exists. If the file exists, consider asking // permission to overwrite the file. // // NOTE: When no extension given, default extension is tried 1st. // FindNameInView calls VerifyOpen before returning. // szPathName[0] = 0; switch (FindNameInView(szBasicPath, Flags, szPathName, nFileOffset, nExtOffset, &nErrCode)) { case (FE_OUTOFMEM) : case (FE_CHANGEDDIR) : { goto ReturnFromOKButtonPressed; } case (FE_TOOMANY) : { SetCursor(LoadCursor(NULL, IDC_ARROW)); CDMessageBox(_hwndDlg, iszTooManyFiles, MB_OK | MB_ICONEXCLAMATION, szBasicPath); goto ReturnFromOKButtonPressed; } default : { break; } } switch (nErrCode) { case (0) : { if (!_ValidateSelectedFile(szPathName, &nErrCode)) { if (nErrCode) { goto Warning; } else { goto ReturnFromOKButtonPressed; } } break; } case (OF_SHARINGVIOLATION) : { // // If the app is "share aware", fall through. // Otherwise, ask the hook function. // if (!(_pOFN->Flags & OFN_SHAREAWARE)) { if (_hSubDlg) { int nShareCode = CD_SendShareNotify(_hSubDlg, _hwndDlg, szPathName, _pOFN, _pOFI); if (nShareCode == OFN_SHARENOWARN) { goto ReturnFromOKButtonPressed; } else if (nShareCode != OFN_SHAREFALLTHROUGH) { // // They might not have handled the notification, // so try the registered message. // nShareCode = CD_SendShareMsg(_hSubDlg, szPathName, _pOFI->ApiType); if (nShareCode == OFN_SHARENOWARN) { goto ReturnFromOKButtonPressed; } else if (nShareCode != OFN_SHAREFALLTHROUGH) { goto Warning; } } } else { goto Warning; } } break; } case (OF_FILENOTFOUND) : case (OF_PATHNOTFOUND) : { if (!_bSave) { // // The file or path wasn't found. // If this is a save dialog, we're ok, but if it's not, // we're toast. // if (_pOFN->Flags & OFN_FILEMUSTEXIST) { if (_pOFN->Flags & OFN_CREATEPROMPT) { int nCreateCode = CreateFileDlg(_hwndDlg, szBasicPath); if (nCreateCode != IDYES) { goto ReturnFromOKButtonPressed; } } else { goto Warning; } } } goto VerifyPath; } case (OF_BUFFERTRUNCATED) : { // The desired path was truncated because of the size of our internal buffers, // meaning the pathname was over maxpath. goto Warning; } default : { if (!_bSave) { goto Warning; } // // The file doesn't exist. Can it be created? This is needed // because there are many extended characters which are invalid // which won't be caught by ParseFile. // // Two more good reasons: Write-protected disks & full disks. // // BUT, if they don't want the test creation, they can request // that we not do it using the OFN_NOTESTFILECREATE flag. If // they want to create files on a share that has // create-but-no-modify privileges, they should set this flag // but be ready for failures that couldn't be caught, such as // no create privileges, invalid extended characters, a full // disk, etc. // VerifyPath: // // Verify the path. // if (_pOFN->Flags & OFN_PATHMUSTEXIST) { if (!(_pOFN->Flags & OFN_NOTESTFILECREATE)) { HANDLE hf = CreateFile(szBasicPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (hf != INVALID_HANDLE_VALUE) { CloseHandle(hf); // // This test is here to see if we were able to // create it, but couldn't delete it. If so, // warn the user that the network admin has given // him create-but-no-modify privileges. As such, // the file has just been created, but we can't // do anything with it, it's of 0 size. // if (!DeleteFile(szBasicPath)) { nErrCode = OF_CREATENOMODIFY; goto Warning; } } else { // // Unable to create it. // // If it's not write-protection, a full disk, // network protection, or the user popping the // drive door open, assume that the filename is // invalid. // nErrCode = GetLastError(); switch (nErrCode) { case (OF_WRITEPROTECTION) : case (OF_DISKFULL) : case (OF_DISKFULL2) : case (OF_NETACCESSDENIED) : case (OF_ACCESSDENIED) : { break; } default : { nErrCode = 0; break; } } goto Warning; } } } } } DWORD dwError; nFileOffset = _CopyFileNameToOFN(szPathName, &dwError); ASSERT(nFileOffset >= 0 && nFileOffset < ARRAYSIZE(szPathName)); _CopyTitleToOFN(szPathName + nFileOffset); if (dwError == 0) { // Only PostProcess if there was no error in copying the info to the OFN struct. _PostProcess(szPathName); } bRet = TRUE; ReturnFromOKButtonPressed: EnableModelessSB(TRUE); if (pFree) LocalFree(pFree); return (bRet); } void CFileOpenBrowser::_CopyTitleToOFN(LPCTSTR pszTitle) { // // File Title. // Note that it's cut off at whatever the buffer length // is, so if the buffer's too small, no notice is given. // (Notice is only given to the app if lpstrFile is of insufficient size). // if (_pOFN->lpstrFileTitle) { StringCchCopy(_pOFN->lpstrFileTitle, _pOFN->nMaxFileTitle, pszTitle); } } int CFileOpenBrowser::_CopyFileNameToOFN(LPTSTR pszFile, DWORD *pdwError) { int nExtOffset, nOldExt, nFileOffset = ParseFileOld(pszFile, &nExtOffset, &nOldExt, FALSE, TRUE); //NULL can be passed in to this function if we don't care about the error condition! if (pdwError) *pdwError = 0; //Assume no error. _pOFN->nFileOffset = (WORD) (nFileOffset > 0 ? nFileOffset : lstrlen(pszFile)); // point at the NULL terminator in error cases _pOFN->nFileExtension = (WORD) nOldExt; _pOFN->Flags &= ~OFN_EXTENSIONDIFFERENT; if (_pOFN->lpstrDefExt && _pOFN->nFileExtension) { WCHAR szPrivateExt[4]; // // Check against _pOFN->lpstrDefExt, not _pszDefExt. // StringCchCopy(szPrivateExt, ARRAYSIZE(szPrivateExt), _pOFN->lpstrDefExt); // truncation desired if (lstrcmpi(szPrivateExt, pszFile + nOldExt)) { _pOFN->Flags |= OFN_EXTENSIONDIFFERENT; } } if (_pOFN->lpstrFile) { DWORD cch = lstrlen(pszFile) + 1; if (_pOFN->Flags & OFN_ALLOWMULTISELECT) { // // Extra room for double-NULL. // ++cch; } if (cch <= _pOFN->nMaxFile) { EVAL(SUCCEEDED(StringCchCopy(_pOFN->lpstrFile, _pOFN->nMaxFile, pszFile))); // We've already verified there's enough room. if (_pOFN->Flags & OFN_ALLOWMULTISELECT) { // // Double-NULL terminate. // *(_pOFN->lpstrFile + cch - 1) = CHAR_NULL; } if (!(_pOFN->Flags & OFN_NOCHANGEDIR) && !PathIsUNC(pszFile) && (nFileOffset > 0)) { TCHAR ch = _pOFN->lpstrFile[nFileOffset]; _pOFN->lpstrFile[nFileOffset] = CHAR_NULL; SetCurrentDirectory(_pOFN->lpstrFile); _pOFN->lpstrFile[nFileOffset] = ch; } } else { // // Buffer is too small, so return the size of the buffer // required to hold the string. // StoreFileSizeInOFN(_pOFN, cch); if (pdwError) *pdwError = FNERR_BUFFERTOOSMALL; //This is an error! } } return nFileOffset; } HRESULT CFileOpenBrowser::_MakeFakeCopy(IShellItem *psi, LPWSTR *ppszPath) { // // now we have to create a temp file // to pass back to the client. // we will do this in the internet cache. // // FEATURE - this should be a service in shell32 - zekel 11-AUG-98 // we should create a dependancy on wininet from // comdlg32. this should really be some sort of // service in shell32 that we call. CreateShellItemTempFile().. // ILocalCopy *plc; HRESULT hr = psi->BindToHandler(NULL, BHID_LocalCopyHelper, IID_PPV_ARG(ILocalCopy, &plc)); if (SUCCEEDED(hr)) { IBindCtx *pbc = NULL; // hr = SIAddBindCtxOfProgressUI(_hwndDlg, NULL, NULL, &pbc); if (SUCCEEDED(hr)) { hr = plc->Download(LCDOWN_READONLY, pbc, ppszPath); } plc->Release(); } return hr; } class CAsyncParseHelper { public: CAsyncParseHelper(IUnknown *punkSite, IBindCtx *pbc); STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT ParseAsync(IShellFolder *psf, LPCWSTR pszName, LPITEMIDLIST *ppidl, ULONG *pdwAttribs); protected: // methods ~CAsyncParseHelper(); static DWORD WINAPI CAsyncParseHelper::s_ThreadProc(void *pv); HRESULT _Prepare(IShellFolder *psf, LPCWSTR pszName); HRESULT _GetFolder(IShellFolder **ppsf); void _Parse(); HRESULT _Pump(); protected: // members LONG _cRef; IUnknown *_punkSite; IBindCtx *_pbc; LPWSTR _pszName; DWORD _dwAttribs; HWND _hwnd; HANDLE _hEvent; LPITEMIDLIST _pidl; HRESULT _hrParse; IShellFolder *_psfFree; // is alright dropping between threads LPITEMIDLIST _pidlFolder; // bind to it in the right thread }; CAsyncParseHelper::~CAsyncParseHelper() { if (_pszName) LocalFree(_pszName); if (_punkSite) _punkSite->Release(); if (_psfFree) _psfFree->Release(); if (_pbc) _pbc->Release(); if (_hEvent) CloseHandle(_hEvent); ILFree(_pidl); ILFree(_pidlFolder); } CAsyncParseHelper::CAsyncParseHelper(IUnknown *punkSite, IBindCtx *pbc) : _cRef(1), _hrParse(E_UNEXPECTED) { if (punkSite) { _punkSite = punkSite; punkSite->AddRef(); IUnknown_GetWindow(_punkSite, &_hwnd); } if (pbc) { _pbc = pbc; pbc->AddRef(); } } HRESULT CAsyncParseHelper::_GetFolder(IShellFolder **ppsf) { HRESULT hr; if (_psfFree) { _psfFree->AddRef(); *ppsf = _psfFree; hr = S_OK; } else if (_pidlFolder) { hr = SHBindToObjectEx(NULL, _pidlFolder, NULL, IID_PPV_ARG(IShellFolder, ppsf)); } else hr = SHGetDesktopFolder(ppsf); return hr; } void CAsyncParseHelper::_Parse() { IShellFolder *psf; _hrParse = _GetFolder(&psf); if (SUCCEEDED(_hrParse)) { _hrParse = IShellFolder_ParseDisplayName(psf, _hwnd, _pbc, _pszName, NULL, &_pidl, _dwAttribs ? &_dwAttribs : NULL); psf->Release(); } SetEvent(_hEvent); } DWORD WINAPI CAsyncParseHelper::s_ThreadProc(void *pv) { CAsyncParseHelper *paph = (CAsyncParseHelper *)pv; paph->_Parse(); paph->Release(); return 0; } HRESULT CAsyncParseHelper::_Prepare(IShellFolder *psf, LPCWSTR pszName) { _pszName = StrDupW(pszName); _hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); HRESULT hr = _pszName && _hEvent ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr) && psf) { IPersistFreeThreadedObject *pfto; hr = psf->QueryInterface(IID_PPV_ARG(IPersistFreeThreadedObject, &pfto)); if (SUCCEEDED(hr)) { _psfFree = psf; psf->AddRef(); pfto->Release(); } else { hr = SHGetIDListFromUnk(psf, &_pidlFolder); } } return hr; } HRESULT CAsyncParseHelper::ParseAsync(IShellFolder *psf, LPCWSTR pszName, LPITEMIDLIST *ppidl, ULONG *pdwAttribs) { HRESULT hr = _Prepare(psf, pszName); if (pdwAttribs) _dwAttribs = *pdwAttribs; // take one for the thread AddRef(); if (SUCCEEDED(hr) && SHCreateThread(CAsyncParseHelper::s_ThreadProc, this, CTF_COINIT, NULL)) { // lets go modal IUnknown_EnableModeless(_punkSite, FALSE); hr = _Pump(); IUnknown_EnableModeless(_punkSite, TRUE); if (SUCCEEDED(hr)) { ASSERT(_pidl); *ppidl = _pidl; _pidl = NULL; if (pdwAttribs) *pdwAttribs = _dwAttribs; } else { ASSERT(!_pidl); } } else { // release because the thread wont Release(); // hr = IShellFolder_ParseDisplayName(_psf, _hwnd, _pbc, pszName, NULL, ppidl, pdwAttribs); } if (FAILED(hr)) *ppidl = NULL; return hr; } HRESULT CAsyncParseHelper::_Pump() { BOOL fCancelled = FALSE; while (!fCancelled) { DWORD dwWaitResult = MsgWaitForMultipleObjects(1, &_hEvent, FALSE, INFINITE, QS_ALLINPUT); if (dwWaitResult != (DWORD)-1) { if (dwWaitResult == WAIT_OBJECT_0) { // our event was triggered // that means that we have finished break; } else { // there is a message MSG msg; // There was some message put in our queue, so we need to dispose // of it while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // maybe there should be a flag to allow this?? if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) { fCancelled = TRUE; break; } else { TranslateMessage(&msg); DispatchMessage(&msg); } if (g_bUserPressedCancel) { fCancelled = TRUE; break; } } } } else { ASSERT(FAILED(_hrParse)); break; } } if (fCancelled) { // Better NULL the pidl out. ParseAsync expects a NULL _pidl if _Pump returns an error code. ILFree(_pidl); _pidl = NULL; // clear this for the parse g_bUserPressedCancel = FALSE; return HRESULT_FROM_WIN32(ERROR_CANCELLED); } else return _hrParse; } STDAPI SHParseNameAsync(IShellFolder *psf, IBindCtx *pbc, LPCWSTR pszName, IUnknown *punkSite, LPITEMIDLIST *ppidl, DWORD *pdwAttribs) { HRESULT hr = E_OUTOFMEMORY; CAsyncParseHelper *paph = new CAsyncParseHelper(punkSite, pbc); if (paph) { hr = paph->ParseAsync(psf, pszName, ppidl, pdwAttribs); paph->Release(); } return hr; } // // _ParseName() // psf = the shell folder to bind/parse with if NULL, use desktop // pszIn= the string that should parsed into a ppmk // ppmk = the IShellItem * that is returned with S_OK // // WARNING: this will jumpto a folder if that was what was passed in... // // returns S_OK if it got an IShellItem for the item with the specified folder // S_FALSE if it was the wrong shellfolder; try again with a different one // ERROR for any problems // HRESULT CFileOpenBrowser::_ParseName(LPCITEMIDLIST pidlParent, IShellFolder *psf, IBindCtx *pbc, LPCOLESTR psz, IShellItem **ppsi) { IBindCtx *pbcLocal; HRESULT hr = BindCtx_RegisterObjectParam(pbc, STR_PARSE_PREFER_FOLDER_BROWSING, SAFECAST(this, IShellBrowser *), &pbcLocal); *ppsi = NULL; if (SUCCEEDED(hr)) { LPITEMIDLIST pidl = NULL; hr = SHParseNameAsync(psf, pbcLocal, psz, SAFECAST(this, IShellBrowser *), &pidl, NULL); if (SUCCEEDED(hr)) { ASSERT(pidl); hr = SHCreateShellItem(pidlParent, pidlParent ? psf : NULL, pidl, ppsi); ILFree(pidl); } else if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { hr = S_FALSE; } else if (psf && !pbc) { if (SUCCEEDED(pbcLocal->RegisterObjectParam(STR_DONT_PARSE_RELATIVE, psf))) { // try to hit it from the desktop HRESULT hrNew = _ParseName(NULL, NULL, pbcLocal, psz, ppsi); // else prop back the original error hr = SUCCEEDED(hrNew) ? hrNew : hr; } } pbcLocal->Release(); } return hr; } BOOL CFileOpenBrowser::_OpenAsContainer(IShellItem *psi, SFGAOF sfgao) { BOOL fRet = _bSave ? _IsSaveContainer(sfgao) : _IsOpenContainer(sfgao); if (fRet && (sfgao & SFGAO_STREAM)) { // this is really both a folder and a file // we guess which the caller wants by looking // at the extension LPWSTR psz; if (SUCCEEDED(psi->GetDisplayName(SIGDN_PARENTRELATIVEPARSING, &psz))) { // if the filter equals what ever we are looking at // we assume the caller is actually looking for // this file. fRet = !PathMatchSpec(psz, _szLastFilter); CoTaskMemFree(psz); } } return fRet; } HRESULT CFileOpenBrowser::_TestShellItem(IShellItem *psi, BOOL fAllowJump, IShellItem **ppsiReal) { SFGAOF flags; psi->GetAttributes(SFGAO_STORAGECAPMASK, &flags); HRESULT hr = E_ACCESSDENIED; *ppsiReal = NULL; if (_OpenAsContainer(psi, flags)) { // we have a subfolder that has been selected. // jumpto it instead if (fAllowJump) { LPITEMIDLIST pidl; if (SUCCEEDED(SHGetIDListFromUnk(psi, &pidl))) { JumpToIDList(pidl); ILFree(pidl); } } hr = S_FALSE; } else if ((flags & SFGAO_LINK) && ((flags & SFGAO_FOLDER) || !_IsNoDereferenceLinks(NULL, psi))) { // If this is a link, and (we should dereference links, or it's also a folder [folder shortcut]) IShellItem *psiTarget; if (SUCCEEDED(psi->BindToHandler(NULL, BHID_LinkTargetItem, IID_PPV_ARG(IShellItem, &psiTarget)))) { hr = _TestShellItem(psiTarget, fAllowJump, ppsiReal); psiTarget->Release(); } } else if (_IsStream(flags)) { *ppsiReal = psi; psi->AddRef(); hr = S_OK; } return hr; } HRESULT CFileOpenBrowser::_ParseNameAndTest(LPCOLESTR pszIn, IBindCtx *pbc, IShellItem **ppsi, BOOL fAllowJump) { IShellItem *psi; HRESULT hr = _ParseName(_pCurrentLocation->pidlFull, _psfCurrent, pbc, pszIn, &psi); if (S_OK == hr) { hr = _TestShellItem(psi, fAllowJump, ppsi); psi->Release(); } return hr; } BOOL _FailedBadPath(HRESULT hr) { switch (hr) { case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND): case HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME): case HRESULT_FROM_WIN32(ERROR_BAD_NETPATH): return TRUE; } return FALSE; } #define STR_ACTIONPROGRESS L"ActionProgress" STDAPI BindCtx_BeginActionProgress(IBindCtx *pbc, SPACTION action, SPBEGINF flags, IActionProgress **ppap) { HRESULT hr = E_NOINTERFACE; // default to no IUnknown *punk; *ppap = NULL; if (pbc && SUCCEEDED(pbc->GetObjectParam(STR_ACTIONPROGRESS, &punk))) { IActionProgress *pap; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IActionProgress, &pap)))) { hr = pap->Begin(action, flags); if (SUCCEEDED(hr)) *ppap = pap; else pap->Release(); } punk->Release(); } return hr; } HRESULT CFileOpenBrowser::_ParseShellItem(LPCOLESTR pszIn, IShellItem **ppsi, BOOL fAllowJump) { WAIT_CURSOR w(this); EnableModelessSB(FALSE); HRESULT hr = _ParseNameAndTest(pszIn, NULL, ppsi, fAllowJump); if (_FailedBadPath(hr)) { // If no extension was included, and we have a default extension, try it with that. WCHAR szPath[MAX_PATH]; if ((LPTSTR)_pszDefExt && (0 == *(PathFindExtension(pszIn)))) { if (SUCCEEDED(StringCchCopy(szPath, ARRAYSIZE(szPath), pszIn))) { if (AppendExt(szPath, ARRAYSIZE(szPath), _pszDefExt, FALSE)) { pszIn = szPath; hr = _ParseNameAndTest(pszIn, NULL, ppsi, fAllowJump); } } } if (_FailedBadPath(hr) && _bSave) { // when we are saving, then we // try to force the creation of this item IBindCtx *pbc; if (SUCCEEDED(CreateBindCtx(0, &pbc))) { BIND_OPTS bo = {0}; bo.cbStruct = SIZEOF(bo); bo.grfMode = STGM_CREATE; pbc->SetBindOptions(&bo); hr = _ParseNameAndTest(pszIn, pbc, ppsi, fAllowJump); pbc->Release(); } } } EnableModelessSB(TRUE); return hr; } class CShellItemList : IEnumShellItems { public: CShellItemList() : _cRef(1) {} // IUnknown methods STDMETHODIMP QueryInterface(REFIID riid, void **ppvOut); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP Next(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt); STDMETHODIMP Reset(); STDMETHODIMP Clone(IEnumShellItems **ppenum); HRESULT Add(IShellItem *psi); private: // methods ~CShellItemList(); BOOL _NextOne(IShellItem **ppsi); private: // members LONG _cRef; CDPA _dpaItems; int _iItem; }; STDMETHODIMP CShellItemList::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CShellItemList, IEnumShellItems), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CShellItemList::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CShellItemList::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDMETHODIMP CShellItemList::Next(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched) { HRESULT hr = S_FALSE; ULONG cFetched = 0; while (celt-- && SUCCEEDED(hr)) { if (_NextOne(&rgelt[cFetched])) cFetched++; else break; } if (cFetched) { *pceltFetched = cFetched; hr = S_OK; } else hr = S_FALSE; return hr; } STDMETHODIMP CShellItemList::Skip(ULONG celt) { _iItem += celt; return S_OK; } STDMETHODIMP CShellItemList::Reset() { _iItem = 0; return S_OK; } STDMETHODIMP CShellItemList::Clone(IEnumShellItems **ppenum) { return E_NOTIMPL; } HRESULT CShellItemList::Add(IShellItem *psi) { HRESULT hr = E_OUTOFMEMORY; if (!_dpaItems) { _dpaItems.Create(4); } if (_dpaItems) { if (-1 != _dpaItems.AppendPtr(psi)) { psi->AddRef(); hr = S_OK; } } return hr; } CShellItemList::~CShellItemList() { if (_dpaItems) { for (int i = 0; i < _dpaItems.GetPtrCount(); i++) { _dpaItems.FastGetPtr(i)->Release(); } _dpaItems.Destroy(); } } BOOL CShellItemList::_NextOne(IShellItem **ppsi) { if (_dpaItems && _iItem < _dpaItems.GetPtrCount()) { *ppsi = _dpaItems.GetPtr(_iItem); if (*ppsi) { (*ppsi)->AddRef(); _iItem++; return TRUE; } } return FALSE; } #ifdef RETURN_SHELLITEMS HRESULT CFileOpenBrowser::_ItemOKButtonPressed(LPCWSTR pszFile, OKBUTTONFLAGS Flags) { CShellItemList *psil = new CShellItemList(); HRESULT hr = psil ? S_OK : E_OUTOFMEMORY; ASSERT(IS_NEW_OFN(_pOFN)); if (SUCCEEDED(hr)) { SHSTR str; hr = str.SetSize(lstrlen(pszFile) * 2); if (SUCCEEDED(hr)) { WAIT_CURSOR w(this); DWORD cFiles = 1; SHExpandEnvironmentStrings(pszFile, str, str.GetSize()); if ((_pOFN->Flags & OFN_ALLOWMULTISELECT) && StrChr(str, CHAR_QUOTE)) { // need to handle MULTISEL here... // str points to a bunch of quoted strings. // alloc enough for the strings and an extra NULL terminator hr = str.SetSize(str.GetLen() + 1); if (SUCCEEDED(hr)) { cFiles = ConvertToNULLTerm(str); } } if (SUCCEEDED(hr)) { BOOL fSingle = cFiles == 1; LPTSTR pch = str; for (; cFiles; cFiles--) { IShellItem *psi; hr = _ParseShellItem(pch, &psi, fSingle); // go to the next item if (S_OK == hr) { hr = psil->Add(psi); psi->Release(); } else // S_FALSE or failure we stop parsing { if (FAILED(hr)) InvalidFileWarningNew(_hwndDlg, pch, OFErrFromHresult(hr)); break; } // goto the next string pch += lstrlen(pch) + 1; } // we have added everything to our list if (hr == S_OK) { hr = psil->QueryInterface(IID_PPV_ARG(IEnumShellItems, &(_pOFN->penum))); } } } psil->Release(); } return hr; } #endif RETURN_SHELLITEMS //////////////////////////////////////////////////////////////////////////// // // DriveList_OpenClose // // Change the state of a drive list. // //////////////////////////////////////////////////////////////////////////// #define OCDL_TOGGLE 0x0000 #define OCDL_OPEN 0x0001 #define OCDL_CLOSE 0x0002 void DriveList_OpenClose( UINT uAction, HWND hwndDriveList) { if (!hwndDriveList || !IsWindowVisible(hwndDriveList)) { return; } OpenClose_TryAgain: switch (uAction) { case (OCDL_TOGGLE) : { uAction = SendMessage(hwndDriveList, CB_GETDROPPEDSTATE, 0, 0L) ? OCDL_CLOSE : OCDL_OPEN; goto OpenClose_TryAgain; break; } case (OCDL_OPEN) : { SetFocus(hwndDriveList); SendMessage(hwndDriveList, CB_SHOWDROPDOWN, TRUE, 0); break; } case (OCDL_CLOSE) : { if (SHIsChildOrSelf(hwndDriveList,GetFocus()) == S_OK) { SendMessage(hwndDriveList, CB_SHOWDROPDOWN, FALSE, 0); } break; } } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::GetFullEditName // // Returns the number of characters needed to get the full path, including // the NULL. // //////////////////////////////////////////////////////////////////////////// UINT CFileOpenBrowser::GetFullEditName( LPTSTR pszBuf, UINT cchBuf, TEMPSTR *pTempStr, BOOL *pbNoDefExt) { UINT cTotalLen; HWND hwndEdit; if (_bUseHideExt) { cTotalLen = lstrlen(_pszHideExt) + 1; } else { if (_bUseCombo) { hwndEdit = (HWND)SendMessage(GetDlgItem(_hwndDlg, cmb13), CBEM_GETEDITCONTROL, 0, 0L); } else { hwndEdit = GetDlgItem(_hwndDlg, edt1); } cTotalLen = GetWindowTextLength(hwndEdit) + 1; } if (pTempStr) { if (!pTempStr->TSStrSize(cTotalLen)) { return ((UINT)-1); } pszBuf = *pTempStr; cchBuf = cTotalLen; } if (_bUseHideExt) { StringCchCopy(pszBuf, cchBuf, _pszHideExt); // Truncate, and return buffer size required. } else { GetWindowText(hwndEdit, pszBuf, cchBuf); } if (pbNoDefExt) { *pbNoDefExt = _bUseHideExt; } return (cTotalLen); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::ProcessEdit // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::ProcessEdit() { TEMPSTR pMultiSel; LPTSTR pszFile; BOOL bNoDefExt = TRUE; OKBUTTONFLAGS Flags = OKBUTTON_NONE; TCHAR szBuf[MAX_PATH + 4]; //if we have a saved pidl then use it instead if (_pidlSelection && _ProcessPidlSelection()) { return; } if (_pOFN->Flags & OFN_ALLOWMULTISELECT) { if (GetFullEditName(szBuf, ARRAYSIZE(szBuf), &pMultiSel, &bNoDefExt) == (UINT)-1) { // // FEATURE There should be some error message here. // return; } pszFile = pMultiSel; } else { if (_bSelIsObject) { pszFile = _pszObjectPath; } else { GetFullEditName(szBuf, ARRAYSIZE(szBuf), NULL, &bNoDefExt); pszFile = szBuf; PathRemoveBlanks(pszFile); int nLen = lstrlen(pszFile); if (*pszFile == CHAR_QUOTE) { LPTSTR pPrev = CharPrev(pszFile, pszFile + nLen); if (*pPrev == CHAR_QUOTE && pszFile != pPrev) { Flags |= OKBUTTON_QUOTED; // // Strip the quotes. // *pPrev = CHAR_NULL; StringCopyOverlap(pszFile, pszFile + 1); } } } } if (bNoDefExt) { Flags |= OKBUTTON_NODEFEXT; } // // Visual Basic passes in an uninitialized lpDefExts string. // Since we only have to use it in OKButtonPressed, update // lpstrDefExts here along with whatever else is only needed // in OKButtonPressed. // if (_pOFI->ApiType == COMDLG_ANSI) { ThunkOpenFileNameA2WDelayed(_pOFI); } // handle special case parsing right here. // our current folder and the desktop both failed // to figure out what this is. if (PathIsDotOrDotDot(pszFile)) { if (pszFile[1] == CHAR_DOT) { // this is ".." LPITEMIDLIST pidl = GetIDListFromFolder(_psfCurrent); if (pidl) { ILRemoveLastID(pidl); JumpToIDList(pidl); ILFree(pidl); } } } else if (OKButtonPressed(pszFile, Flags)) { BOOL bReturn = TRUE; if (_pOFN->lpstrFile) { if (!(_pOFN->Flags & OFN_NOVALIDATE)) { if (_pOFN->nMaxFile >= 3) { if ((_pOFN->lpstrFile[0] == 0) || (_pOFN->lpstrFile[1] == 0) || (_pOFN->lpstrFile[2] == 0)) { bReturn = FALSE; StoreExtendedError(FNERR_BUFFERTOOSMALL); } } else { bReturn = FALSE; StoreExtendedError(FNERR_BUFFERTOOSMALL); } } } _CleanupDialog(bReturn); } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::InitializeDropDown // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::InitializeDropDown(HWND hwndCtl) { if (!_bDropped) { MYLISTBOXITEM *pParentItem; SHChangeNotifyEntry fsne[2]; // // Expand the Desktop item. // pParentItem = GetListboxItem(hwndCtl, _iNodeDesktop); if (pParentItem) { UpdateLevel(hwndCtl, _iNodeDesktop + 1, pParentItem); fsne[0].pidl = pParentItem->pidlFull; fsne[0].fRecursive = FALSE; // // Look for the My Computer item, since it may not necessarily // be the next one after the Desktop. // LPITEMIDLIST pidlDrives; if (SHGetFolderLocation(NULL, CSIDL_DRIVES, NULL, 0, &pidlDrives) == S_OK) { int iNode = _iNodeDesktop; while (pParentItem = GetListboxItem(hwndCtl, iNode)) { if (ILIsEqual(pParentItem->pidlFull, pidlDrives)) { _iNodeDrives = iNode; break; } iNode++; } ILFree(pidlDrives); } // // Make sure My Computer was found. If not, then just assume it's // in the first spot after the desktop (this shouldn't happen). // if (pParentItem == NULL) { pParentItem = GetListboxItem(hwndCtl, _iNodeDesktop + 1); _iNodeDrives = _iNodeDesktop +1; } if (pParentItem) { // // Expand the My Computer item. // UpdateLevel(hwndCtl, _iNodeDrives + 1, pParentItem); _bDropped = TRUE; fsne[1].pidl = pParentItem->pidlFull; fsne[1].fRecursive = FALSE; } _uRegister = SHChangeNotifyRegister( _hwndDlg, SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_NewDelivery, SHCNE_ALLEVENTS & ~(SHCNE_CREATE | SHCNE_DELETE | SHCNE_RENAMEITEM), CDM_FSNOTIFY, pParentItem ? ARRAYSIZE(fsne) : ARRAYSIZE(fsne) - 1, fsne); } } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnCommandMessage // // Process a WM_COMMAND message for the dialog. // //////////////////////////////////////////////////////////////////////////// LRESULT CFileOpenBrowser::OnCommandMessage( WPARAM wParam, LPARAM lParam) { int idCmd = GET_WM_COMMAND_ID(wParam, lParam); switch (idCmd) { case (edt1) : { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case (EN_CHANGE) : { _bUseHideExt = FALSE; Pidl_Set(&_pidlSelection,NULL);; break; } } break; } case (cmb13) : { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case (CBN_EDITCHANGE) : { _bUseHideExt = FALSE; Pidl_Set(&_pidlSelection,NULL);; break; } case (CBN_DROPDOWN) : { LoadMRU(_szLastFilter, GET_WM_COMMAND_HWND(wParam, lParam), MAX_MRU); break; } case (CBN_SETFOCUS) : { SetModeBias(MODEBIASMODE_FILENAME); break; } case (CBN_KILLFOCUS) : { SetModeBias(MODEBIASMODE_DEFAULT); break; } } break; } case (cmb2) : { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case (CBN_CLOSEUP) : { OnSelChange(); UpdateNavigation(); SelectEditText(_hwndDlg); return TRUE; } case (CBN_DROPDOWN) : { InitializeDropDown(GET_WM_COMMAND_HWND(wParam, lParam)); break; } } break; } case (cmb1) : { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case (CBN_DROPDOWN) : { _iComboIndex = (int) SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), CB_GETCURSEL, NULL, NULL); break; } // // We're trying to see if anything changed after // (and only after) the user is done scrolling through the // drop down. When the user tabs away from the combobox, we // do not get a CBN_SELENDOK. // Why not just use CBN_SELCHANGE? Because then we'd refresh // the view (very slow) as the user scrolls through the // combobox. // case (CBN_CLOSEUP) : case (CBN_SELENDOK) : { // // Did anything change? // if (_iComboIndex >= 0 && _iComboIndex == SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), CB_GETCURSEL, NULL, NULL)) { break; } } case (MYCBN_DRAW) : { RefreshFilter(GET_WM_COMMAND_HWND(wParam, lParam)); _iComboIndex = -1; return TRUE; } default : { break; } } break; } case (IDC_PARENT) : { OnDotDot(); SelectEditText(_hwndDlg); break; } case (IDC_NEWFOLDER) : { ViewCommand(VC_NEWFOLDER); break; } case (IDC_VIEWLIST) : { SendMessage(_hwndView, WM_COMMAND, (WPARAM)SFVIDM_VIEW_LIST, 0); break; } case (IDC_VIEWDETAILS) : { SendMessage(_hwndView, WM_COMMAND, (WPARAM)SFVIDM_VIEW_DETAILS,0); break; } case (IDC_VIEWMENU) : { // // Pass off the nCmdID to the view for processing / translation. // DFVCMDDATA cd; cd.pva = NULL; cd.hwnd = _hwndDlg; cd.nCmdIDTranslated = 0; SendMessage(_hwndView, WM_COMMAND, SFVIDM_VIEW_VIEWMENU, (LONG_PTR)&cd); break; } case (IDOK) : { HWND hwndFocus = ::GetFocus(); if (hwndFocus == ::GetDlgItem(_hwndDlg, IDOK)) { hwndFocus = _hwndLastFocus; } hwndFocus = GetFocusedChild(_hwndDlg, hwndFocus); if (hwndFocus == _hwndView) { OnDblClick(TRUE); } else if (_hwndPlacesbar && (hwndFocus == _hwndPlacesbar)) { //Places bar has the focus. Get the current hot item. INT_PTR i = SendMessage(_hwndPlacesbar, TB_GETHOTITEM, 0,0); if (i >= 0) { //Get the Pidl for this button. TBBUTTONINFO tbbi; tbbi.cbSize = SIZEOF(tbbi); tbbi.lParam = 0; tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX; if (SendMessage(_hwndPlacesbar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi) >= 0) { LPITEMIDLIST pidl= (LPITEMIDLIST)tbbi.lParam; if (pidl) { //Jump to the location corresponding to this Button JumpToIDList(pidl, FALSE, TRUE); } } } } else { ProcessEdit(); } SelectEditText(_hwndDlg); break; } case (IDCANCEL) : { // the parse async can listen for this g_bUserPressedCancel = TRUE; _hwndModelessFocus = NULL; if (!_cRefCannotNavigate) { _CleanupDialog(FALSE); } return TRUE; } case (pshHelp) : { if (_hSubDlg) { CD_SendHelpNotify(_hSubDlg, _hwndDlg, _pOFN, _pOFI); } if (_pOFN->hwndOwner) { CD_SendHelpMsg(_pOFN, _hwndDlg, _pOFI->ApiType); } break; } case (IDC_DROPDRIVLIST) : // VK_F4 { // // If focus is on the "File of type" combobox, // then F4 should open that combobox, not the "Look in" one. // HWND hwnd = GetFocus(); if (_bUseCombo && (SHIsChildOrSelf(GetDlgItem(_hwndDlg, cmb13), hwnd) == S_OK) ) { hwnd = GetDlgItem(_hwndDlg, cmb13); } if ((hwnd != GetDlgItem(_hwndDlg, cmb1)) && (hwnd != GetDlgItem(_hwndDlg, cmb13)) ) { // // We shipped Win95 where F4 *always* opens the "Look in" // combobox, so keep F4 opening that even when it shouldn't. // hwnd = GetDlgItem(_hwndDlg, cmb2); } DriveList_OpenClose(OCDL_TOGGLE, hwnd); break; } case (IDC_REFRESH) : { if (_psv) { _psv->Refresh(); } break; } case (IDC_PREVIOUSFOLDER) : { OnDotDot(); break; } //Back Navigation case (IDC_BACK) : // Try to travel in the directtion if (_ptlog && SUCCEEDED(_ptlog->Travel(TRAVEL_BACK))) { LPITEMIDLIST pidl; //Able to travel in the given direction. //Now Get the new pidl _ptlog->GetCurrent(&pidl); //Update the UI to reflect the current state UpdateUI(pidl); //Jump to the new location // second paremeter is whether to translate to logical pidl // and third parameter is whether to add to the navigation stack // since this pidl comes from the stack , we should not add this to // the navigation stack JumpToIDList(pidl, FALSE, FALSE); ILFree(pidl); } break; } if ((idCmd >= IDC_PLACESBAR_BASE) && (idCmd <= (IDC_PLACESBAR_BASE + _iCommandID))) { TBBUTTONINFO tbbi; LPITEMIDLIST pidl; tbbi.cbSize = SIZEOF(tbbi); tbbi.lParam = 0; tbbi.dwMask = TBIF_LPARAM; if (SendMessage(_hwndPlacesbar, TB_GETBUTTONINFO, idCmd, (LPARAM)&tbbi) >= 0) { pidl = (LPITEMIDLIST)tbbi.lParam; if (pidl) { JumpToIDList(pidl, FALSE, TRUE); } } } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnCDMessage // // Process a special CommDlg message for the dialog. // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::OnCDMessage( UINT uMsg, WPARAM wParam, LPARAM lParam) { LONG lResult = -1; LPCITEMIDLIST pidl; LPTSTR pBuf = (LPTSTR)lParam; LPWSTR pBufW = NULL; int cbLen; // we should make some better thunk wrappers for COMDLG_ANSI // like OnCDMessageAorW() calls OnCDMessage() switch (uMsg) { case (CDM_GETSPEC) : case (CDM_GETFILEPATH) : case (CDM_GETFOLDERPATH) : { if (_pOFI->ApiType == COMDLG_ANSI) { if (pBufW = (LPWSTR)LocalAlloc(LPTR, (int)wParam * sizeof(WCHAR))) { pBuf = pBufW; } else { break; } } if (uMsg == CDM_GETSPEC) { lResult = GetFullEditName(pBuf, (UINT) wParam, NULL, NULL); break; } // else, fall thru... } case (CDM_GETFOLDERIDLIST) : { TCHAR szDir[MAX_PATH]; pidl = _pCurrentLocation->pidlFull; if (uMsg == CDM_GETFILEPATH) { // We can't necessarily use the (current folder) + (edit box name) thing in this case // because the (current folder) could be incorrect, for example in the case // where the current folder is the desktop folder. Items _could_ be in the // All Users desktop folder - in which case we want to return All Users\Desktop\file, not // \Desktop\file // So we'll key off _pidlSelection... if that doesn't work, we fall back to the old // behaviour, which could be incorrect in some cases. if (pidl && _pidlSelection) { LPITEMIDLIST pidlFull = ILCombine(pidl, _pidlSelection); if (pidlFull) { if (SHGetPathFromIDList(pidlFull, szDir)) { goto CopyAndReturn; } ILFree(pidlFull); } } } lResult = ILGetSize(pidl); if (uMsg == CDM_GETFOLDERIDLIST) { if ((LONG)wParam < lResult) { break; } CopyMemory((LPBYTE)pBuf, (LPBYTE)pidl, lResult); break; } if (!SHGetPathFromIDList(pidl, szDir)) { *szDir = 0; } if (!*szDir) { lResult = -1; break; } if (uMsg == CDM_GETFOLDERPATH) { CopyAndReturn: lResult = lstrlen(szDir) + 1; if ((LONG)wParam >= lResult) { // Ok to ignore failure. Spec calls for return value to be req'd buffer size // if the buffer isn't big enough. StringCchCopy(pBuf, lResult, szDir); } if (_pOFI->ApiType == COMDLG_ANSI) { lResult = WideCharToMultiByte(CP_ACP, 0, szDir, -1, NULL, 0, NULL, NULL); } if ((int)wParam > lResult) { wParam = lResult; } break; } // // We'll just fall through to the error case for now, since // doing the full combine is not an easy thing. // TCHAR szFile[MAX_PATH]; if (GetFullEditName(szFile, ARRAYSIZE(szFile), NULL, NULL) <= ARRAYSIZE(szFile) - 5) { if (PathCombine(szDir, szDir, szFile)) { goto CopyAndReturn; } // else the path was larger than maxpath! } // else we filled our buffer! lResult = -1; break; } case (CDM_SETCONTROLTEXT) : { if (_pOFI->ApiType == COMDLG_ANSI) { // // Need to convert pBuf (lParam) to Unicode. // cbLen = lstrlenA((LPSTR)pBuf) + 1; if (pBufW = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))) { SHAnsiToUnicode((LPSTR)pBuf,pBufW,cbLen); pBuf = pBufW; } } //Are we using combobox and the control they are setting is edit box? if (_bUseCombo && wParam == edt1) { //Change it to combo box. wParam = cmb13; } if (_bSave && wParam == IDOK) { _tszDefSave.TSStrCpy(pBuf); // // Do this to set the OK button correctly. // SelFocusChange(TRUE); } else { SetDlgItemText(_hwndDlg, (int) wParam, pBuf); } break; } case (CDM_HIDECONTROL) : { //Make sure the control id is not zero (0 is child dialog) if ((int)wParam != 0) { ShowWindow(GetDlgItem(_hwndDlg, (int) wParam), SW_HIDE); } break; } case (CDM_SETDEFEXT) : { if (_pOFI->ApiType == COMDLG_ANSI) { // // Need to convert pBuf (lParam) to Unicode. // cbLen = lstrlenA((LPSTR)pBuf) + 1; if (pBufW = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))) { SHAnsiToUnicode((LPSTR)pBuf,pBufW,cbLen); pBuf = pBufW; } } _pszDefExt.TSStrCpy(pBuf); _bNoInferDefExt = TRUE; break; } default: { lResult = -1; break; } } SetWindowLongPtr(_hwndDlg, DWLP_MSGRESULT, lResult); if (_pOFI->ApiType == COMDLG_ANSI) { switch (uMsg) { case (CDM_GETSPEC) : case (CDM_GETFILEPATH) : case (CDM_GETFOLDERPATH) : { // // Need to convert pBuf (pBufW) to Ansi and store in lParam. // if (wParam && lParam) { SHUnicodeToAnsi(pBuf,(LPSTR)lParam,(int) wParam); } break; } } if (pBufW) { LocalFree(pBufW); } } return TRUE; } //////////////////////////////////////////////////////////////////////////// // // OKSubclass // // Subclass window proc for the OK button. // // The OK button is subclassed so we know which control had focus before // the user clicked OK. This in turn lets us know whether to process OK // based on the current selection in the listview, or the current text // in the edit control. // //////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK OKSubclass( HWND hOK, UINT msg, WPARAM wParam, LPARAM lParam) { HWND hwndDlg = ::GetParent(hOK); CFileOpenBrowser *pDlgStruct = HwndToBrowser(hwndDlg); WNDPROC pOKProc = pDlgStruct ? pDlgStruct->_lpOKProc : NULL; if (pDlgStruct) { switch (msg) { case WM_SETFOCUS: pDlgStruct->_hwndLastFocus = (HWND)wParam; break; } } return ::CallWindowProc(pOKProc, hOK, msg, wParam, lParam); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::GetNodeFromIDList // //////////////////////////////////////////////////////////////////////////// int CFileOpenBrowser::GetNodeFromIDList( LPCITEMIDLIST pidl) { int i; HWND hwndCB = GetDlgItem(_hwndDlg, cmb2); Assert(this->_bDropped); // // Just check DRIVES and DESKTOP. // for (i = _iNodeDrives; i >= NODE_DESKTOP; --i) { MYLISTBOXITEM *pItem = GetListboxItem(hwndCB, i); if (pItem && ILIsEqual(pidl, pItem->pidlFull)) { break; } } return (i); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::FSChange // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::FSChange( LONG lNotification, LPCITEMIDLIST *ppidl) { int iNode = -1; LPCITEMIDLIST pidl = ppidl[0]; switch (lNotification) { case (SHCNE_RENAMEFOLDER) : { LPCITEMIDLIST pidlExtra = ppidl[1]; // // Rename is special. We need to invalidate both // the pidl and the pidlExtra, so we call ourselves. // FSChange(0, &pidlExtra); } case (0) : case (SHCNE_MKDIR) : case (SHCNE_RMDIR) : { LPITEMIDLIST pidlClone = ILClone(pidl); if (!pidlClone) { break; } ILRemoveLastID(pidlClone); iNode = GetNodeFromIDList(pidlClone); ILFree(pidlClone); break; } case (SHCNE_UPDATEITEM) : case (SHCNE_NETSHARE) : case (SHCNE_NETUNSHARE) : case (SHCNE_UPDATEDIR) : { iNode = GetNodeFromIDList(pidl); break; } case (SHCNE_DRIVEREMOVED) : case (SHCNE_DRIVEADD) : case (SHCNE_MEDIAINSERTED) : case (SHCNE_MEDIAREMOVED) : case (SHCNE_DRIVEADDGUI) : { iNode = _iNodeDrives; break; } } if (iNode >= 0) { // // We want to delay the processing a little because we always do // a full update, so we should accumulate. // SetTimer(_hwndDlg, TIMER_FSCHANGE + iNode, 100, NULL); } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::Timer // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::Timer( WPARAM wID) { KillTimer(_hwndDlg, (UINT) wID); wID -= TIMER_FSCHANGE; ASSERT(this->_bDropped); HWND hwndCB; MYLISTBOXITEM *pParentItem; hwndCB = GetDlgItem(_hwndDlg, cmb2); pParentItem = GetListboxItem(hwndCB, wID); UpdateLevel(hwndCB, (int) wID + 1, pParentItem); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnGetMinMax // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::OnGetMinMax( LPMINMAXINFO pmmi) { if ((_ptMinTrack.x != 0) || (_ptMinTrack.y != 0)) { pmmi->ptMinTrackSize = _ptMinTrack; } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::OnSize // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::OnSize( int width, int height) { RECT rcMaster; RECT rcView; RECT rc; HWND hwnd; HDWP hdwp; int dx; int dy; // // Set the sizing grip to the correct location. // SetWindowPos(_hwndGrip, NULL, width - g_cxGrip, height - g_cyGrip, g_cxGrip, g_cyGrip, SWP_NOZORDER | SWP_NOACTIVATE); // // Ignore sizing until we are initialized. // if ((_ptLastSize.x == 0) && (_ptLastSize.y == 0)) { return; } GetWindowRect(_hwndDlg, &rcMaster); // // Calculate the deltas in the x and y positions that we need to move // each of the child controls. // dx = (rcMaster.right - rcMaster.left) - _ptLastSize.x; dy = (rcMaster.bottom - rcMaster.top) - _ptLastSize.y; //Dont do anything if the size remains the same if ((dx == 0) && (dy == 0)) { return; } // // Update the new size. // _ptLastSize.x = rcMaster.right - rcMaster.left; _ptLastSize.y = rcMaster.bottom - rcMaster.top; // // Size the view. // GetWindowRect(_hwndView, &rcView); MapWindowRect(HWND_DESKTOP, _hwndDlg, &rcView); hdwp = BeginDeferWindowPos(10); if (hdwp) { hdwp = DeferWindowPos(hdwp, _hwndGrip, NULL, width - g_cxGrip, height - g_cyGrip, g_cxGrip, g_cyGrip, SWP_NOZORDER | SWP_NOACTIVATE); if (hdwp) { hdwp = DeferWindowPos(hdwp, _hwndView, NULL, 0, 0, rcView.right - rcView.left + dx, // resize x rcView.bottom - rcView.top + dy, // resize y SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } #if 0 // // Can't do this because some sub-dialogs are dependent on the // original size of this control. Instead we just try to rely on // the size of the _hwndView above. // hwnd = GetDlgItem(_hwndDlg, lst1); if (hdwp) { hdwp = DeferWindowPos(hdwp, hwnd, NULL, 0, 0, rcView.right - rcView.left + dx, // resize x rcView.bottom - rcView.top + dy, // resize y SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } #endif } // // Move the controls. // hwnd = ::GetWindow(_hwndDlg, GW_CHILD); while (hwnd && hdwp) { if ((hwnd != _hSubDlg) && (hwnd != _hwndGrip) && (hdwp)) { GetWindowRect(hwnd, &rc); MapWindowRect(HWND_DESKTOP, _hwndDlg, &rc); // // See if the control needs to be adjusted. // if (rc.top > rcView.bottom) { switch (GetDlgCtrlID(hwnd)) { case (edt1) : case (cmb13) : case (cmb1) : { //Increase the width of these controls hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left, rc.top + dy, RECTWIDTH(rc) + dx, RECTHEIGHT(rc), SWP_NOZORDER); break; } case (IDOK): case (IDCANCEL): case (pshHelp): { //Move these controls to the right hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left + dx, rc.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE); break; } default : { // // The control is below the view, so adjust the y // coordinate appropriately. // hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left, rc.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } } } else if (rc.left > rcView.right) { // // The control is to the right of the view, so adjust the // x coordinate appropriately. // hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left + dx, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } else { int id = GetDlgCtrlID(hwnd); switch (id) { case (cmb2) : { // // Size this one larger. // hdwp = DeferWindowPos(hdwp, hwnd, NULL, 0, 0, RECTWIDTH(rc) + dx, RECTHEIGHT(rc), SWP_NOZORDER | SWP_NOMOVE); break; } case ( IDOK) : if ((SHGetAppCompatFlags(ACF_FILEOPENBOGUSCTRLID) & ACF_FILEOPENBOGUSCTRLID) == 0) break; // else continue through - toolbar bar has ctrlid == IDOK, so we will resize that. case ( stc1 ) : // // Move the toolbar right by dx. // hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left + dx, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); break; case ( ctl1 ) : { // Size the places bar vertically hdwp = DeferWindowPos(hdwp, hwnd, NULL, 0, 0, RECTWIDTH(rc), RECTHEIGHT(rc) + dy, SWP_NOZORDER | SWP_NOMOVE); break; } } } } hwnd = ::GetWindow(hwnd, GW_HWNDNEXT); } if (!hdwp) { return; } EndDeferWindowPos(hdwp); if (_hSubDlg) { hdwp = NULL; hwnd = ::GetWindow(_hSubDlg, GW_CHILD); while (hwnd) { GetWindowRect(hwnd, &rc); MapWindowRect(HWND_DESKTOP, _hSubDlg, &rc); // // See if the control needs to be adjusted. // if (rc.top > rcView.bottom) { // // The control is below the view, so adjust the y // coordinate appropriately. // if (hdwp == NULL) { hdwp = BeginDeferWindowPos(10); } if (hdwp) { hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left, rc.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } } else if (rc.left > rcView.right) { // // The control is to the right of the view, so adjust the // x coordinate appropriately. // if (hdwp == NULL) { hdwp = BeginDeferWindowPos(10); } if (hdwp) { hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left + dx, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } } hwnd = ::GetWindow(hwnd, GW_HWNDNEXT); } if (hdwp) { EndDeferWindowPos(hdwp); // // Size the sub dialog. // SetWindowPos(_hSubDlg, NULL, 0, 0, _ptLastSize.x, // make it the same _ptLastSize.y, // make it the same SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::VerifyListViewPosition // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::VerifyListViewPosition() { RECT rcList, rcView; FOLDERSETTINGS fs; // // Get the rectangle for both the list view and the hidden list box. // GetControlRect(_hwndDlg, lst1, &rcList); rcView.left = 0; if ((!GetWindowRect(_hwndView, &rcView)) || (!MapWindowRect(HWND_DESKTOP, _hwndDlg, &rcView))) { return; } // // See if the list view is off the screen and the list box is not. // if ((rcView.left < 0) && (rcList.left >= 0)) { // // Reset the list view to the list box position. // if (_pCurrentLocation) { if (_psv) { _psv->GetCurrentInfo(&fs); } else { fs.ViewMode = FVM_LIST; fs.fFlags = _pOFN->Flags & OFN_ALLOWMULTISELECT ? 0 : FWF_SINGLESEL; } SwitchView(_pCurrentLocation->GetShellFolder(), _pCurrentLocation->pidlFull, &fs, NULL, FALSE); } } } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::UpdateNavigation // This function updates the navigation stack by adding the current // pidl to the stack //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::UpdateNavigation() { WPARAM iItem; HWND hwndCombo = GetDlgItem(_hwndDlg, cmb2); iItem = SendMessage(hwndCombo, CB_GETCURSEL, NULL, NULL); MYLISTBOXITEM *pNewLocation = GetListboxItem(hwndCombo, iItem); if (_ptlog && pNewLocation && pNewLocation->pidlFull) { LPITEMIDLIST pidl; _ptlog->GetCurrent(&pidl); if (pidl && (!ILIsEqual(pNewLocation->pidlFull, pidl))) { _ptlog->AddEntry(pNewLocation->pidlFull); } if (pidl) { ILFree(pidl); } } //Update the UI UpdateUI(_pCurrentLocation ? _pCurrentLocation->pidlFull : NULL); } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::UpdateUI // //////////////////////////////////////////////////////////////////////////// void CFileOpenBrowser::UpdateUI(LPITEMIDLIST pidlNew) { TBBUTTONINFO tbbi; LPITEMIDLIST pidl; ::SendMessage(_hwndToolbar, TB_ENABLEBUTTON, IDC_BACK, _ptlog ? _ptlog->CanTravel(TRAVEL_BACK) : 0); if (_iCheckedButton >= 0) { //Reset the Hot Button ::SendMessage(_hwndPlacesbar, TB_CHECKBUTTON, (WPARAM)_iCheckedButton, MAKELONG(FALSE,0)); _iCheckedButton = -1; } if (pidlNew) { //Get Each Toolbar Buttons pidl and see if the current pidl matches for (int i=0; i < MAXPLACESBARITEMS; i++) { tbbi.cbSize = SIZEOF(tbbi); tbbi.lParam = 0; tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX | TBIF_COMMAND; if (SendMessage(_hwndPlacesbar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi) >= 0) { pidl = (LPITEMIDLIST)tbbi.lParam; if (pidl && ILIsEqual(pidlNew, pidl)) { _iCheckedButton = tbbi.idCommand; break; } } } if (_iCheckedButton >= 0) { ::SendMessage(_hwndPlacesbar, TB_CHECKBUTTON, (WPARAM)_iCheckedButton, MAKELONG(TRUE,0)); } } } //////////////////////////////////////////////////////////////////////////// // // OpenDlgProc // // Main dialog procedure for file open dialogs. // //////////////////////////////////////////////////////////////////////////// BOOL_PTR CALLBACK OpenDlgProc( HWND hDlg, // window handle of the dialog box UINT message, // type of message WPARAM wParam, // message-specific information LPARAM lParam) { CFileOpenBrowser *pDlgStruct = HwndToBrowser(hDlg); // we divide the message processing into two switch statments: // those who don't use pDlgStruct first and then those who do. switch (message) { case WM_INITDIALOG: { // // Initialize dialog box. // LPOFNINITINFO poii = (LPOFNINITINFO)lParam; if (CDGetAppCompatFlags() & CDACF_MATHCAD) { if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) ::EndDialog(hDlg, FALSE); } poii->hrOleInit = SHOleInitialize(0); if (!InitLocation(hDlg, poii)) { ::EndDialog(hDlg, FALSE); } if (!gp_uQueryCancelAutoPlay) { // try to register for autoplay messages gp_uQueryCancelAutoPlay = RegisterWindowMessage(TEXT("QueryCancelAutoPlay")); } // // Always return FALSE to indicate we have already set the focus. // return FALSE; } break; case WM_DESTROY: { RECT r; //Cache in this dialogs size and position so that new //dialog are created at this location and size GetWindowRect(hDlg, &r); if (pDlgStruct && (pDlgStruct->_bEnableSizing)) { g_rcDlg = r; } // // Make sure we do not respond to any more messages. // StoreBrowser(hDlg, NULL); ClearListbox(GetDlgItem(hDlg, cmb2)); // Unsubclass the ok button now, otherwise we leak the button control, // because in OkSubclass we won't be able to forward the WM_NCDESTORY since // the original wndproc will have been nuked in pDlgStruct->_lpOKProc if (pDlgStruct) { SetWindowLongPtr(::GetDlgItem(hDlg, IDOK), GWLP_WNDPROC, (LONG_PTR)pDlgStruct->_lpOKProc); } if (pDlgStruct) { pDlgStruct->Release(); } return FALSE; } break; case WM_ACTIVATE: { if (wParam == WA_INACTIVE) { // // Make sure some other Open dialog has not already grabbed // the focus. This is a process global, so it should not // need to be protected. // if (gp_hwndActiveOpen == hDlg) { gp_hwndActiveOpen = NULL; } } else { gp_hwndActiveOpen = hDlg; } return FALSE; } break; case WM_MEASUREITEM: { if (!g_cxSmIcon && !g_cySmIcon) { HIMAGELIST himl; Shell_GetImageLists(NULL, &himl); ImageList_GetIconSize(himl, &g_cxSmIcon, &g_cySmIcon); } MeasureDriveItems(hDlg, (MEASUREITEMSTRUCT*)lParam); return TRUE; } break; case CWM_GETISHELLBROWSER: { ::SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LRESULT)pDlgStruct); return TRUE; } break; case WM_DEVICECHANGE: { if (DBT_DEVICEARRIVAL == wParam) { // and refresh our view in case this was a notification for the folder // we are viewing. avoids making the user do a manual refresh DEV_BROADCAST_VOLUME *pbv = (DEV_BROADCAST_VOLUME *)lParam; if (pbv->dbcv_flags & DBTF_MEDIA) { int chRoot; TCHAR szPath[MAX_PATH]; if (pDlgStruct->GetDirectoryFromLB(szPath, &chRoot)) { int iDrive = PathGetDriveNumber(szPath); if (iDrive != -1 && ((1 << iDrive) & pbv->dbcv_unitmask)) { // refresh incase this was this folder PostMessage(hDlg, WM_COMMAND, IDC_REFRESH, 0); } } } } return TRUE; } break; default: if (message == gp_uQueryCancelAutoPlay) { // cancel the autoplay SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 1); return TRUE; } break; } // NOTE: // all of the messages below require that we have a valid pDlgStruct. if you // don't refrence pDlgStruct, then add your msg to the switch statement above. if (pDlgStruct) { switch (message) { case WM_COMMAND: { return ((BOOL_PTR)pDlgStruct->OnCommandMessage(wParam, lParam)); } break; case WM_DRAWITEM: { pDlgStruct->PaintDriveLine((DRAWITEMSTRUCT *)lParam); // // Make sure the list view is in the same place as the // list box. Apps like VB move the list box off of the // dialog. If the list view is placed on the list box // before the list box gets moved back to the dialog, we // end up with an ugly gray spot. // pDlgStruct->VerifyListViewPosition(); return TRUE; } break; case WM_NOTIFY: { return (BOOL_PTR)pDlgStruct->OnNotify((LPNMHDR)lParam); } break; case WM_SETCURSOR: { if (pDlgStruct->OnSetCursor()) { SetDlgMsgResult(hDlg, message, (LRESULT)TRUE); return TRUE; } } break; case WM_HELP: { HWND hwndItem = (HWND)((LPHELPINFO)lParam)->hItemHandle; if (hwndItem != pDlgStruct->_hwndToolbar) { HWND hwndItem = (HWND)((LPHELPINFO)lParam)->hItemHandle; // We assume that the defview has one child window that // covers the entire defview window. HWND hwndDefView = GetDlgItem(hDlg, lst2); if (GetParent(hwndItem) == hwndDefView) { hwndItem = hwndDefView; } WinHelp(hwndItem, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)(pDlgStruct->_bSave ? aFileSaveHelpIDs : aFileOpenHelpIDs)); } return TRUE; } break; case WM_CONTEXTMENU: { if ((HWND)wParam != pDlgStruct->_hwndToolbar) { WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)(pDlgStruct->_bSave ? aFileSaveHelpIDs : aFileOpenHelpIDs)); } return TRUE; } break; case CDM_SETSAVEBUTTON: { pDlgStruct->RealSetSaveButton((UINT)wParam); } break; case CDM_FSNOTIFY: { LPITEMIDLIST *ppidl; LONG lEvent; BOOL bRet; LPSHChangeNotificationLock pLock; // Get the change notification info from the shared memory // block identified by the handle passed in the wParam. pLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent); if (pLock == NULL) { pDlgStruct->_bDropped = FALSE; return FALSE; } bRet = pDlgStruct->FSChange(lEvent, (LPCITEMIDLIST *)ppidl); // Release the shared block. SHChangeNotification_Unlock(pLock); return bRet; } break; case CDM_SELCHANGE: { pDlgStruct->_fSelChangedPending = FALSE; pDlgStruct->SelFocusChange(TRUE); if (pDlgStruct->_hSubDlg) { CD_SendSelChangeNotify(pDlgStruct->_hSubDlg, hDlg, pDlgStruct->_pOFN, pDlgStruct->_pOFI); } } break; case WM_TIMER: { pDlgStruct->Timer(wParam); } break; case WM_GETMINMAXINFO: { if (pDlgStruct->_bEnableSizing) { pDlgStruct->OnGetMinMax((LPMINMAXINFO)lParam); return FALSE; } } break; case WM_SIZE: { if (pDlgStruct->_bEnableSizing) { pDlgStruct->OnSize(LOWORD(lParam), HIWORD(lParam)); return TRUE; } } break; case WM_NCCALCSIZE: { // AppHack for Borland JBuilder: Need to keep track of whether // any redraw requests have come in. pDlgStruct->_bAppRedrawn = TRUE; } break; case WM_THEMECHANGED: { // Need to change some parameters on the placesbar for this. pDlgStruct->OnThemeActive(hDlg, IsAppThemed()); return TRUE; } break; case WM_SETTINGCHANGE: { // If icon size has changed, we need to regenerate the places bar. pDlgStruct->_RecreatePlacesbar(); return FALSE; } break; default: { if (IsInRange(message, CDM_FIRST, CDM_LAST) && pDlgStruct) { return pDlgStruct->OnCDMessage(message, wParam, lParam); } } } } // Did not process the message. return FALSE; } //////////////////////////////////////////////////////////////////////////// // // OpenFileHookProc // //////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK OpenFileHookProc( int nCode, WPARAM wParam, LPARAM lParam) { MSG *lpMsg; if (nCode < 0) { return (DefHookProc(nCode, wParam, lParam, &gp_hHook)); } if (nCode != MSGF_DIALOGBOX) { return (0); } lpMsg = (MSG *)lParam; // // Check if this message is for the last active OpenDialog in this // process. // // Note: This is only done for WM_KEY* messages so that we do not slow // down this window too much. // if (IsInRange(lpMsg->message, WM_KEYFIRST, WM_KEYLAST)) { HWND hwndActiveOpen = gp_hwndActiveOpen; HWND hwndFocus = GetFocusedChild(hwndActiveOpen, lpMsg->hwnd); CFileOpenBrowser *pDlgStruct; if (hwndFocus && (pDlgStruct = HwndToBrowser(hwndActiveOpen)) != NULL) { if (pDlgStruct->_psv && (hwndFocus == pDlgStruct->_hwndView)) { if (pDlgStruct->_psv->TranslateAccelerator(lpMsg) == S_OK) { return (1); } if (gp_haccOpenView && TranslateAccelerator(hwndActiveOpen, gp_haccOpenView, lpMsg)) { return (1); } } else { if (gp_haccOpen && TranslateAccelerator(hwndActiveOpen, gp_haccOpen, lpMsg)) { return (1); } // // Note that the view won't be allowed to translate when the // focus is not there. // } } } return (0); } //////////////////////////////////////////////////////////////////////////// // // NewGetFileName // //////////////////////////////////////////////////////////////////////////// BOOL NewGetFileName( LPOPENFILEINFO lpOFI, BOOL bSave) { OFNINITINFO oii = { lpOFI, bSave, FALSE, -1}; LPOPENFILENAME lpOFN = lpOFI->pOFN; BOOL bHooked = FALSE; WORD wErrorMode; HRSRC hResInfo; HGLOBAL hDlgTemplate; LPDLGTEMPLATE pDlgTemplate; int nRet; LANGID LangID; //Initialize the common controls INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(INITCOMMONCONTROLSEX); icc.dwICC = ICC_USEREX_CLASSES; //ComboBoxEx class InitCommonControlsEx(&icc); if ((lpOFN->lStructSize != sizeof(OPENFILENAME)) && (lpOFN->lStructSize != OPENFILENAME_SIZE_VERSION_400) ) { StoreExtendedError(CDERR_STRUCTSIZE); return FALSE; } // // OFN_ENABLEINCLUDENOTIFY requires OFN_EXPLORER and OFN_ENABLEHOOK. // if (lpOFN->Flags & OFN_ENABLEINCLUDENOTIFY) { if ((!(lpOFN->Flags & OFN_EXPLORER)) || (!(lpOFN->Flags & OFN_ENABLEHOOK))) { StoreExtendedError(CDERR_INITIALIZATION); return FALSE; } } wErrorMode = (WORD)SetErrorMode(SEM_NOERROR); SetErrorMode(SEM_NOERROR | wErrorMode); // // There ought to be a better way. I am compelled to keep the hHook in a // global because my callback needs it, but I have no lData where I could // possibly store it. // Note that we initialize nHookRef to -1 so we know when the first // increment is. // if (InterlockedIncrement((LPLONG)&gp_nHookRef) == 0) { gp_hHook = SetWindowsHookEx(WH_MSGFILTER, OpenFileHookProc, 0, GetCurrentThreadId()); if (gp_hHook) { bHooked = TRUE; } else { --gp_nHookRef; } } else { bHooked = TRUE; } if (!gp_haccOpen) { gp_haccOpen = LoadAccelerators(g_hinst, MAKEINTRESOURCE(IDA_OPENFILE)); } if (!gp_haccOpenView) { gp_haccOpenView = LoadAccelerators(g_hinst, MAKEINTRESOURCE(IDA_OPENFILEVIEW)); } g_cxGrip = GetSystemMetrics(SM_CXVSCROLL); g_cyGrip = GetSystemMetrics(SM_CYHSCROLL); // // Get the dialog resource and load it. // nRet = FALSE; WORD wResID; // if the version of the structure passed is older than the current version and the application // has specified hook or template or template handle then use template corresponding to that version // else use the new file open template if (((lpOFI->iVersion < OPENFILEVERSION) && (lpOFI->pOFN->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))) || (IsRestricted(REST_NOPLACESBAR)) || (IS_NEW_OFN(lpOFI->pOFN) && (lpOFI->pOFN->FlagsEx & OFN_EX_NOPLACESBAR)) ) { wResID = NEWFILEOPENORD; } else { wResID = NEWFILEOPENV2ORD; } LangID = GetDialogLanguage(lpOFN->hwndOwner, NULL); // // Warning! Warning! Warning! // // We have to set g_tlsLangID before any call for CDLoadString // TlsSetValue(g_tlsLangID, (void *) LangID); if ((hResInfo = FindResourceExFallback(::g_hinst, RT_DIALOG, MAKEINTRESOURCE(wResID), LangID)) && (hDlgTemplate = LoadResource(::g_hinst, hResInfo)) && (pDlgTemplate = (LPDLGTEMPLATE)LockResource(hDlgTemplate))) { ULONG cbTemplate = SizeofResource(::g_hinst, hResInfo); LPDLGTEMPLATE pDTCopy = (LPDLGTEMPLATE)LocalAlloc(LPTR, cbTemplate); if (pDTCopy) { CopyMemory(pDTCopy, pDlgTemplate, cbTemplate); UnlockResource(hDlgTemplate); FreeResource(hDlgTemplate); if ((lpOFN->Flags & OFN_ENABLESIZING) || (!(lpOFN->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))) { if (((LPDLGTEMPLATE2)pDTCopy)->wSignature == 0xFFFF) { //This is a dialogex template ((LPDLGTEMPLATE2)pDTCopy)->style |= WS_SIZEBOX; } else { //This is a dialog template ((LPDLGTEMPLATE)pDTCopy)->style |= WS_SIZEBOX; } oii.bEnableSizing = TRUE; } oii.hrOleInit = E_FAIL; nRet = (BOOL)DialogBoxIndirectParam(::g_hinst, pDTCopy, lpOFN->hwndOwner, OpenDlgProc, (LPARAM)(LPOFNINITINFO)&oii); //Unintialize OLE SHOleUninitialize(oii.hrOleInit); if (CDGetAppCompatFlags() & CDACF_MATHCAD) { CoUninitialize(); } LocalFree(pDTCopy); } } if (bHooked) { // // Put this in a local so we don't need a critical section. // HHOOK hHook = gp_hHook; if (InterlockedDecrement((LPLONG)&gp_nHookRef) < 0) { UnhookWindowsHookEx(hHook); } } switch (nRet) { case (TRUE) : { break; } case (FALSE) : { if ((!g_bUserPressedCancel) && (!GetStoredExtendedError())) { StoreExtendedError(CDERR_DIALOGFAILURE); } break; } default : { StoreExtendedError(CDERR_DIALOGFAILURE); nRet = FALSE; break; } } // // // There is a race condition here where we free dlls but a thread // using this stuff still hasn't terminated so we page fault. // FreeImports(); SetErrorMode(wErrorMode); return (nRet); } extern "C" { //////////////////////////////////////////////////////////////////////////// // // NewGetOpenFileName // //////////////////////////////////////////////////////////////////////////// BOOL NewGetOpenFileName( LPOPENFILEINFO lpOFI) { return (NewGetFileName(lpOFI, FALSE)); } //////////////////////////////////////////////////////////////////////////// // // NewGetSaveFileName // //////////////////////////////////////////////////////////////////////////// BOOL NewGetSaveFileName( LPOPENFILEINFO lpOFI) { return (NewGetFileName(lpOFI, TRUE)); } } // extern "C" //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_ValidateSelectedFile // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::_ValidateSelectedFile(LPCTSTR pszFile, int *pErrCode) { // // Successfully opened. // // Note: (pfortier) If/when IShellItem is removed from this version of comdlg, the // following if statement should probably revert to // if ((_pOFN->Flags & OFN_NOREADONLYRETURN) && // and the next one to // if (_bSave || (_pOFN->Flags & OFN_NOREADONLYRETURN)) // // These were changed in order to be consistent with w2k behaviour regarding // message box errors that appear when OFN_NOREADONLYRETURN is specified and // the user selects a readonly file - the point of contention is that errors were // not shown in win2k when it was an OpenFile dialog. IShellItem changes modified // the codepath such that errors were now produced when in an OpenFile dialog. // To compensate, the logic has been changed here. DWORD dwAttrib = GetFileAttributes(pszFile); if ((_pOFN->Flags & OFN_NOREADONLYRETURN) && _bSave && (0xFFFFFFFF != dwAttrib) && (dwAttrib & FILE_ATTRIBUTE_READONLY)) { *pErrCode = OF_LAZYREADONLY; return FALSE; } if (_bSave) { *pErrCode = WriteProtectedDirCheck((LPTSTR)pszFile); if (*pErrCode) { return FALSE; } } if (_pOFN->Flags & OFN_OVERWRITEPROMPT) { if (_bSave && PathFileExists(pszFile) && !FOkToWriteOver(_hwndDlg, (LPTSTR)pszFile)) { if (_bUseCombo) { PostMessage(_hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(_hwndDlg, cmb13), 1); } else { PostMessage(_hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(_hwndDlg, edt1), 1); } return FALSE; } } return TRUE; } //////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_ProcessPidlSelection // //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::_ProcessPidlSelection() { IShellItem *psi; if (SUCCEEDED(SHCreateShellItem(_pCurrentLocation->pidlFull, _psfCurrent, _pidlSelection, &psi))) { IShellItem *psiReal; HRESULT hr = _TestShellItem(psi, TRUE, &psiReal); if (S_OK == hr) { hr = _ProcessItemAsFile(psiReal); psiReal->Release(); } psi->Release(); // if there was any kind of error then we fall back // to the old code to show errors and the like return SUCCEEDED(hr); } return FALSE; } /////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_ProcessItemAsFile // //////////////////////////////////////////////////////////////////////////// HRESULT CFileOpenBrowser::_ProcessItemAsFile(IShellItem *psi) { LPTSTR pszPath; HRESULT hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath); if (FAILED(hr)) { hr = _MakeFakeCopy(psi, &pszPath); } if (SUCCEEDED(hr)) { int nErrCode; hr = E_FAIL; if (_ValidateSelectedFile(pszPath, &nErrCode)) { DWORD dwError = 0; int nFileOffset = _CopyFileNameToOFN(pszPath, &dwError); ASSERT(nFileOffset >= 0); _CopyTitleToOFN(pszPath+nFileOffset); if (dwError) { StoreExtendedError(dwError); } else { // Only PostProcess is there was no error copying our info to the OFN (e.g. buffers not big enough) _PostProcess(pszPath); } _CleanupDialog((dwError == NOERROR)); hr = S_OK; } else { //Check to see if there is an error in the file or user pressed no for overwrite prompt // if user pressed no to overwritte prompt then return true if (nErrCode == 0) hr = S_FALSE; // Otherwise, return failure. } CoTaskMemFree(pszPath); } return hr; } /////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_ProcessPidlAsShellItem // //////////////////////////////////////////////////////////////////////////// #ifdef RETURN_SHELLITEMS HRESULT CFileOpenBrowser::_ProcessShellItem(IShellItem *psi) { CShellItemList *psil = new CShellItemList(); HRESULT hr = E_OUTOFMEMORY; ASSERT(IS_NEW_OFN(_pOFN)); if (psil) { hr = psil->Add(psi); // we have added everything to our list if (SUCCEEDED(hr)) { hr = psil->QueryInterface(IID_PPV_ARG(IEnumShellItems, &(_pOFN->penum))); } psil->Release(); } return hr; } #endif RETURN_SHELLITEMS /////////////////////////////////////////////////////////////////////////// // // CFileOpenBrowser::_PostProcess // // This functions does all the bookkeeping operations that needs to be // done when File Open/Save Dialog closes. //////////////////////////////////////////////////////////////////////////// BOOL CFileOpenBrowser::_PostProcess(LPTSTR pszFile) { int nFileOffset = ParseFileNew(pszFile, NULL, FALSE, TRUE); //Set the last visited directory for this application. //We should this all the time regardless of how we opened b'cos app may specify an initial //directory(many apps do this in Save As case) but the user might decide to save it in a differnt directory //in this case we need to save the directory where user saved. AddToLastVisitedMRU(pszFile, nFileOffset); //Add to recent documents. if (!(_pOFN->Flags & OFN_DONTADDTORECENT)) { SHAddToRecentDocs(SHARD_PATH, pszFile); //Add to the file mru AddToMRU(_pOFN); } // Check to see if we need to set Read only bit or not if (!(_pOFN->Flags & OFN_HIDEREADONLY)) { // // Read-only checkbox visible? // if (IsDlgButtonChecked(_hwndDlg, chx1)) { _pOFN->Flags |= OFN_READONLY; } else { _pOFN->Flags &= ~OFN_READONLY; } } return TRUE; }