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

8850 lines
226 KiB

/*++
Copyright (c) 1990-1995, Microsoft Corporation All rights reserved.
Module Name:
filenew.cpp
Abstract:
This module implements the Win32 explorer fileopen dialogs.
Revision History:
--*/
//
// Include Files.
//
#define _SHELL32_
#define _COMCTL32_
#define _INC_OLE
extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
}
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
#include <shlapip.h>
#include <shell2.h>
#include <shellp.h>
#include <commctrl.h>
#include <comctrlp.h>
#include <ole2.h>
#include <shlobj.h>
#include "privcomd.h"
#include "cdids.h"
#include "isz.h"
#include "fileopen.h"
#define INITGUID
#include <initguid.h>
#include <coguid.h>
#include <shlguid.h>
#include <shguidp.h>
#include <oleguid.h>
//
// Constant Declarations.
//
#define IDOI_SHARE 1
#define IDC_TOOLBAR 1 // toolbar control ID
#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
//
// 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 OFN_FILTERDOWN 0x10000000
#define MIN_DEFEXT_LEN 4
#define MAX_DRIVELIST_STRING_LEN (64 + 4)
//
// Macro Definitions.
//
#define IsServer(psz) (IsUNC(psz) && !mystrchr((psz) + 2, CHAR_BSLASH))
#define LPIDL_GetIDList(_pida,n) \
(LPCITEMIDLIST)(((LPBYTE)(_pida)) + (_pida)->aoffset[n])
#define RectWid(_rc) ((_rc).right - (_rc).left)
#define RectHgt(_rc) ((_rc).bottom - (_rc).top)
#define IsVisible(_hwnd) (GetWindowLong(_hwnd, GWL_STYLE)&WS_VISIBLE)
#define HwndToBrowser(_hwnd) (CFileOpenBrowser *)GetWindowLong(_hwnd, DWL_USER)
#define StoreBrowser(_hwnd, _pbrs) \
SetWindowLong(_hwnd, DWL_USER, (DWORD)_pbrs);
#ifdef DBCS
#define ISBACKSLASH(szPath, nOffset) (IsBackSlash(szPath, szPath + nOffset))
#else
#define ISBACKSLASH(szPath, nOffset) (szPath[nOffset] == CHAR_BSLASH)
#endif
#define IsInRange(id, idFirst, idLast) \
((UINT)((id) - idFirst) <= (UINT)(idLast - idFirst))
//
// Typedef Declarations.
//
typedef LPVOID *LPLPVOID;
typedef struct _OFNINITINFO
{
LPOPENFILEINFO lpOFI;
BOOL bSave;
} OFNINITINFO, *LPOFNINITINFO;
//
// Global Variables.
//
WNDPROC lpOKProc = NULL;
HWND gp_hwndActiveOpen = NULL;
HACCEL gp_haccOpen = NULL;
HACCEL gp_haccOpenView = NULL;
HHOOK gp_hHook = NULL;
int gp_nHookRef = -1;
static int g_cxSmIcon;
static int g_cySmIcon;
const LPCSTR c_szCommandsA[] =
{
CMDSTR_NEWFOLDERA,
CMDSTR_VIEWLISTA,
CMDSTR_VIEWDETAILSA,
};
const LPCWSTR c_szCommandsW[] =
{
CMDSTR_NEWFOLDERW,
CMDSTR_VIEWLISTW,
CMDSTR_VIEWDETAILSW,
};
//
// Function Prototypes.
//
LRESULT CALLBACK
OKSubclass(
HWND hOK,
UINT msg,
WPARAM wParam,
LPARAM lParam);
BOOL
InitImports(void);
void
FreeImports(void);
int
GetControlsBottom(
HWND hDlg,
HWND hwndExclude);
BOOL CALLBACK
OpenDlgProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam);
HRESULT
ICoCreateInstance(
REFCLSID rclsid,
REFIID riid,
LPLPVOID ppv);
void
CleanupDialog(
HWND hDlg,
BOOL fRet);
//
// 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,
chx1, IDH_OPEN_READONLY,
IDOK, IDH_OPEN_BUTTON,
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,
chx1, IDH_OPEN_READONLY,
IDOK, IDH_SAVE_BUTTON,
0, 0
};
////////////////////////////////////////////////////////////////////////////
//
// CD_SendShareMsg
//
////////////////////////////////////////////////////////////////////////////
WORD CD_SendShareMsg(
HWND hwnd,
LPTSTR szFile,
UINT ApiType)
{
#ifdef UNICODE
if (ApiType == COMDLG_ANSI)
{
CHAR szFileA[MAX_PATH + 1];
WideCharToMultiByte( CP_ACP,
0,
szFile,
-1,
szFileA,
MAX_PATH + 1,
NULL,
NULL );
return ( (WORD)SendMessage( hwnd,
msgSHAREVIOLATIONA,
0,
(LONG)(LPSTR)(szFileA) ) );
}
else
#endif
{
return ( (WORD)SendMessage( hwnd,
msgSHAREVIOLATIONW,
0,
(LONG)(LPTSTR)(szFile) ) );
}
}
////////////////////////////////////////////////////////////////////////////
//
// CD_SendHelpMsg
//
////////////////////////////////////////////////////////////////////////////
VOID CD_SendHelpMsg(
LPOPENFILENAME pOFN,
HWND hwndDlg,
UINT ApiType)
{
#ifdef UNICODE
if (ApiType == COMDLG_ANSI)
{
if (msgHELPA && pOFN->hwndOwner)
{
SendMessage( pOFN->hwndOwner,
msgHELPA,
(WPARAM)hwndDlg,
(LPARAM)pOFN );
}
}
else
#endif
{
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;
#ifdef UNICODE
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
#endif
{
Result = SendMessage(hwnd, msgFILEOKW, 0, (LPARAM)(pOFN));
}
return (Result);
}
////////////////////////////////////////////////////////////////////////////
//
// CD_SendLBChangeMsg
//
////////////////////////////////////////////////////////////////////////////
LRESULT CD_SendLBChangeMsg(
HWND hwnd,
int Id,
short Index,
short Code,
UINT ApiType)
{
#ifdef UNICODE
if (ApiType == COMDLG_ANSI)
{
return (SendMessage(hwnd, msgLBCHANGEA, Id, MAKELONG(Index, Code)));
}
else
#endif
{
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)
////////////////////////////////////////////////////////////////////////////
//
// SendOFNotify
//
////////////////////////////////////////////////////////////////////////////
LRESULT SendOFNotify(
HWND hwndTo,
HWND hwndFrom,
UINT code,
LPTSTR szFile,
LPOPENFILENAME pOFN,
LPOPENFILEINFO pOFI)
{
OFNOTIFY ofn;
#ifdef UNICODE
if (pOFI->ApiType == COMDLG_ANSI)
{
OFNOTIFYA ofnA;
LRESULT Result;
//
// Convert the file name from Unicode to Ansi.
//
if (szFile)
{
CHAR szFileA[MAX_PATH + 1];
WideCharToMultiByte( CP_ACP,
0,
szFile,
-1,
szFileA,
MAX_PATH + 1,
NULL,
NULL );
ofnA.pszFile = szFileA;
}
else
{
ofnA.pszFile = NULL;
}
//
// Convert the OFN from Unicode to Ansi.
//
ThunkOpenFileNameW2A(pOFI);
ofnA.lpOFN = pOFI->pOFNA;
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
#endif
{
ofn.pszFile = szFile;
ofn.lpOFN = pOFN;
return (SendNotify(hwndTo, hwndFrom, code, &ofn.hdr));
}
}
////////////////////////////////////////////////////////////////////////////
//
// WAIT_CURSOR class
//
////////////////////////////////////////////////////////////////////////////
class WAIT_CURSOR
{
private:
HCURSOR _hcurOld;
public:
WAIT_CURSOR()
{
_hcurOld = ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
}
~WAIT_CURSOR()
{
::SetCursor(_hcurOld);
}
};
////////////////////////////////////////////////////////////////////////////
//
// TEMPMEM class
//
////////////////////////////////////////////////////////////////////////////
class TEMPMEM
{
public:
TEMPMEM(UINT cb)
{
m_uSize = cb;
m_pMem = cb ? LocalAlloc(LPTR, cb) : NULL;
}
~TEMPMEM()
{
if (m_pMem)
{
LocalFree(m_pMem);
}
}
operator LPBYTE() const
{
return ((LPBYTE)m_pMem);
}
BOOL Resize(UINT cb);
private:
LPVOID m_pMem;
protected:
UINT m_uSize;
};
////////////////////////////////////////////////////////////////////////////
//
// 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);
}
LPVOID pTemp = LocalReAlloc(m_pMem, cb, LHND);
if (pTemp)
{
m_pMem = pTemp;
return (TRUE);
}
m_uSize = uOldSize;
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// TEMPSTR class
//
////////////////////////////////////////////////////////////////////////////
class TEMPSTR : public TEMPMEM
{
public:
TEMPSTR(UINT cc = 0) : TEMPMEM(cc * sizeof(TCHAR))
{
}
operator LPTSTR() const
{
return ((LPTSTR)(LPBYTE) * (TEMPMEM *)this);
}
BOOL StrCpy(LPCTSTR pszText);
BOOL StrCat(LPCTSTR pszText);
BOOL StrSize(UINT cb)
{
return (TEMPMEM::Resize(cb * sizeof(TCHAR)));
}
};
////////////////////////////////////////////////////////////////////////////
//
// TEMPSTR::StrCpy
//
////////////////////////////////////////////////////////////////////////////
BOOL TEMPSTR::StrCpy(
LPCTSTR pszText)
{
if (!pszText)
{
StrSize(0);
return (TRUE);
}
UINT uNewSize = lstrlen(pszText) + 1;
if (!StrSize(uNewSize))
{
return (FALSE);
}
lstrcpy(*this, pszText);
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// TEMPSTR::StrCat
//
////////////////////////////////////////////////////////////////////////////
BOOL TEMPSTR::StrCat(
LPCTSTR pszText)
{
if (!(LPTSTR)*this)
{
//
// This should 0 init.
//
if (!StrSize(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 (!StrSize(uNewSize))
{
return (FALSE);
}
}
lstrcat(*this, pszText);
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// CDMessageBox
//
////////////////////////////////////////////////////////////////////////////
int _cdecl CDMessageBox(
HWND hwndParent,
UINT idText,
UINT uFlags,
...)
{
TCHAR szText[MAX_PATH + WARNINGMSGLENGTH];
TCHAR szTitle[WARNINGMSGLENGTH];
va_list ArgList;
LoadString(g_hinst, idText, szTitle, ARRAYSIZE(szTitle));
va_start(ArgList, uFlags);
wvsprintf(szText, szTitle, ArgList);
va_end(ArgList);
GetWindowText(hwndParent, szTitle, ARRAYSIZE(szTitle));
return (MessageBox(hwndParent, szText, szTitle, uFlags));
}
////////////////////////////////////////////////////////////////////////////
//
// InvalidFileWarningNew
//
////////////////////////////////////////////////////////////////////////////
VOID InvalidFileWarningNew(
HWND hWnd,
LPTSTR szFile,
int wErrCode)
{
LPTSTR lpszContent = szFile;
int isz;
BOOL bDriveLetter = FALSE;
if (lstrlen(szFile) > MAX_PATH)
{
#ifdef DBCS
EliminateString(szFile, MAX_PATH);
#else
*(szFile + MAX_PATH) = CHAR_NULL;
#endif
}
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;
}
default :
{
isz = iszInvalidFileName;
break;
}
}
// StringLower(szFile);
if (bDriveLetter)
{
CDMessageBox(hWnd, isz, MB_OK | MB_ICONEXCLAMATION, (TCHAR)*szFile);
}
else
{
CDMessageBox(hWnd, isz, MB_OK | MB_ICONEXCLAMATION, (LPTSTR)szFile);
}
if (isz == iszInvalidFileName)
{
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)
{
Edit_SetSel(GetDlgItem(hwndDlg, edt1), 0, -1);
}
////////////////////////////////////////////////////////////////////////////
//
// MYLISTBOXITEM class
//
// One object of this class exists for each item in the location dropdown.
//
// Data members:
// psfSub - instance of IShellFolder bound to this container
// pidlThis - IDL of this container, relative to its parent
// pidlFull - IDL of this container, relative to the desktop
// cIndent - indent level (0-based)
// dwFlags -
// MLBI_PERMANENT - item is an "information source" and should
// always remain
// dwAttrs - attributes of this container as reported by GetAttributesOf()
// iImage, iSelectedImage - indices into the system image list for this
// object
//
// Member functions:
// ShouldInclude() - returns whether item belongs in the location dropdown
// IsShared() - returns whether an item is shared or not
// SwitchCurrentDirectory() - changes the Win32 current directory to the
// directory indicated by this item
//
////////////////////////////////////////////////////////////////////////////
class MYLISTBOXITEM
{
public:
IShellFolder *psfSub;
IShellFolder *psfParent;
LPITEMIDLIST pidlThis;
LPITEMIDLIST pidlFull;
DWORD cIndent;
DWORD dwFlags;
DWORD dwAttrs;
int iImage;
int iSelectedImage;
MYLISTBOXITEM( MYLISTBOXITEM *pParentItem,
IShellFolder *psf,
LPCITEMIDLIST pidl,
DWORD c,
DWORD f );
~MYLISTBOXITEM();
inline BOOL ShouldInclude()
{
return (dwAttrs & (SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM));
}
inline BOOL IsShared()
{
return (dwAttrs & SFGAO_SHARE);
}
void SwitchCurrentDirectory();
IShellFolder* GetShellFolder();
};
////////////////////////////////////////////////////////////////////////////
//
// MYLISTBOXITEM::MYLISTBOXITEM
//
////////////////////////////////////////////////////////////////////////////
#define MLBI_PERMANENT 0x0001
#define MLBI_PSFFROMPARENT 0x0002
MYLISTBOXITEM::MYLISTBOXITEM(
MYLISTBOXITEM *pParentItem,
IShellFolder *psf,
LPCITEMIDLIST pidl,
DWORD c,
DWORD f)
{
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 = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR | SFGAO_SHARE;
psf->GetAttributesOf(1, &pidl, &dwAttrs);
iImage = SHMapPIDLToSystemImageListIndex(psf, pidl, &iSelectedImage);
}
////////////////////////////////////////////////////////////////////////////
//
// MYLISTBOXITEM::~MYLISTBOXITEM
//
////////////////////////////////////////////////////////////////////////////
MYLISTBOXITEM::~MYLISTBOXITEM()
{
if (psfSub != NULL)
{
psfSub->Release();
}
if (psfParent != NULL)
{
psfParent->Release();
}
if (pidlThis != NULL)
{
SHFree(pidlThis);
}
if (pidlFull != NULL)
{
SHFree(pidlFull);
}
}
////////////////////////////////////////////////////////////////////////////
//
// ShouldIncludeObject
//
////////////////////////////////////////////////////////////////////////////
BOOL ShouldIncludeObject(
LPSHELLFOLDER psfParent,
LPCITEMIDLIST pidl)
{
BOOL fInclude = FALSE;
DWORD dwAttrs = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
if (SUCCEEDED(psfParent->GetAttributesOf(1, &pidl, &dwAttrs)))
{
if (dwAttrs & (SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR))
{
fInclude = TRUE;
}
}
return (fInclude);
}
////////////////////////////////////////////////////////////////////////////
//
// IsContainer
//
////////////////////////////////////////////////////////////////////////////
BOOL IsContainer(
IShellFolder *psf,
LPCITEMIDLIST pidl)
{
DWORD dwAttrs = SFGAO_FOLDER;
return (SUCCEEDED(psf->GetAttributesOf(1, &pidl, &dwAttrs)) &&
(dwAttrs & SFGAO_FOLDER));
}
////////////////////////////////////////////////////////////////////////////
//
// IsLink
//
////////////////////////////////////////////////////////////////////////////
BOOL IsLink(
IShellFolder *psf,
LPCITEMIDLIST pidl)
{
DWORD dwAttrs = SFGAO_LINK;
return (SUCCEEDED(psf->GetAttributesOf(1, &pidl, &dwAttrs)) &&
(dwAttrs & SFGAO_LINK));
}
////////////////////////////////////////////////////////////////////////////
//
// MYLISTBOXITEM::GetShellFolder
//
////////////////////////////////////////////////////////////////////////////
IShellFolder *MYLISTBOXITEM::GetShellFolder()
{
if (!psfSub)
{
if (FAILED(psfParent->BindToObject( pidlThis,
NULL,
IID_IShellFolder,
(LPLPVOID)&psfSub )))
{
psfSub = NULL;
}
else
{
psfParent->Release();
psfParent = NULL;
}
}
return (psfSub);
}
////////////////////////////////////////////////////////////////////////////
//
// MYLISTBOXITEM::SwitchCurrentDirectory
//
////////////////////////////////////////////////////////////////////////////
void MYLISTBOXITEM::SwitchCurrentDirectory(void)
{
TCHAR szDir[MAX_PATH + 1];
if (!pidlFull)
{
SHGetSpecialFolderPath(NULL, szDir, CSIDL_DESKTOPDIRECTORY, FALSE);
}
else
{
SHGetPathFromIDList(pidlFull, szDir);
}
if (szDir[0])
{
SetCurrentDirectory(szDir);
}
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser class
//
////////////////////////////////////////////////////////////////////////////
typedef BOOL (*EIOCALLBACK)(class CFileOpenBrowser*that, LPCITEMIDLIST pidl, LPARAM lParam);
typedef enum
{
ECODE_S_OK = 0,
ECODE_BADDRIVE = 1,
ECODE_BADPATH = 2,
} ECODE;
typedef enum
{
OKBUTTON_NONE = 0x0000,
OKBUTTON_NODEFEXT = 0x0001,
OKBUTTON_QUOTED = 0x0002,
} OKBUTTON_FLAGS;
typedef UINT OKBUTTONFLAGS;
class CFileOpenBrowser : public IShellBrowser, public ICommDlgBrowser
{
public:
// *** IUnknown methods ***
STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj);
STDMETHOD_(ULONG,AddRef) (THIS);
STDMETHOD_(ULONG,Release) (THIS);
// *** IOleWindow methods ***
STDMETHOD(GetWindow) (THIS_ HWND * lphwnd);
STDMETHOD(ContextSensitiveHelp) (THIS_ BOOL fEnterMode);
// *** IShellBrowser methods *** (same as IOleInPlaceFrame)
STDMETHOD(InsertMenusSB) (THIS_ HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
STDMETHOD(SetMenuSB) (THIS_ HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject);
STDMETHOD(RemoveMenusSB) (THIS_ HMENU hmenuShared);
STDMETHOD(SetStatusTextSB) (THIS_ LPCOLESTR lpszStatusText);
STDMETHOD(EnableModelessSB) (THIS_ BOOL fEnable);
STDMETHOD(TranslateAcceleratorSB) (THIS_ LPMSG lpmsg, WORD wID);
// *** IShellBrowser methods ***
STDMETHOD(BrowseObject)(THIS_ LPCITEMIDLIST pidl, UINT wFlags);
STDMETHOD(GetViewStateStream)(THIS_ DWORD grfMode, LPSTREAM *pStrm);
STDMETHOD(GetControlWindow)(THIS_ UINT id, HWND * lphwnd);
STDMETHOD(SendControlMsg)(THIS_ UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT * pret);
STDMETHOD(QueryActiveShellView)(THIS_ struct IShellView ** ppshv);
STDMETHOD(OnViewWindowActive)(THIS_ struct IShellView * pshv);
STDMETHOD(SetToolbarItems)(THIS_ LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags);
// *** ICommDlgBrowser methods ***
STDMETHOD(OnDefaultCommand) (THIS_ struct IShellView * ppshv);
STDMETHOD(OnStateChange) (THIS_ struct IShellView * ppshv, ULONG uChange);
STDMETHOD(IncludeObject) (THIS_ struct IShellView * ppshv, LPCITEMIDLIST lpItem);
// *** Our own methods ***
CFileOpenBrowser(HWND hDlg, BOOL fIsSaveAs);
~CFileOpenBrowser();
HRESULT SwitchView(struct IShellFolder * psfNew, LPCITEMIDLIST pidlNew, FOLDERSETTINGS *pfs);
void OnDblClick(BOOL bFromOKButton);
LRESULT OnNotify(LPNMHDR lpnmhdr);
void ViewCommand(UINT uIndex);
void PaintDriveLine(DRAWITEMSTRUCT *lpdis);
void GetFullPath(LPTSTR pszBuf);
BOOL OnSelChange(int iItem = -1, BOOL bForceUpdate = FALSE);
void OnDotDot();
void RefreshFilter(HWND hwndFilter);
BOOL JumpToPath(LPCTSTR pszDirectory, BOOL bTranslate = FALSE);
BOOL JumpToIDList(LPCITEMIDLIST pidlNew, BOOL bTranslate = FALSE);
BOOL SetDirRetry(LPTSTR pszDir, BOOL bNoValidate = FALSE);
BOOL MultiSelectOKButton(LPCTSTR pszFiles, OKBUTTONFLAGS Flags);
BOOL OKButtonPressed(LPCTSTR pszFile, OKBUTTONFLAGS Flags);
UINT GetDirectoryFromLB(LPTSTR szBuffer, int *pichRoot);
void SetCurrentFilter(LPCTSTR pszFilter, OKBUTTONFLAGS Flags = OKBUTTON_QUOTED);
UINT GetFullEditName(LPTSTR pszBuf, UINT cLen, TEMPSTR *pTempStr = NULL, BOOL *pbNoDefExt = NULL);
void ProcessEdit();
LRESULT OnCommandMessage(WPARAM wParam, LPARAM lParam);
BOOL OnCDMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
void RemoveOldPath(int *piNewSel);
BOOL LinkMatchSpec(LPCITEMIDLIST pidl, LPCTSTR szFile, LPCTSTR szSpec);
HRESULT InitShellLink();
HRESULT ResolveLink(LPCTSTR pszLink, LPTSTR pszFile, UINT cchFile, WIN32_FIND_DATA *pfd);
void SelFocusChange(BOOL bSelChange);
void SelRename(void);
void SetSaveButton(UINT idSaveButton);
void RealSetSaveButton(UINT idSaveButton);
void SetEditFile(LPTSTR pszFile, BOOL bShowExt, BOOL bSaveNullExt = TRUE);
BOOL EnumItemObjects(UINT uItem, EIOCALLBACK pfnCallBack, LPARAM lParam);
BOOL IsKnownExtension(LPCTSTR pszExtension);
UINT FindNameInView(LPTSTR pszFile, OKBUTTONFLAGS Flags, LPTSTR pszPathName,
int nFileOffset, int nExtOffset, int *pnErrCode,
BOOL bTryAsDir = TRUE);
void UpdateLevel(HWND hwndLB, int iInsert, MYLISTBOXITEM *pParentItem);
void InitializeDropDown(HWND hwndCtl);
BOOL FSChange(LONG lNotification, LPCITEMIDLIST *ppidl);
int GetNodeFromIDList(LPCITEMIDLIST pidl);
void Timer(WPARAM wID);
BOOL CreateHookDialog(int nCtlsBottom);
UINT cRef; // compobj refcount
int iCurrentLocation; // index of curr selection in location dropdown
MYLISTBOXITEM *pCurrentLocation; // ptr to object for same
HWND hwndDlg; // handle of this dialog
HWND hSubDlg; // handle of the hook dialog
IShellView *psv; // current view object
IShellFolder *psfCurrent; // current shellfolder object
IShellLink *psl; // Cached for use
IPersistFile *ppf; // Cached for use
HWND hwndView; // current view window
HWND hwndToolbar; // toolbar window
HWND hwndLastFocus; // ctrl that had focus before OK button
HIMAGELIST himl; // system imagelist (small images)
TCHAR szLastFilter[MAX_PATH + 1]; // last filter chosen by the user
TCHAR szStartDir[MAX_PATH + 1]; // saved starting directory
TCHAR szCurDir[MAX_PATH + 1]; // Currently viewed dir (if FS)
TCHAR szBuf[MAX_PATH + 4]; // scratch buffer
TCHAR szTipBuf[MAX_PATH + 1]; // tool tip buffer
TEMPSTR pszHideExt; // saved file with extension
TEMPSTR tszDefSave; // saved file with extension
TEMPSTR pszDefExt; // Writable version of the DefExt
int iWaitCount;
UINT uRegister;
int iComboIndex;
LPOPENFILENAME lpOFN; // caller's OPENFILENAME struct
BOOL bSave : 1; // whether this is a save-as dialog
BOOL fShowExtensions : 1; // whether to show extensions
BOOL bUseHideExt : 1; // whether pszHideExt is valid
BOOL bDropped : 1;
BOOL bNoInferDefExt : 1; // don't get defext from combo
BOOL fSelChangedPending : 1; // we have a selchanging message pending
HWND hwndTips; // hWnd of tooltip control for this window
LPOPENFILEINFO lpOFI; // info for thunking (ansi callers only)
};
const TBBUTTON atbButtons[] =
{
{ 0, 0, 0, TBSTYLE_SEP, { 0, 0 }, 0, 0 },
{ VIEW_PARENTFOLDER, IDC_PARENT, TBSTATE_ENABLED, TBSTYLE_BUTTON, { 0, 0 }, 0, -1 },
{ 0, 0, 0, TBSTYLE_SEP, { 0, 0 }, 0, 0 },
{ VIEW_NEWFOLDER, IDC_NEWFOLDER, TBSTATE_ENABLED, TBSTYLE_BUTTON, { 0, 0 }, 0, -1 },
{ 0, 0, 0, TBSTYLE_SEP, { 0, 0 }, 0, 0 },
{ VIEW_LIST, IDC_VIEWLIST, TBSTATE_ENABLED | TBSTATE_CHECKED, TBSTYLE_CHECKGROUP, { 0, 0 }, 0, -1 },
{ VIEW_DETAILS, IDC_VIEWDETAILS, TBSTATE_ENABLED, TBSTYLE_CHECKGROUP, { 0, 0 }, 0, -1 }
};
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::CFileOpenBrowser
//
// CFileOpenBrowser constructor.
// Minimal construction of the object. Much more construction in
// InitLocation.
//
////////////////////////////////////////////////////////////////////////////
CFileOpenBrowser::CFileOpenBrowser(
HWND hDlg,
BOOL fIsSaveAs)
: cRef(1),
iCurrentLocation(-1),
pCurrentLocation(NULL),
psv(NULL),
hwndDlg(hDlg),
hwndView(NULL),
psfCurrent(NULL),
bSave(fIsSaveAs),
iComboIndex(-1),
psl(0),
hwndTips(NULL)
{
RECT rcToolbar;
szLastFilter[0] = CHAR_NULL;
GetControlRect(hwndDlg, stc1, &rcToolbar);
hwndToolbar = CreateToolbarEx( hwndDlg,
TBSTYLE_TOOLTIPS | WS_CHILD | CCS_NORESIZE |
CCS_NODIVIDER, // | WS_CLIPSIBLINGS,
IDC_TOOLBAR,
12,
HINST_COMMCTRL,
IDB_VIEW_SMALL_COLOR,
atbButtons,
ARRAYSIZE(atbButtons),
0,
0,
0,
0,
sizeof(TBBUTTON) );
if (hwndToolbar)
{
::SetWindowPos( hwndToolbar,
NULL,
rcToolbar.left,
rcToolbar.top,
rcToolbar.right - rcToolbar.left,
rcToolbar.bottom - rcToolbar.top,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW );
}
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;
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::~CFileOpenBrowser
//
// CFileOpenBrowser destructor.
//
////////////////////////////////////////////////////////////////////////////
CFileOpenBrowser::~CFileOpenBrowser()
{
if (uRegister)
{
SHChangeNotifyDeregister(uRegister);
uRegister = 0;
}
if (psl)
{
psl->Release();
ppf->Release();
psl = NULL;
}
//
// Ensure that we discard the tooltip window.
//
if (hwndTips)
{
DestroyWindow(hwndTips);
hwndTips = NULL; // handle is no longer valid
}
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::QueryInterface
//
// Standard OLE2 style methods for this object.
//
////////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CFileOpenBrowser::QueryInterface(
REFIID riid,
LPVOID * ppvObj)
{
if (IsEqualIID(riid, IID_IShellBrowser) || IsEqualIID(riid, IID_IUnknown))
{
*ppvObj = (IShellBrowser *)this;
++cRef;
return (S_OK);
}
else if (IsEqualIID(riid, IID_ICommDlgBrowser))
{
*ppvObj = (ICommDlgBrowser *)this;
++cRef;
return (S_OK);
}
*ppvObj = NULL;
return (E_NOINTERFACE);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::AddRef
//
////////////////////////////////////////////////////////////////////////////
ULONG STDMETHODCALLTYPE CFileOpenBrowser::AddRef()
{
return (++cRef);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::Release
//
////////////////////////////////////////////////////////////////////////////
ULONG STDMETHODCALLTYPE CFileOpenBrowser::Release()
{
cRef--;
if (cRef > 0)
{
return (cRef);
}
delete this;
return (0);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::GetWindow
//
////////////////////////////////////////////////////////////////////////////
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);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::EnableModelessSB
//
////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CFileOpenBrowser::EnableModelessSB(
BOOL fEnable)
{
//
// We don't have any modeless window to be enabled/disabled.
//
return (S_OK);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::TranslateAcceleratorSB
//
////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CFileOpenBrowser::TranslateAcceleratorSB(
LPMSG pmsg,
WORD wID)
{
//
// We don't support EXE embedding.
//
return (S_OK);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::BrowseObject
//
////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CFileOpenBrowser::BrowseObject(
LPCITEMIDLIST pidl,
UINT wFlags)
{
//
// We don't support browsing, or more precisely, CDefView doesn't.
//
return (S_OK);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::GetViewStateStream
//
////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CFileOpenBrowser::GetViewStateStream(
DWORD grfMode,
LPSTREAM *pStrm)
{
//
// BUGBUG: We should implement this so there is some persistence
// for the file open dailog.
//
return (E_NOTIMPL);
}
////////////////////////////////////////////////////////////////////////////
//
// 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
//
////////////////////////////////////////////////////////////////////////////
#define SFVIDM_VIEW_FIRST (SFVIDM_FIRST + 0x0028)
#define SFVIDM_VIEW_LIST (SFVIDM_VIEW_FIRST + 0x0003)
#define SFVIDM_VIEW_DETAILS (SFVIDM_VIEW_FIRST + 0x0004)
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 ) :
{
switch (wParam)
{
case ( SFVIDM_VIEW_DETAILS ) :
{
wParam = IDC_VIEWDETAILS;
break;
}
case ( SFVIDM_VIEW_LIST ) :
{
wParam = IDC_VIEWLIST;
break;
}
default :
{
goto Bail;
}
}
break;
}
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);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::SetCurrentFilter
//
////////////////////////////////////////////////////////////////////////////
void CFileOpenBrowser::SetCurrentFilter(
LPCTSTR pszFilter,
OKBUTTONFLAGS Flags)
{
LPTSTR lpNext;
lstrcpyn(szLastFilter, pszFilter, ARRAYSIZE(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 = lpOFN->lpstrCustomFilter && *lpOFN->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; )
{
LPTSTR lpSemiColon = mystrchr(lpNext, CHAR_SEMICOLON);
if (!lpSemiColon)
{
lpSemiColon = lpNext + lstrlen(lpNext);
}
TCHAR cTemp = *lpSemiColon;
*lpSemiColon = CHAR_NULL;
LPTSTR lpDot = mystrchr(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)
{
hmemcpy( lpSemiColon + 2,
lpSemiColon + 1,
lstrlen(lpSemiColon + 1) * sizeof(TCHAR) );
}
*lpSemiColon = CHAR_STAR;
++lpSemiColon;
--nLeft;
}
*lpSemiColon = cTemp;
if (cTemp == CHAR_NULL)
{
break;
}
else
{
lpNext = lpSemiColon + 1;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// GetFocusedChild
//
////////////////////////////////////////////////////////////////////////////
#define VC_NEWFOLDER 0
#define VC_VIEWLIST 1
#define VC_VIEWDETAILS 2
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::SwitchView
//
// Switch the view control to a new container.
//
////////////////////////////////////////////////////////////////////////////
HRESULT CFileOpenBrowser::SwitchView(
IShellFolder *psfNew,
LPCITEMIDLIST pidlNew,
FOLDERSETTINGS *pfs)
{
IShellView *psvNew;
if (!psfNew)
{
return (E_INVALIDARG);
}
HRESULT hres = psfNew->CreateViewObject( hwndDlg,
IID_IShellView,
(LPLPVOID)&psvNew );
if (SUCCEEDED(hres))
{
IShellView *psvOld;
HWND hwndNew;
iWaitCount++;
SetCursor(LoadCursor(NULL, IDC_WAIT));
//
// 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();
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);
}
RECT rc;
GetControlRect(hwndDlg, lst1, &rc);
//
// psv must be set before creating the view window since we
// validate it on the IncludeObject callback.
//
psv = psvNew;
hres = psvNew->CreateViewWindow(psvOld, pfs, this, &rc, &hwndNew);
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))
{
IContextMenu *pcm;
TCHAR szTemp[10];
BOOL bNewFolder, bViewList, bViewDetails;
hwndView = hwndNew;
if (SUCCEEDED(psvNew->GetItemObject( SVGIO_BACKGROUND,
IID_IContextMenu,
(LPVOID *)&pcm )))
{
bNewFolder = pcm->GetCommandString( (ULONG)CMDSTR_NEWFOLDER,
GCS_VALIDATE,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
if (FAILED(bNewFolder))
{
//
// The szTemp parameter is not used in GCS_VALIDATE,
// so there is no need to convert the string to either
// Unicode or Ansi.
//
#ifdef UNICODE
bNewFolder = pcm->GetCommandString(
(ULONG)CMDSTR_NEWFOLDERA,
GCS_VALIDATEA,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
#else
bNewFolder = pcm->GetCommandString(
(ULONG)CMDSTR_NEWFOLDERW,
GCS_VALIDATEW,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
#endif
}
bNewFolder = (bNewFolder == S_OK);
bViewList = pcm->GetCommandString( (ULONG)CMDSTR_VIEWLIST,
GCS_VALIDATE,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
if (FAILED(bViewList))
{
//
// The szTemp parameter is not used in GCS_VALIDATE,
// so there is no need to convert the string to either
// Unicode or Ansi.
//
#ifdef UNICODE
bViewList = pcm->GetCommandString( (ULONG)CMDSTR_VIEWLISTA,
GCS_VALIDATEA,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
#else
bViewList = pcm->GetCommandString( (ULONG)CMDSTR_VIEWLISTW,
GCS_VALIDATEW,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
#endif
}
bViewList = (bViewList == S_OK);
bViewDetails = pcm->GetCommandString( (ULONG)CMDSTR_VIEWDETAILS,
GCS_VALIDATE,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
if (FAILED(bViewDetails))
{
//
// The szTemp parameter is not used in GCS_VALIDATE,
// so there is no need to convert the string to either
// Unicode or Ansi.
//
#ifdef UNICODE
bViewDetails = pcm->GetCommandString(
(ULONG)CMDSTR_VIEWDETAILSA,
GCS_VALIDATEA,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
#else
bViewDetails = pcm->GetCommandString(
(ULONG)CMDSTR_VIEWDETAILSW,
GCS_VALIDATEW,
NULL,
(LPSTR)szTemp,
ARRAYSIZE(szTemp) );
#endif
}
bViewDetails = (bViewDetails == S_OK);
pcm->Release();
}
else
{
bNewFolder = bViewList = bViewDetails = FALSE;
}
::SendMessage(hwndToolbar, TB_ENABLEBUTTON, IDC_NEWFOLDER, bNewFolder);
::SendMessage(hwndToolbar, TB_ENABLEBUTTON, IDC_VIEWLIST, bViewList);
::SendMessage(hwndToolbar, TB_ENABLEBUTTON, IDC_VIEWDETAILS, bViewDetails);
//
// 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);
}
iWaitCount--;
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
return (hres);
}
////////////////////////////////////////////////////////////////////////////
//
// JustGetToolTipText
//
////////////////////////////////////////////////////////////////////////////
void JustGetToolTipText(
UINT idCommand,
LPTOOLTIPTEXT pTtt)
{
if (!LoadString( ::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)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
{
JustGetToolTipText(pnm->idFrom, lptt);
}
lres = TRUE;
break;
}
case ( NM_STARTWAIT ) :
case ( NM_ENDWAIT ) :
{
iWaitCount += (pnm->code == NM_STARTWAIT ? 1 : -1);
//
// What we really want is for the user to simulate a mouse
// move/setcursor.
//
SetCursor(LoadCursor(NULL, iWaitCount ? IDC_WAIT : IDC_ARROW));
break;
}
}
return (lres);
}
////////////////////////////////////////////////////////////////////////////
//
// GetViewItemText
//
// Get the display name of a shell object.
//
////////////////////////////////////////////////////////////////////////////
void GetViewItemText(
IShellFolder *psf,
LPCITEMIDLIST pidl,
LPTSTR pBuf,
UINT cchBuf,
BOOL fPath = TRUE)
{
STRRET sr;
if (SUCCEEDED(psf->GetDisplayNameOf( pidl,
fPath
? SHGDN_INFOLDER | SHGDN_FORPARSING
: SHGDN_INFOLDER,
&sr )))
{
LPTSTR pszName = NULL;
#ifdef UNICODE
switch (sr.uType)
{
case ( STRRET_OLESTR ) :
{
pszName = sr.pOleStr;
break;
}
case ( STRRET_CSTR ) :
{
UINT cchLen = lstrlenA(sr.cStr) + 1;
pszName = (LPTSTR)SHAlloc(cchLen * sizeof(TCHAR));
if (pszName)
{
MultiByteToWideChar( CP_ACP,
0,
sr.cStr,
cchLen,
pszName,
cchLen );
}
else
{
break;
}
break;
}
case ( STRRET_OFFSET ) :
{
LPSTR lpText = (LPSTR)(((LPBYTE)&pidl->mkid) + sr.uOffset);
UINT cchLen = lstrlenA(lpText) + 1;
pszName = (LPTSTR)SHAlloc(cchLen * sizeof(TCHAR));
if (pszName)
{
MultiByteToWideChar( CP_ACP,
0,
lpText,
cchLen,
pszName,
cchLen );
}
else
{
break;
}
break;
}
default :
{
//
// Unknown format.
//
break;
}
}
#else
switch (sr.uType)
{
case ( STRRET_OLESTR ) :
{
LPOLESTR pwszDisplayName;
//
// Convert from STRRET_OLESTR to STRRET_CSTR.
//
pwszDisplayName = sr.pOleStr;
OleStrToStrN( sr.cStr,
ARRAYSIZE(sr.cStr),
pwszDisplayName,
(UINT)(-1) );
SHFree(pwszDisplayName);
sr.uType = STRRET_CSTR;
// Fall Thru...
}
case ( STRRET_CSTR ) :
{
pszName = sr.cStr;
break;
}
case ( STRRET_OFFSET ) :
{
pszName = (LPTSTR)(((LPBYTE)&pidl->mkid) + sr.uOffset);
break;
}
default :
{
//
// Unknown format.
//
break;
}
}
#endif
if (pszName != NULL)
{
lstrcpyn(pBuf, pszName, cchBuf);
#ifdef UNICODE
SHFree(pszName);
#endif
}
else
{
*pBuf = CHAR_NULL;
}
}
else
{
*pBuf = CHAR_NULL;
}
}
////////////////////////////////////////////////////////////////////////////
//
// GetListboxItem
//
// Get a MYLISTBOXITEM object out of the location dropdown.
//
////////////////////////////////////////////////////////////////////////////
MYLISTBOXITEM *GetListboxItem(
HWND hCtrl,
int 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)
{
LoadString(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(
LPTSTR pszFile,
BOOL bShowExt,
BOOL bSaveNullExt)
{
BOOL bHasHiddenExt = FALSE;
//
// Save the whole file name.
//
if (!pszHideExt.StrCpy(pszFile))
{
pszHideExt.StrCpy(NULL);
bShowExt = TRUE;
}
//
// BUGBUG: This is bogus -- we only want to hide KNOWN extensions,
// not all extensions.
//
if (!bShowExt && !IsWild(pszFile))
{
LPTSTR pszExt = PathFindExtension(pszFile);
if (*pszExt)
{
//
// If there was an extension, hide it.
//
*pszExt = 0;
bHasHiddenExt = TRUE;
}
}
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
//
////////////////////////////////////////////////////////////////////////////
LPTSTR FindEOF(
LPTSTR pszFiles)
{
BOOL bQuoted;
LPTSTR pszBegin = pszFiles;
while (*pszBegin == CHAR_SPACE)
{
++pszBegin;
}
//
// 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 <Start Menu> or <My Document> in the edit control and the right
// thing happens.
//
bQuoted = TRUE;
if (*pszBegin == CHAR_QUOTE)
{
++pszBegin;
}
lstrcpy(pszFiles, pszBegin);
//
// 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
//
////////////////////////////////////////////////////////////////////////////
BOOL ConvertToNULLTerm(
LPTSTR pchRead)
{
BOOL bNoFilesYet = TRUE;
LPTSTR pchWrite = pchRead;
//
// Convert to a double-NULL terminated list.
//
for ( ; ; )
{
LPTSTR pchEnd = FindEOF(pchRead);
//
// Mark the end of the filename with a NULL.
//
if (*pchEnd)
{
*pchEnd = NULL;
bNoFilesYet = FALSE;
lstrcpy(pchWrite, pchRead);
pchWrite += pchEnd - pchRead + 1;
}
else
{
//
// Found EOL. Make sure we did not end with spaces.
//
if (*pchRead)
{
lstrcpy(pchWrite, pchRead);
pchWrite += pchEnd - pchRead + 1;
}
else if (bNoFilesYet)
{
//
// Nothing of significance in the edit control.
//
return (FALSE);
}
break;
}
pchRead = pchEnd + 1;
}
//
// Double-NULL terminate.
//
*pchWrite = CHAR_NULL;
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// SelFocusEnumCB
//
////////////////////////////////////////////////////////////////////////////
typedef struct _SELFOCUS
{
BOOL bSelChange;
UINT idSaveButton;
int nSel;
TEMPSTR sHidden;
TEMPSTR sDisplayed;
} SELFOCUS;
BOOL SelFocusEnumCB(
CFileOpenBrowser *that,
LPCITEMIDLIST pidl,
LPARAM lParam)
{
SELFOCUS *psf = (SELFOCUS *)lParam;
DWORD dwAttrs = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
TCHAR szBuf[MAX_PATH + 1];
if (!pidl)
{
return (TRUE);
}
if (SUCCEEDED(that->psfCurrent->GetAttributesOf(1, &pidl, &dwAttrs)))
{
if (dwAttrs & SFGAO_FOLDER)
{
psf->idSaveButton = iszFileOpenButton;
}
else
{
if (psf->bSelChange && (dwAttrs & SFGAO_FILESYSTEM))
{
++psf->nSel;
if (that->lpOFN->Flags & OFN_ALLOWMULTISELECT)
{
*szBuf = CHAR_QUOTE;
GetViewItemText( that->psfCurrent,
pidl,
szBuf + 1,
ARRAYSIZE(szBuf) - 3 );
lstrcat(szBuf, TEXT("\" "));
if (!psf->sHidden.StrCat(szBuf))
{
psf->nSel = -1;
return (FALSE);
}
if (!that->fShowExtensions)
{
LPTSTR pszExt = PathFindExtension(szBuf + 1);
if (*pszExt)
{
*pszExt = 0;
lstrcat(szBuf, TEXT("\" "));
}
}
if (!psf->sDisplayed.StrCat(szBuf))
{
psf->nSel = -1;
return (FALSE);
}
}
else
{
GetViewItemText(that->psfCurrent, pidl, szBuf, ARRAYSIZE(szBuf));
that->SetEditFile(szBuf, that->fShowExtensions);
}
}
}
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::SelFocusChange
//
////////////////////////////////////////////////////////////////////////////
void CFileOpenBrowser::SelFocusChange(
BOOL bSelChange)
{
SELFOCUS sf;
sf.bSelChange = bSelChange;
sf.idSaveButton = iszFileSaveButton;
sf.nSel = 0;
EnumItemObjects(SVGIO_SELECTION, SelFocusEnumCB, (LPARAM)&sf);
if (lpOFN->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);
SetEditFile(sf.sHidden, fShowExtensions);
sf.idSaveButton = iszFileSaveButton;
break;
}
default :
{
SetEditFile(sf.sDisplayed, TRUE);
pszHideExt.StrCpy(sf.sHidden);
sf.idSaveButton = iszFileSaveButton;
break;
}
}
}
SetSaveButton(sf.idSaveButton);
}
////////////////////////////////////////////////////////////////////////////
//
// SelRenameCB
//
////////////////////////////////////////////////////////////////////////////
BOOL SelRenameCB(
CFileOpenBrowser *that,
LPCITEMIDLIST pidl,
LPARAM lParam)
{
DWORD dwAttrs = SFGAO_FOLDER;
if (!pidl)
{
return (TRUE);
}
if (SUCCEEDED(that->psfCurrent->GetAttributesOf(1, &pidl, &dwAttrs)))
{
if (!(dwAttrs & 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);
}
DWORD dwAttrs = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
if (SUCCEEDED(psfCurrent->GetAttributesOf(1, &pidl, &dwAttrs)))
{
if (!(dwAttrs & (SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR)))
{
return (S_FALSE);
}
}
dwAttrs &= SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
if (*szLastFilter && (dwAttrs == SFGAO_FILESYSTEM))
{
GetViewItemText(psfCurrent, (LPITEMIDLIST)pidl, szBuf, ARRAYSIZE(szBuf));
if (!LinkMatchSpec(pidl, szBuf, szLastFilter) &&
!PathMatchSpec(szBuf, szLastFilter))
{
return (S_FALSE);
}
}
return (S_OK);
}
////////////////////////////////////////////////////////////////////////////
//
// ICoCreateInstance
//
// Create an instance of the specified shell class, IClassFactory interface.
// Lightweight version of the OLE2 binder.
//
////////////////////////////////////////////////////////////////////////////
HRESULT ICoCreateInstance(
REFCLSID rclsid,
REFIID riid,
LPLPVOID ppv)
{
LPCLASSFACTORY pcf;
HRESULT hres = SHDllGetClassObject( rclsid,
IID_IClassFactory,
(LPLPVOID)&pcf );
if (SUCCEEDED(hres))
{
hres = pcf->CreateInstance(NULL, riid, ppv);
pcf->Release();
}
return (hres);
}
////////////////////////////////////////////////////////////////////////////
//
// InsertItem
//
// 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);
}
////////////////////////////////////////////////////////////////////////////
//
// LBItemCompareProc
//
////////////////////////////////////////////////////////////////////////////
int CALLBACK LBItemCompareProc(
LPVOID p1,
LPVOID p2,
LPARAM lParam)
{
IShellFolder *psfParent = (IShellFolder *)lParam;
MYLISTBOXITEM *pItem1 = (MYLISTBOXITEM *)p1;
MYLISTBOXITEM *pItem2 = (MYLISTBOXITEM *)p2;
//
// Do default sorting (by name).
//
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();
hdpa = DPA_Create(4);
if (!hdpa)
{
//
// No memory: Cannot enum this level.
//
return;
}
if (SUCCEEDED(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(psfParent, pidl))
{
MYLISTBOXITEM *pItem =
new MYLISTBOXITEM( pParentItem,
psfParent,
pidl,
cIndent,
MLBI_PERMANENT | MLBI_PSFFROMPARENT );
if (pItem != NULL)
{
if (DPA_InsertPtr(hdpa, 0x7fff, pItem) < 0)
{
delete pItem;
}
}
}
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;
}
delete pOldItem;
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),
FALSE );
if (szBuf[0] && InsertItem(hwndLB, nLBIndex, pNewItem, szBuf))
{
++nLBIndex;
}
else
{
NotThisItem:
delete pNewItem;
}
}
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 = SendMessage(hwndList, CB_GETCOUNT, NULL, NULL);
while (cItems--)
{
MYLISTBOXITEM *pItem = GetListboxItem(hwndList, 0);
delete pItem;
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;
}
}
//
// BUGBUG: nIndex could be CB_ERR.
//
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 CALLBACK DummyDlgProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case ( WM_INITDIALOG ) :
{
break;
}
default :
{
return (FALSE);
}
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// ResetDialogHeight
//
////////////////////////////////////////////////////////////////////////////
void ResetDialogHeight(
HWND hDlg,
HWND hwndExclude,
int nCtlsBottom)
{
int nDiffBottom = nCtlsBottom - GetControlsBottom(hDlg, hwndExclude);
if (nDiffBottom > 0)
{
RECT rcFull;
GetWindowRect(hDlg, &rcFull);
SetWindowPos( hDlg,
NULL,
0,
0,
RectWid(rcFull),
RectHgt(rcFull) - nDiffBottom,
SWP_NOZORDER | SWP_NOMOVE );
}
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::CreateHookDialog
//
////////////////////////////////////////////////////////////////////////////
BOOL CFileOpenBrowser::CreateHookDialog(
int nCtlsBottom)
{
DWORD Flags = lpOFN->Flags;
BOOL bRet = FALSE;
HANDLE hTemplate;
HINSTANCE hinst;
LPCTSTR lpDlg;
HWND hCtlCmn;
RECT rcReal, rcSub;
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, nCtlsBottom);
return (TRUE);
}
if (Flags & OFN_ENABLETEMPLATEHANDLE)
{
hTemplate = lpOFN->hInstance;
hinst = ::g_hinst;
}
else
{
if (Flags & OFN_ENABLETEMPLATE)
{
if (!lpOFN->lpTemplateName)
{
StoreExtendedError(CDERR_NOTEMPLATE);
return (FALSE);
}
if (!lpOFN->hInstance)
{
StoreExtendedError(CDERR_NOHINSTANCE);
return (FALSE);
}
lpDlg = lpOFN->lpTemplateName;
hinst = lpOFN->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)lpOFN->lpfnHook;
#ifdef WX86
if ((lpOFN->Flags & CD_WX86APP))
{
lpfnHookProc = (DLGPROC)Wx86GetX86Callback(lpfnHookProc);
}
#endif
}
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.
//
#ifdef UNICODE
if (lpOFI->ApiType == COMDLG_ANSI)
{
ThunkOpenFileNameW2A(lpOFI);
hSubDlg = CreateDialogIndirectParamA( hinst,
(LPDLGTEMPLATE)hTemplate,
hwndDlg,
lpfnHookProc,
(LPARAM)(lpOFI->pOFNA) );
ThunkOpenFileNameA2W(lpOFI);
}
else
#endif
{
hSubDlg = CreateDialogIndirectParam( hinst,
(LPDLGTEMPLATE)hTemplate,
hwndDlg,
lpfnHookProc,
(LPARAM)lpOFN );
}
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, nCtlsBottom);
//
// 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;
nXMove = (rcReal.right - rcReal.left) - (rcCmn.right - rcCmn.left);
nYMove = (rcReal.bottom - rcReal.top) - (rcCmn.bottom - rcCmn.top);
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.
// BUGBUG: We need to check whether part of the dialog is off screen.
//
GetWindowRect(hwndDlg, &rcReal);
SetWindowPos( hwndDlg,
NULL,
0,
0,
(rcReal.right - rcReal.left) + nXRoom,
(rcReal.bottom - rcReal.top) + nYRoom,
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, lpOFN, lpOFI);
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);
}
LoadString( g_hinst,
aSaveAsControls[iControl].idString,
szText,
ARRAYSIZE(szText) );
SetWindowText(hwnd, szText);
}
}
////////////////////////////////////////////////////////////////////////////
//
// GetControlsBottom
//
// Returns the bottom of the control farthest down (in screen coordinates).
//
////////////////////////////////////////////////////////////////////////////
int GetControlsBottom(
HWND hDlg,
HWND hwndExclude)
{
RECT rc;
HWND hwnd;
int uBottom = 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)
{
continue;
}
GetWindowRect(hwnd, &rc);
if (uBottom < rc.bottom)
{
uBottom = rc.bottom;
}
}
return (uBottom);
}
////////////////////////////////////////////////////////////////////////////
//
// InitLocation
//
// 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;
int nCtlsBottom = GetControlsBottom(hDlg, NULL);
CFileOpenBrowser *pDlgStruct = new CFileOpenBrowser(hDlg, FALSE);
if (pDlgStruct == NULL)
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
StoreBrowser(hDlg, pDlgStruct);
//
// Now that pDlgStruct is stored in the hDlg, it will get freed on the
// WM_DESTROY.
//
IShellFolder *psfRoot;
if (FAILED(ICoCreateInstance( CLSID_ShellDesktop,
IID_IShellFolder,
(LPLPVOID)&psfRoot )))
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
LPITEMIDLIST pidlRoot = SHCloneSpecialIDList(hDlg, CSIDL_DESKTOP, FALSE);
if (!pidlRoot)
{
psfRoot->Release();
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
//
// Add the desktop item itself.
//
MYLISTBOXITEM *pRootItem = new MYLISTBOXITEM( NULL,
psfRoot,
pidlRoot,
0,
MLBI_PERMANENT );
SHFree(pidlRoot);
if (pRootItem == NULL)
{
psfRoot->Release();
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
//
// Now that psfRoot is stored in the pRootItem, it will get freed on the
// delete.
//
TCHAR szScratch[MAX_PATH + 1];
GetViewItemText(psfRoot, NULL, szScratch, ARRAYSIZE(szScratch));
if (!InsertItem(hCtrl, 0, pRootItem, szScratch))
{
delete pRootItem;
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
//
// Now that pRootItem is stored in the combo, it will get freed on the
// WM_DESTROY.
//
#if 0
//
// Enumerate the desktop entries and add them.
//
pDlgStruct->UpdateLevel(hCtrl, 1, pRootItem);
//
// Get the My Computer item and expand it - it's item 1.
//
MYLISTBOXITEM *pDrivesItem = GetListboxItem(hCtrl, 1);
if (!pDrivesItem)
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
pDlgStruct->UpdateLevel(hCtrl, 2, pDrivesItem);
SHChangeNotifyEntry fsne[2];
fsne[0].pidl = pRootItem->pidlFull;
fsne[0].fRecursive = FALSE;
fsne[1].pidl = pDrivesItem->pidlFull;
fsne[1].fRecursive = FALSE;
pDlgStruct->uRegister =
SHChangeNotifyRegister(
hDlg,
SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
SHCNE_ALLEVENTS & ~(SHCNE_CREATE | SHCNE_DELETE | SHCNE_RENAMEITEM),
CDM_FSNOTIFY,
2,
fsne );
#endif
pDlgStruct->pCurrentLocation = pRootItem;
pDlgStruct->lpOFN = lpOFN;
pDlgStruct->bSave = fIsSaveAs;
pDlgStruct->lpOFI = poii->lpOFI;
pDlgStruct->pszDefExt.StrCpy(lpOFN->lpstrDefExt);
//
// Here follows all the caller-parameter-based initialization.
//
::lpOKProc = (WNDPROC)::SetWindowLong( ::GetDlgItem(hDlg, IDOK),
GWL_WNDPROC,
(LONG)OKSubclass );
if (lpOFN->Flags & OFN_CREATEPROMPT)
{
lpOFN->Flags |= (OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST);
}
else if (lpOFN->Flags & OFN_FILEMUSTEXIST)
{
lpOFN->Flags |= OFN_PATHMUSTEXIST;
}
//
// 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.
//
SendDlgItemMessage(hDlg, edt1, EM_LIMITTEXT, MAX_PATH - 1, 0);
SendDlgItemMessage(hDlg, cmb2, CB_SETEXTENDEDUI, 1, 0);
SendDlgItemMessage(hDlg, cmb1, CB_SETEXTENDEDUI, 1, 0);
//
// Check if original directory should be saved for later restoration.
//
if (lpOFN->Flags & OFN_NOCHANGEDIR)
{
GetCurrentDirectory( ARRAYSIZE(pDlgStruct->szStartDir),
pDlgStruct->szStartDir );
}
//
// Initialize all provided filters.
//
if (lpOFN->lpstrCustomFilter && *lpOFN->lpstrCustomFilter)
{
SendDlgItemMessage( hDlg,
cmb1,
CB_INSERTSTRING,
0,
(LONG)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);
}
//
// 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);
}
if (lpOFN->lpstrTitle && *lpOFN->lpstrTitle)
{
SetWindowText(hDlg, lpOFN->lpstrTitle);
}
if (lpOFN->Flags & OFN_HIDEREADONLY)
{
HideControl(hDlg, chx1);
}
else
{
CheckDlgButton(hDlg, chx1, (lpOFN->Flags & OFN_READONLY) ? 1 : 0);
}
if (!(lpOFN->Flags & OFN_SHOWHELP))
{
HideControl(hDlg, pshHelp);
}
if (!pDlgStruct->CreateHookDialog(nCtlsBottom))
{
return (FALSE);
}
//
// Explicitly set the focus since this is no longer the first item in the
// dialog template.
//
SetFocus(GetDlgItem(hDlg, edt1));
//
// Check out if the filename contains a path. If so, override whatever
// is contained in lpstrInitialDir. Chop off the path and put up only
// the filename.
//
LPCTSTR lpstrInitialDir = lpOFN->lpstrInitialDir;
LPTSTR lpInitialText = lpOFN->lpstrFile;
if (lpInitialText && *lpInitialText)
{
// StringLower(lpInitialText);
if ( DBL_BSLASH(lpInitialText + 2) &&
(*(lpInitialText + 1) == CHAR_COLON) )
{
lstrcpy(lpInitialText, lpInitialText + 2);
}
int nFileOffset;
lstrcpyn(szScratch, lpInitialText, ARRAYSIZE(szScratch));
nFileOffset = ParseFileNew(szScratch, NULL, FALSE, TRUE);
//
// Is the filename invalid?
//
if ( !(lpOFN->Flags & OFN_NOVALIDATE) &&
(nFileOffset < 0) &&
(nFileOffset != PARSE_EMPTYSTRING) )
{
StoreExtendedError(FNERR_INVALIDFILENAME);
return (FALSE);
}
//
// It all looks valid. I need to use to the original text because
// ParseFile does too much modifying.
//
PathRemoveBlanks(lpInitialText);
LPTSTR pszFileName = PathFindFileName(lpInitialText);
if (nFileOffset >= 0 && IsWild(pszFileName))
{
pDlgStruct->SetCurrentFilter(pszFileName);
}
nFileOffset = pszFileName - lpInitialText;
if (nFileOffset > 0)
{
CopyMemory(szScratch, lpInitialText, nFileOffset * sizeof(TCHAR));
szScratch[nFileOffset] = CHAR_NULL;
PathRemoveBslash(szScratch);
//
// If there is no specified initial dir or it is the same as the
// dir of the file, use the dir of the file and strip the path
// off the file.
//
if (!lpstrInitialDir || !lpstrInitialDir[0]
|| lstrcmpi(lpstrInitialDir, szScratch) == 0)
{
lpstrInitialDir = szScratch;
lpInitialText = lpInitialText + nFileOffset;
}
}
}
if (lpstrInitialDir && *lpstrInitialDir)
{
//
// It's better to come up somewhere rather than just tell the app
// they have a bogus directory set.
//
pDlgStruct->JumpToPath(lpstrInitialDir, TRUE);
}
//
// This checks if the previous jump failed.
//
if (!pDlgStruct->psv)
{
//
// If we tried to set the dir above and it failed, we should
// probably always show the full path of the file. If we didn't try
// above, then we are already showing the full path, so no problem.
//
lpInitialText = lpOFN->lpstrFile;
pDlgStruct->JumpToPath(TEXT("."), TRUE);
}
if (!pDlgStruct->psv)
{
//
// Maybe the curdir has been deleted; try the desktop.
//
ITEMIDLIST idl = { 0 };
//
// Do not try to translate this.
//
pDlgStruct->JumpToIDList(&idl, FALSE);
}
if (!pDlgStruct->psv)
{
//
// This would be very bad.
//
StoreExtendedError(CDERR_INITIALIZATION);
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 | 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) hCtrl;
ti.hinst = NULL;
ti.lpszText = LPSTR_TEXTCALLBACK;
SendMessage( pDlgStruct ->hwndTips,
TTM_ADDTOOL,
0,
(LPARAM)&ti );
}
else
{
//
// Failed to create the tooltip window.
//
return (FALSE);
}
}
}
//
// Show the window after creating the ShellView so we do not get a
// big ugly gray spot.
//
::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, pDlgStruct->fShowExtensions, FALSE);
SelectEditText(hDlg);
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// CleanupDialog
//
// Dialog cleanup, memory deallocation.
//
////////////////////////////////////////////////////////////////////////////
void CleanupDialog(
HWND hDlg,
BOOL fRet)
{
CFileOpenBrowser *pDlgStruct = HwndToBrowser(hDlg);
//
// Return the most recently used filter.
//
LPOPENFILENAME lpOFN = pDlgStruct->lpOFN;
if (lpOFN->lpstrCustomFilter)
{
UINT len = lstrlen(lpOFN->lpstrCustomFilter) + 1;
UINT sCount = lstrlen(pDlgStruct->szLastFilter);
if (lpOFN->nMaxCustFilter > sCount + len)
{
lstrcpy(lpOFN->lpstrCustomFilter + len, pDlgStruct->szLastFilter);
}
}
if ( (fRet == TRUE) &&
pDlgStruct->hSubDlg &&
( CD_SendOKNotify(pDlgStruct->hSubDlg, hDlg, lpOFN, pDlgStruct->lpOFI) ||
CD_SendOKMsg(pDlgStruct->hSubDlg, lpOFN, pDlgStruct->lpOFI) ) )
{
//
// Give the hook a chance to validate the file name.
//
return;
}
//
// We need to make sure the IShellBrowser is still around during
// destruction.
//
if (pDlgStruct->psv != NULL)
{
pDlgStruct->psv->DestroyViewWindow();
pDlgStruct->psv->Release();
pDlgStruct->psv = NULL;
}
if ((lpOFN->Flags & OFN_NOCHANGEDIR) && *pDlgStruct->szStartDir)
{
SetCurrentDirectory(pDlgStruct->szStartDir);
}
::EndDialog(hDlg, 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);
for (--iItem; iItem >= 0; iItem--)
{
MYLISTBOXITEM *pPrev = GetListboxItem(hwndCombo, iItem);
if (pPrev->cIndent < pItem->cIndent)
{
*piItem = iItem;
return (pPrev);
}
}
return (NULL);
}
////////////////////////////////////////////////////////////////////////////
//
// GetFullPathEnumCB
//
////////////////////////////////////////////////////////////////////////////
BOOL GetFullPathEnumCB(
CFileOpenBrowser *that,
LPCITEMIDLIST pidl,
LPARAM lParam)
{
DWORD dwAttrs = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
MYLISTBOXITEM *pLoc = that->pCurrentLocation;
if (!pidl)
{
return (TRUE);
}
if ((SUCCEEDED(that->psfCurrent->GetAttributesOf(1, &pidl, &dwAttrs))) &&
(dwAttrs & SFGAO_FILESYSTEM))
{
LPITEMIDLIST pidlFull;
if (pLoc->pidlFull == NULL)
{
pidlFull = ILClone(pidl);
}
else
{
pidlFull = ILCombine(pLoc->pidlFull, pidl);
}
if (pidlFull != NULL)
{
SHGetPathFromIDList(pidlFull, (LPTSTR)lParam);
SHFree(pidlFull);
}
}
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// 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);
delete pItem;
*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 = 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 = lpOFN->Flags & OFN_ALLOWMULTISELECT ? 0 : FWF_SINGLESEL;
}
iCurrentLocation = iItem;
pCurrentLocation = pNewLocation;
TryAgain:
if (FAILED(SwitchView( pCurrentLocation->GetShellFolder(),
pCurrentLocation->pidlFull,
&fs )))
{
//
// 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 TryAgain;
}
}
}
//
// Try the parent of the old item.
//
if (iCurrentLocation)
{
pCurrentLocation = GetParentItem(hwndCombo, &iCurrentLocation);
ComboBox_SetCurSel(hwndCombo, iCurrentLocation);
goto TryAgain;
}
//
// We cannot create the Desktop view. I think we are in
// real trouble. We had better bail out.
//
StoreExtendedError(CDERR_DIALOGFAILURE);
CleanupDialog(hwndDlg, FALSE);
return (FALSE);
}
::SendMessage( hwndToolbar,
TB_SETSTATE,
IDC_PARENT,
iCurrentLocation ? TBSTATE_ENABLED : 0 );
if (!iCurrentLocation || (pCurrentLocation->dwAttrs & SFGAO_FILESYSTEM))
{
pCurrentLocation->SwitchCurrentDirectory();
}
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.
//
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, TRUE);
}
SetSaveButton(iszFileSaveButton);
SwitchedBack:
RemoveOldPath(&iCurrentLocation);
}
if (!bSwitchedBack && hSubDlg)
{
CD_SendFolderChangeNotify(hSubDlg, hwndDlg, lpOFN, lpOFI);
}
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();
}
////////////////////////////////////////////////////////////////////////////
//
// 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);
}
if (IsContainer(that->psfCurrent, pidl))
{
LPITEMIDLIST pidlFull = ILCombine(pLoc->pidlFull, pidl);
if (pidlFull)
{
that->JumpToIDList(pidlFull);
SHFree(pidlFull);
}
*ppidl = PIDL_FOLDERSEL;
}
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 (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)
{
BOOL bRet;
TCHAR szTemp[MAX_PATH + 1];
TCHAR szCurDir[MAX_PATH + 1];
//
// This should do the whole job of canonicalizing the directory.
//
GetCurrentDirectory(ARRAYSIZE(szCurDir), szCurDir);
PathCombine(szTemp, szCurDir, pszDirectory);
LPITEMIDLIST pidlNew = ILCreateFromPath(szTemp);
if (pidlNew == NULL)
{
return (FALSE);
}
bRet = JumpToIDList(pidlNew, bTranslate);
SHFree(pidlNew);
return (bRet);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::JumpTOIDList
//
// Refocus the entire dialog on a different IDList.
//
////////////////////////////////////////////////////////////////////////////
BOOL CFileOpenBrowser::JumpToIDList(
LPCITEMIDLIST pidlNew,
BOOL bTranslate)
{
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))
{
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( pBestParent,
pBestParent->GetShellFolder(),
pidlFirst,
cIndent,
MLBI_PSFFROMPARENT );
if (pNewItem == NULL)
{
break;
}
GetViewItemText( pBestParent->psfSub,
pidlFirst,
szBuf,
ARRAYSIZE(szBuf),
FALSE );
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);
return (OnSelChange(iItem, TRUE));
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::ViewCommand
//
// Process the new-folder button on the toolbar.
//
////////////////////////////////////////////////////////////////////////////
void CFileOpenBrowser::ViewCommand(
UINT uIndex)
{
IContextMenu *pcm;
if (SUCCEEDED(psv->GetItemObject( SVGIO_BACKGROUND,
IID_IContextMenu,
(LPVOID *)&pcm )))
{
CMINVOKECOMMANDINFOEX ici;
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;
#ifdef UNICODE
ici.lpVerbW = ::c_szCommandsW[uIndex];
ici.fMask |= CMIC_MASK_UNICODE;
#endif
HMENU hmContext = CreatePopupMenu();
pcm->QueryContextMenu(hmContext, 0, 1, 256, 0);
pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)(&ici));
pcm->Release();
DestroyMenu(hmContext);
}
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::InitShellLink
//
////////////////////////////////////////////////////////////////////////////
HRESULT CFileOpenBrowser::InitShellLink(void)
{
if (psl)
{
return (S_OK);
}
HRESULT hres = ICoCreateInstance( CLSID_ShellLink,
IID_IShellLink,
(LPLPVOID)&psl );
if (FAILED(hres))
{
return (hres);
}
hres = psl->QueryInterface(IID_IPersistFile, (LPLPVOID)&ppf);
if (FAILED(hres))
{
psl->Release();
psl = NULL;
return (hres);
}
return (S_OK);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::ResolveLink
//
////////////////////////////////////////////////////////////////////////////
HRESULT CFileOpenBrowser::ResolveLink(
LPCTSTR pszLink,
LPTSTR pszFile,
UINT cchFile,
WIN32_FIND_DATA *pfd)
{
WCHAR wszFile[MAX_PATH + 1];
HRESULT hres = InitShellLink();
if (FAILED(hres))
{
return (hres);
}
#ifdef UNICODE
lstrcpyn(wszFile, pszLink, ARRAYSIZE(wszFile));
#else
StrToOleStrN(wszFile, ARRAYSIZE(wszFile), pszLink, -1);
#endif
hres = ppf->Load(wszFile, 0);
if (FAILED(hres))
{
return (hres);
}
hres = psl->GetPath(wszFile, MAX_PATH, pfd, 0);
ExpandEnvironmentStrings(wszFile, pszFile, cchFile);
pszFile[cchFile - 1] = 0;
return (hres);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::LinkMatchSpec
//
////////////////////////////////////////////////////////////////////////////
BOOL CFileOpenBrowser::LinkMatchSpec(
LPCITEMIDLIST pidl,
LPCTSTR pszFile,
LPCTSTR pszSpec)
{
if (!IsLink(psfCurrent, pidl))
{
return (FALSE);
}
TCHAR szFile[MAX_PATH];
TCHAR szLinkFile[MAX_PATH];
WIN32_FIND_DATA fd = { 0, };
PathCombine(szLinkFile, szCurDir, pszFile);
if (ResolveLink(szLinkFile, szFile, MAX_PATH, &fd) != S_OK || !szFile[0])
{
return (FALSE);
}
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
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);
::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;
lpOFN->Flags &= ~OFN_FILTERDOWN;
short nIndex = (short) SendMessage(hwndFilter, CB_GETCURSEL, 0, 0L);
if (nIndex < 0)
{
//
// No current selection.
//
return;
}
BOOL bCustomFilter = lpOFN->lpstrCustomFilter && *lpOFN->lpstrCustomFilter;
lpOFN->nFilterIndex = nIndex;
if (!bCustomFilter)
{
lpOFN->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 && lpOFN->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 = mystrchr(lpFilter, CHAR_DOT);
//
// Skip past the CHAR_DOT.
//
if (lpDot && pszDefExt.StrCpy(lpDot + 1))
{
LPTSTR lpSemiColon = mystrchr(pszDefExt, CHAR_SEMICOLON);
if (lpSemiColon)
{
*lpSemiColon = CHAR_NULL;
}
if (IsWild(pszDefExt))
{
pszDefExt.StrCpy(lpOFN->lpstrDefExt);
}
}
else
{
pszDefExt.StrCpy(lpOFN->lpstrDefExt);
}
}
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, TRUE);
}
if (psv)
{
psv->Refresh();
}
}
if (hSubDlg)
{
if (!CD_SendTypeChangeNotify(hSubDlg, hwndDlg, lpOFN, lpOFI))
{
CD_SendLBChangeMsg(hSubDlg, cmb1, nIndex, CD_LBSELCHANGE, lpOFI->ApiType);
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::GetDirectoryFromLB
//
// Return the dropdown's directory and its length.
// Set *pichRoot to the start of the path (C:\ or \\server\share\).
//
////////////////////////////////////////////////////////////////////////////
UINT CFileOpenBrowser::GetDirectoryFromLB(
LPTSTR pszBuf,
int *pichRoot)
{
if (pCurrentLocation->pidlFull != NULL &&
SHGetPathFromIDList(pCurrentLocation->pidlFull, pszBuf))
{
PathAddBackslash(pszBuf);
LPTSTR pszBackslash = mystrchr(pszBuf + 2, CHAR_BSLASH);
if (pszBackslash != NULL)
{
//
// For UNC paths, the "root" is on the next backslash.
//
if (DBL_BSLASH(pszBuf))
{
pszBackslash = mystrchr(pszBackslash + 1, CHAR_BSLASH);
}
UINT cchRet = lstrlen(pszBuf);
*pichRoot = (pszBackslash != NULL) ? 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 = { 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_IDataObject,
(LPVOID *)&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,
0,
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.
//
// BUGBUG: 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
//
////////////////////////////////////////////////////////////////////////////
void CDPathQualify(
LPCTSTR lpFile,
LPTSTR pszPathName)
{
TCHAR szCurDir[MAX_PATH + 1];
lstrcpy(pszPathName, lpFile);
//
// This should do the whole job of canonicalizing the directory.
//
GetCurrentDirectory(ARRAYSIZE(szCurDir), szCurDir);
PathCombine(pszPathName, szCurDir, pszPathName);
}
////////////////////////////////////////////////////////////////////////////
//
// VerifyOpen
//
// Returns: 0 success
// !0 dos error code
//
////////////////////////////////////////////////////////////////////////////
int VerifyOpen(
LPCTSTR lpFile,
LPTSTR pszPathName)
{
HANDLE hf;
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)
{
return GetLastError();
}
else
{
CloseHandle(hf);
return (0);
}
}
////////////////////////////////////////////////////////////////////////////
//
// 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 (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 (lpOFN->lpstrFilter)
{
LPCTSTR pFilter = lpOFN->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)
{
#ifdef DBCS
if (IsDBCSLeadByte(*pExt))
{
if (*(pExt + 1) != *(pFilter + 1))
{
break;
}
pExt++;
pFilter++;
}
#endif
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);
}
////////////////////////////////////////////////////////////////////////////
//
// 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.
//
////////////////////////////////////////////////////////////////////////////
#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.
//
CDPathQualify(pszFile, pszPathName);
if (!IsLFNDrive(pszPathName))
{
goto VerifyTheName;
}
}
bGetOut = FALSE;
if ((LPTSTR)pszDefExt &&
((DWORD)nNewExt + lstrlen(pszDefExt) < lpOFN->nMaxFile))
{
bAddExt = TRUE;
//
// Note that we check lpstrDefExt to see if they want an automatic
// extension, but actually copy pszDefExt.
//
AppendExt(pszFile, pszDefExt, FALSE);
//
// 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 (!(lpOFN->Flags & OFN_NODEREFERENCELINKS) && nLoop > 0)
{
--nLoop;
LPITEMIDLIST pidl;
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)
{
hRes = SHILCreateFromPath(pszPathName, &pidl, &dwAttr);
}
else
{
WCHAR wszDisplayName[MAX_PATH + 1];
ULONG chEaten;
#ifdef UNICODE
lstrcpyn(wszDisplayName, pszFile, ARRAYSIZE(wszDisplayName));
#else
StrToOleStrN( wszDisplayName,
ARRAYSIZE(wszDisplayName),
pszFile,
-1 );
#endif
hRes = psfCurrent->ParseDisplayName( NULL,
NULL,
wszDisplayName,
&chEaten,
&pidl,
&dwAttr );
}
if (SUCCEEDED(hRes))
{
if (pidl)
{
SHFree(pidl);
}
if ((dwAttr & SFGAO_LINK) &&
ResolveLink( pszPathName,
szTemp,
ARRAYSIZE(szTemp),
NULL ) == S_OK)
{
//
// It was a link, and it "dereferenced" to something,
// so we should try again with that new file.
//
lstrcpy(pszFile, szTemp);
goto VerifyTheName;
}
}
}
return (FE_FOUNDNAME);
}
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)
{
TCHAR cSave = *(pszFile + nFileOffset);
*(pszFile + nFileOffset) = CHAR_NULL;
//
// We need to have the view on the dir with the file to do the
// next steps.
//
BOOL bOK = JumpToPath(pszFile);
*(pszFile + nFileOffset) = cSave;
if (!psv)
{
//
// We're dead.
//
return (FE_OUTOFMEM);
}
if (bOK)
{
lstrcpy(pszFile, pszFile + nFileOffset);
nNewExt -= nFileOffset;
SetEditFile(pszFile, 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;
}
}
#ifdef FIND_FILES_NOT_IN_VIEW
//
// We still have not found a match, so try enumerating files we cannot
// see.
//
int nFilterLen;
nFilterLen = lstrlen(szLastFilter);
if (nFilterLen + nNewExt + ARRAYSIZE(szDotStar) < ARRAYSIZE(szLastFilter))
{
TCHAR szSaveFilter[ARRAYSIZE(szLastFilter)];
lstrcpy(szSaveFilter, szLastFilter);
//
// Add "Joe.*" to the current filter and refresh.
//
szLastFilter[nFilterLen] = CHAR_SEMICOLON;
lstrcpy(szLastFilter + nFilterLen + 1, pszFile);
lstrcat(szLastFilter, szDotStar);
HANDLE hf;
WIN32_FIND_DATA fd;
//
// Make sure we are in a FileSystem folder, and then see if at least
// one file named "Joe.*" exists.
//
// Note we always set the current directory.
//
if ((pCurrentLocation->dwAttrs & SFGAO_FILESYSTEM) &&
(hf = FindFirstFile( szLastFilter + nFilterLen + 1,
&fd)) != INVALID_HANDLE_VALUE)
{
psv->Refresh();
fns.pidlFound = NULL;
EnumItemObjects(SVGIO_ALLVIEW, FindNameEnumCB, (LPARAM)&fns);
FindClose(hf);
}
//
// We must not restore the filter until AFTER getting the
// EnumItemObjects in case there is a background thread doing the
// enumeration. ALLVIEW should cause the threads to sync up before
// returning.
//
lstrcpy(szLastFilter, szSaveFilter);
switch (fns.uRet)
{
case ( FE_INVALID_VALUE ) :
{
break;
}
case ( FE_FOUNDNAME ) :
{
goto VerifyTheName;
}
default :
{
uRet = fns.uRet;
goto VerifyAndRet;
}
}
}
#endif
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.
//
AppendExt(pszFile, 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);
}
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;
UINT cch, cchCurDir, cchFiles;
//
// This doesn't really mean anything for multiselection.
//
lpOFN->nFileExtension = 0;
if (!lpOFN->lpstrFile)
{
return (TRUE);
}
SetCursor(LoadCursor(NULL, IDC_WAIT));
//
// Check for space for first full path element.
//
cchCurDir = lstrlen(szCurDir) + 1;
cchFiles = lstrlen(pszFiles) + 1;
cch = cchCurDir + cchFiles;
if (cch > (UINT)lpOFN->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.
//
if (lpOFN->nMaxFile >= 3)
{
#ifdef UNICODE
lpOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
lpOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
#else
lpOFN->lpstrFile[0] = (TCHAR)LOBYTE(cch);
lpOFN->lpstrFile[1] = (TCHAR)HIBYTE(cch);
#endif
lpOFN->lpstrFile[2] = CHAR_NULL;
}
else
{
#ifdef UNICODE
lpOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
if (lpOFN->nMaxFile == 2)
{
lpOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
}
#else
lpOFN->lpstrFile[0] = LOBYTE(cch);
if (lpOFN->nMaxFile == 2)
{
lpOFN->lpstrFile[1] = HIBYTE(cch);
}
#endif
}
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (TRUE);
}
TEMPSTR psFiles(cchFiles);
pchRead = psFiles;
if (!pchRead)
{
//
// Out of memory.
// BUGBUG: There should be some sort of error message here.
//
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (FALSE);
}
//
// Copy in the full path as the first element.
//
lstrcpy(lpOFN->lpstrFile, szCurDir);
//
// Set nFileOffset to 1st file.
//
lpOFN->nFileOffset = cchCurDir;
pchWrite = lpOFN->lpstrFile + cchCurDir;
//
// We know there is enough room for the whole string.
//
lstrcpy(pchRead, pszFiles);
//
// This should only compact the string.
//
if (!ConvertToNULLTerm(pchRead))
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (FALSE);
}
for ( ; *pchRead; pchRead += lstrlen(pchRead) + 1)
{
int nFileOffset, nExtOffset;
TCHAR szBasicPath[MAX_PATH];
lstrcpy(szBasicPath, pchRead);
nFileOffset = ParseFileNew(szBasicPath, &nExtOffset, FALSE, TRUE);
if (nFileOffset < 0)
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
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 ) :
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (FALSE);
}
case ( FE_TOOMANY ) :
{
CDMessageBox( hwndDlg,
iszTooManyFiles,
MB_OK | MB_ICONEXCLAMATION,
pchRead );
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (FALSE);
}
default :
{
break;
}
}
if ( nErrCode &&
( (lpOFN->Flags & OFN_FILEMUSTEXIST) ||
(nErrCode != OF_FILENOTFOUND) ) &&
( (lpOFN->Flags & OFN_PATHMUSTEXIST) ||
(nErrCode != OF_PATHNOTFOUND) ) &&
( !(lpOFN->Flags & OFN_SHAREAWARE) ||
(nErrCode != OF_SHARINGVIOLATION) ) )
{
if ((nErrCode == OF_SHARINGVIOLATION) && hSubDlg)
{
int nShareCode = CD_SendShareNotify( hSubDlg,
hwndDlg,
szPathName,
lpOFN,
lpOFI );
if (nShareCode == OFN_SHARENOWARN)
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
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, lpOFI->ApiType);
if (nShareCode == OFN_SHARENOWARN)
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
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;
}
}
//
// BUGBUG: These will never be set.
//
if ((nErrCode == OF_WRITEPROTECTION) ||
(nErrCode == OF_DISKFULL) ||
(nErrCode == OF_DISKFULL2) ||
(nErrCode == OF_ACCESSDENIED))
{
*pchRead = szPathName[0];
}
MultiWarning:
SetCursor(LoadCursor(NULL, IDC_ARROW));
InvalidFileWarningNew(hwndDlg, pchRead, nErrCode);
return (FALSE);
}
EscapedThroughShare:
if (nErrCode == 0)
{
//
// Successfully opened.
//
if ((lpOFN->Flags & OFN_NOREADONLYRETURN) &&
(GetFileAttributes(szPathName) & FILE_ATTRIBUTE_READONLY))
{
nErrCode = OF_LAZYREADONLY;
goto MultiWarning;
}
if ((bSave || (lpOFN->Flags & OFN_NOREADONLYRETURN)) &&
(nErrCode = WriteProtectedDirCheck(szPathName)))
{
goto MultiWarning;
}
if (lpOFN->Flags & OFN_OVERWRITEPROMPT)
{
if (bSave && !FOkToWriteOver(hwndDlg, szPathName))
{
PostMessage( hwndDlg,
WM_NEXTDLGCTL,
(WPARAM)GetDlgItem(hwndDlg, edt1),
1 );
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (FALSE);
}
}
}
//
// Add some more in case the file name got larger.
//
cch += lstrlen(szBasicPath) - lstrlen(pchRead);
if (cch > (UINT)lpOFN->nMaxFile)
{
//
// Buffer is too small, so return the size of the buffer
// required to hold the string.
//
if (lpOFN->nMaxFile >= 3)
{
#ifdef UNICODE
lpOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
lpOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
#else
lpOFN->lpstrFile[0] = (TCHAR)LOBYTE(cch);
lpOFN->lpstrFile[1] = (TCHAR)HIBYTE(cch);
#endif
lpOFN->lpstrFile[2] = CHAR_NULL;
}
else
{
#ifdef UNICODE
lpOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
if (lpOFN->nMaxFile == 2)
{
lpOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
}
#else
lpOFN->lpstrFile[0] = LOBYTE(cch);
if (lpOFN->nMaxFile == 2)
{
lpOFN->lpstrFile[1] = HIBYTE(cch);
}
#endif
}
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (TRUE);
}
//
// We already know we have anough room.
//
lstrcpy(pchWrite, szBasicPath);
pchWrite += lstrlen(pchWrite) + 1;
}
//
// double-NULL terminate.
//
*pchWrite = CHAR_NULL;
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// 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
//
////////////////////////////////////////////////////////////////////////////
#define PathFileExists(lpszPath) (GetFileAttributes(lpszPath) != (UINT)-1)
BOOL CFileOpenBrowser::OKButtonPressed(
LPCTSTR pszFile,
OKBUTTONFLAGS Flags)
{
TCHAR szExpFile[MAX_PATH];
TCHAR szPathName[MAX_PATH];
TCHAR szBasicPath[MAX_PATH];
TCHAR szD[4];
LPTSTR pExpFile = NULL;
int nErrCode;
ECODE eCode = ECODE_S_OK;
int cch;
int nFileOffset, nExtOffset, nOldExt;
TCHAR ch;
BOOL bAddExt = FALSE;
BOOL bUNCName = FALSE;
int nTempOffset;
BOOL bIsDir;
BOOL bRet = FALSE;
WAIT_CURSOR w;
//
// Expand any environment variables.
//
if ((cch = LOWORD(lpOFN->nMaxFile)) > MAX_PATH)
{
pExpFile = (LPTSTR)LocalAlloc(LPTR, (cch * sizeof(TCHAR)));
}
if (!pExpFile)
{
pExpFile = szExpFile;
cch = MAX_PATH;
}
ExpandEnvironmentStrings(pszFile, pExpFile, cch);
pExpFile[cch - 1] = 0;
//
// See if we're in Multi Select mode.
//
if (mystrchr(pExpFile, CHAR_QUOTE) && (lpOFN->Flags & OFN_ALLOWMULTISELECT))
{
bRet = MultiSelectOKButton(pExpFile, Flags);
goto ReturnFromOKButtonPressed;
}
SetCursor(LoadCursor(NULL, IDC_WAIT));
#ifdef UNICODE
if (pExpFile[1] == CHAR_COLON || DBL_BSLASH(pExpFile))
#else
if ((!IsDBCSLeadByte(pExpFile[0]) && pExpFile[1] == CHAR_COLON) ||
DBL_BSLASH(pExpFile))
#endif
{
//
// If a drive or UNC was specified, use it.
//
lstrcpyn(szBasicPath, pExpFile, ARRAYSIZE(szBasicPath) - 1);
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:).
//
lstrcpyn( szBasicPath + nTempOffset,
pExpFile,
ARRAYSIZE(szBasicPath) - nTempOffset - 1 );
}
else
{
//
// Tack the file to the end of the path.
//
lstrcpyn(szBasicPath + cch, pExpFile, ARRAYSIZE(szBasicPath) - cch - 1);
}
}
nFileOffset = ParseFileOld(szBasicPath, &nExtOffset, &nOldExt, FALSE, TRUE);
if (nFileOffset == PARSE_EMPTYSTRING)
{
if (psv)
{
psv->Refresh();
}
goto ReturnFromOKButtonPressed;
}
else if ((nFileOffset != PARSE_DIRECTORYNAME) &&
(lpOFN->Flags & OFN_NOVALIDATE))
{
lpOFN->nFileOffset = nFileOffset;
lpOFN->nFileExtension = nOldExt;
if (lpOFN->lpstrFile)
{
cch = lstrlen(szBasicPath);
if (cch <= LOWORD(lpOFN->nMaxFile))
{
lstrcpy(lpOFN->lpstrFile, szBasicPath);
}
else
{
//
// Buffer is too small, so return the size of the buffer
// required to hold the string.
//
if (lpOFN->nMaxFile >= 3)
{
//
// For single file requests, we will never go over 64K
// because the filesystem is limited to 256.
//
#ifdef UNICODE
lpOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
lpOFN->lpstrFile[1] = CHAR_NULL;
#else
lpOFN->lpstrFile[0] = LOBYTE(cch);
lpOFN->lpstrFile[1] = HIBYTE(cch);
lpOFN->lpstrFile[2] = 0;
#endif
}
else
{
#ifdef UNICODE
lpOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
if (lpOFN->nMaxFile == 2)
{
lpOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
}
#else
lpOFN->lpstrFile[0] = LOBYTE(cch);
if (lpOFN->nMaxFile == 2)
{
lpOFN->lpstrFile[1] = HIBYTE(cch);
}
#endif
}
}
}
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;
}
}
else if ((nFileOffset == 2) && (szBasicPath[2] == CHAR_DOT))
{
lstrcpy(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)
{
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];
}
SetCursor(LoadCursor(NULL, IDC_ARROW));
InvalidFileWarningNew(hwndDlg, szBasicPath, nErrCode);
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)
{
//
// 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)
{
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 &&
(lpOFN->Flags & OFN_PATHMUSTEXIST) )
{
if (eCode == ECODE_BADPATH)
{
nErrCode = OF_PATHNOTFOUND;
}
else if (eCode == ECODE_BADDRIVE)
{
//
// 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;
}
//
// Full pattern?
//
if (IsWild(szBasicPath + nFileOffset))
{
if (!bUNCName)
{
SetCurrentFilter(szBasicPath + nFileOffset, Flags);
if (nTempOffset)
{
szBasicPath[nTempOffset] = 0;
JumpToPath(szBasicPath);
}
else if (psv)
{
psv->Refresh();
}
goto ReturnFromOKButtonPressed;
}
else
{
SetCurrentFilter(szBasicPath + nFileOffset, Flags);
szBasicPath[nFileOffset] = CHAR_NULL;
JumpToPath(szBasicPath);
// StringLower(szLastFilter);
goto ReturnFromOKButtonPressed;
}
}
if (PortName(szBasicPath + nFileOffset))
{
nErrCode = OF_PORTNAME;
goto Warning;
}
//
// 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))
{
lstrcpy(szBuf, szBasicPath + 2);
lstrcpy(szBasicPath + 4, szBuf);
szBasicPath[2] = CHAR_DOT;
szBasicPath[3] = CHAR_BSLASH;
nExtOffset += 2;
}
//
// 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 ) :
{
CDMessageBox( hwndDlg,
iszTooManyFiles,
MB_OK | MB_ICONEXCLAMATION,
szBasicPath );
goto ReturnFromOKButtonPressed;
}
default :
{
break;
}
}
switch (nErrCode)
{
case ( 0 ) :
{
//
// Is the file read-only?
//
if ((lpOFN->Flags & OFN_NOREADONLYRETURN) &&
(GetFileAttributes(szPathName) & FILE_ATTRIBUTE_READONLY))
{
nErrCode = OF_LAZYREADONLY;
goto Warning;
}
if ((bSave || (lpOFN->Flags & OFN_NOREADONLYRETURN)) &&
(nTempOffset = WriteProtectedDirCheck(szPathName)))
{
nErrCode = nTempOffset;
goto Warning;
}
if (lpOFN->Flags & OFN_OVERWRITEPROMPT)
{
if (bSave && !FOkToWriteOver(hwndDlg, szPathName))
{
PostMessage( hwndDlg,
WM_NEXTDLGCTL,
(WPARAM)GetDlgItem(hwndDlg, edt1),
1 );
goto ReturnFromOKButtonPressed;
}
}
if (nErrCode == OF_SHARINGVIOLATION)
{
goto SharingViolationInquiry;
}
break;
}
case ( OF_SHARINGVIOLATION ) :
{
SharingViolationInquiry:
//
// If the app is "share aware", fall through.
// Otherwise, ask the hook function.
//
if (!(lpOFN->Flags & OFN_SHAREAWARE))
{
if (hSubDlg)
{
int nShareCode = CD_SendShareNotify( hSubDlg,
hwndDlg,
szPathName,
lpOFN,
lpOFI );
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, lpOFI->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 (lpOFN->Flags & OFN_FILEMUSTEXIST)
{
if (lpOFN->Flags & OFN_CREATEPROMPT)
{
int nCreateCode = CreateFileDlg(hwndDlg, szBasicPath);
if (nCreateCode != IDYES)
{
goto ReturnFromOKButtonPressed;
}
}
else
{
goto Warning;
}
}
}
goto VerifyPath;
}
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 (lpOFN->Flags & OFN_PATHMUSTEXIST)
{
if (!(lpOFN->Flags & OFN_NOTESTFILECREATE))
{
HANDLE hf = CreateFile( szBasicPath,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
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;
}
}
}
}
}
nFileOffset = ParseFileOld(szPathName, &nExtOffset, &nOldExt, FALSE, TRUE);
lpOFN->nFileOffset = nFileOffset;
lpOFN->nFileExtension = nOldExt;
lpOFN->Flags &= ~OFN_EXTENSIONDIFFERENT;
if (lpOFN->lpstrDefExt && lpOFN->nFileExtension)
{
TCHAR szPrivateExt[MIN_DEFEXT_LEN];
//
// Check against lpOFN->lpstrDefExt, not pszDefExt.
//
lstrcpyn(szPrivateExt, lpOFN->lpstrDefExt, MIN_DEFEXT_LEN);
if (lstrcmpi(szPrivateExt, szPathName + nOldExt))
{
lpOFN->Flags |= OFN_EXTENSIONDIFFERENT;
}
}
if (lpOFN->lpstrFile)
{
cch = lstrlen(szPathName) + 1;
if (lpOFN->Flags & OFN_ALLOWMULTISELECT)
{
//
// Extra room for double-NULL.
//
++cch;
}
if (cch <= LOWORD(lpOFN->nMaxFile))
{
lstrcpy(lpOFN->lpstrFile, szPathName);
if (lpOFN->Flags & OFN_ALLOWMULTISELECT)
{
//
// Double-NULL terminate.
//
*(lpOFN->lpstrFile + cch - 1) = CHAR_NULL;
}
if (!(lpOFN->Flags & OFN_NOCHANGEDIR) && !bUNCName && nFileOffset)
{
TCHAR ch = lpOFN->lpstrFile[nFileOffset];
lpOFN->lpstrFile[nFileOffset] = CHAR_NULL;
SetCurrentDirectory(lpOFN->lpstrFile);
lpOFN->lpstrFile[nFileOffset] = ch;
}
}
else
{
//
// Buffer is too small, so return the size of the buffer
// required to hold the string.
//
if (lpOFN->nMaxFile >= 3)
{
#ifdef UNICODE
lpOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
lpOFN->lpstrFile[1] = CHAR_NULL;
#else
lpOFN->lpstrFile[0] = LOBYTE(cch);
lpOFN->lpstrFile[1] = HIBYTE(cch);
lpOFN->lpstrFile[2] = 0;
#endif
}
else
{
#ifdef UNICODE
lpOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
if (lpOFN->nMaxFile == 2)
{
lpOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
}
#else
lpOFN->lpstrFile[0] = LOBYTE(cch);
if (lpOFN->nMaxFile == 2)
{
lpOFN->lpstrFile[1] = HIBYTE(cch);
}
#endif
}
}
}
if (!PathIsExe(szPathName))
{
SHAddToRecentDocs(SHARD_PATH, szPathName);
}
//
// 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.
//
if (lpOFN->lpstrFileTitle)
{
cch = lstrlen(szPathName + nFileOffset);
if ((DWORD)cch >= lpOFN->nMaxFileTitle)
{
#ifdef DBCS
EliminateString( szPathName + nFileOffset,
lpOFN->nMaxFileTitle - 1 );
#else
szPathName[nFileOffset + lpOFN->nMaxFileTitle - 1] = CHAR_NULL;
#endif
}
lstrcpy(lpOFN->lpstrFileTitle, szPathName + nFileOffset);
}
if (!(lpOFN->Flags & OFN_HIDEREADONLY))
{
//
// Read-only checkbox visible?
//
if (IsDlgButtonChecked(hwndDlg, chx1))
{
lpOFN->Flags |= OFN_READONLY;
}
else
{
lpOFN->Flags &= ~OFN_READONLY;
}
}
bRet = TRUE;
ReturnFromOKButtonPressed:
if (pExpFile != szExpFile)
{
LocalFree(pExpFile);
}
SetCursor(LoadCursor(NULL, IDC_ARROW));
return (bRet);
}
////////////////////////////////////////////////////////////////////////////
//
// 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;
}
TryAgain:
switch (uAction)
{
case ( OCDL_TOGGLE ) :
{
uAction = SendMessage(hwndDriveList, CB_GETDROPPEDSTATE, 0, 0L)
? OCDL_CLOSE
: OCDL_OPEN;
goto TryAgain;
break;
}
case ( OCDL_OPEN ) :
{
SetFocus(hwndDriveList);
SendMessage(hwndDriveList, CB_SHOWDROPDOWN, TRUE, 0);
break;
}
case ( OCDL_CLOSE ) :
{
if (GetFocus() == hwndDriveList)
{
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 cLen,
TEMPSTR *pTempStr,
BOOL *pbNoDefExt)
{
UINT cTotalLen;
HWND hwndEdit;
if (bUseHideExt)
{
cTotalLen = lstrlen(pszHideExt) + 1;
}
else
{
hwndEdit = GetDlgItem(hwndDlg, edt1);
cTotalLen = GetWindowTextLength(hwndEdit) + 1;
}
if (pTempStr)
{
if (!pTempStr->StrSize(cTotalLen))
{
return ((UINT)-1);
}
pszBuf = *pTempStr;
cLen = cTotalLen;
}
if (bUseHideExt)
{
lstrcpyn(pszBuf, pszHideExt, cLen);
}
else
{
GetWindowText(hwndEdit, pszBuf, cLen);
}
if (pbNoDefExt)
{
*pbNoDefExt = bUseHideExt;
}
return (cTotalLen);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::ProcessEdit
//
////////////////////////////////////////////////////////////////////////////
void CFileOpenBrowser::ProcessEdit()
{
TEMPSTR pMultiSel;
LPTSTR pszFile;
BOOL bNoDefExt;
OKBUTTONFLAGS Flags = OKBUTTON_NONE;
if (lpOFN->Flags & OFN_ALLOWMULTISELECT)
{
if (GetFullEditName( szBuf,
ARRAYSIZE(szBuf),
&pMultiSel,
&bNoDefExt ) == (UINT)-1)
{
//
// BUGBUG: There should be some error message here.
//
return;
}
pszFile = pMultiSel;
}
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;
lstrcpy(pszFile, pszFile + 1);
}
}
}
if (bNoDefExt)
{
Flags |= OKBUTTON_NODEFEXT;
}
#ifdef UNICODE
//
// 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 (lpOFI->ApiType == COMDLG_ANSI)
{
ThunkOpenFileNameA2WDelayed(lpOFI);
}
#endif
if (OKButtonPressed(pszFile, Flags))
{
BOOL bReturn = TRUE;
if (lpOFN->lpstrFile)
{
if (!(lpOFN->Flags & OFN_NOVALIDATE))
{
if (lpOFN->nMaxFile >= 3)
{
if ((lpOFN->lpstrFile[0] == 0) ||
(lpOFN->lpstrFile[1] == 0) ||
(lpOFN->lpstrFile[2] == 0))
{
bReturn = FALSE;
StoreExtendedError(FNERR_BUFFERTOOSMALL);
}
}
else
{
bReturn = FALSE;
StoreExtendedError(FNERR_BUFFERTOOSMALL);
}
}
}
CleanupDialog(hwndDlg, bReturn);
}
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::InitializeDropDown
//
////////////////////////////////////////////////////////////////////////////
void CFileOpenBrowser::InitializeDropDown(
HWND hwndCtl)
{
if (!bDropped)
{
MYLISTBOXITEM *pParentItem;
SHChangeNotifyEntry fsne[2];
pParentItem = GetListboxItem(hwndCtl, NODE_DESKTOP);
UpdateLevel(hwndCtl, NODE_DESKTOP + 1, pParentItem);
fsne[0].pidl = pParentItem->pidlFull;
fsne[0].fRecursive = FALSE;
pParentItem = GetListboxItem(hwndCtl, NODE_DRIVES);
UpdateLevel(hwndCtl, NODE_DRIVES + 1, pParentItem);
bDropped = TRUE;
fsne[1].pidl = pParentItem->pidlFull;
fsne[1].fRecursive = FALSE;
uRegister = SHChangeNotifyRegister(
hwndDlg,
SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
SHCNE_ALLEVENTS &
~(SHCNE_CREATE | SHCNE_DELETE | SHCNE_RENAMEITEM),
CDM_FSNOTIFY,
2,
fsne );
}
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::OnCommandMessage
//
// Process a WM_COMMAND message for the dialog.
//
////////////////////////////////////////////////////////////////////////////
LRESULT CFileOpenBrowser::OnCommandMessage(
WPARAM wParam,
LPARAM lParam)
{
UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);
switch (idCmd)
{
case ( edt1 ) :
{
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
{
//
// The user started typing, so delete the hidden extension.
//
bUseHideExt = FALSE;
}
break;
}
case ( cmb2 ) :
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case ( CBN_CLOSEUP ) :
{
OnSelChange();
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 = 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 ) :
{
ViewCommand(VC_VIEWLIST);
break;
}
case ( IDC_VIEWDETAILS ) :
{
ViewCommand(VC_VIEWDETAILS);
break;
}
case ( IDOK ) :
{
HWND hwndFocus = ::GetFocus();
if (hwndFocus == ::GetDlgItem(hwndDlg, IDOK))
{
hwndFocus = hwndLastFocus;
}
hwndFocus = GetFocusedChild(hwndDlg, hwndFocus);
if (hwndFocus == hwndView)
{
OnDblClick(TRUE);
}
else
{
ProcessEdit();
}
SelectEditText(hwndDlg);
break;
}
case ( IDCANCEL ) :
{
bUserPressedCancel = TRUE;
CleanupDialog(hwndDlg, FALSE);
return (TRUE);
}
case ( pshHelp ) :
{
if (hSubDlg)
{
CD_SendHelpNotify(hSubDlg, hwndDlg, lpOFN, lpOFI);
}
if (lpOFN->hwndOwner)
{
CD_SendHelpMsg(lpOFN, hwndDlg, lpOFI->ApiType);
}
break;
}
case ( IDC_DROPDRIVLIST ) :
{
DriveList_OpenClose(OCDL_TOGGLE, GetDlgItem(hwndDlg, cmb2));
break;
}
case ( IDC_REFRESH ) :
{
if (psv)
{
psv->Refresh();
}
break;
}
case ( IDC_PREVIOUSFOLDER ) :
{
OnDotDot();
break;
}
}
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// CFileOpenBrowser::OnCDMessage
//
// Process a special CommDlg message for the dialog.
//
////////////////////////////////////////////////////////////////////////////
BOOL CFileOpenBrowser::OnCDMessage(
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
LONG lResult;
LPCITEMIDLIST pidl;
LPTSTR pBuf = (LPTSTR)lParam;
#ifdef UNICODE
LPWSTR pBufW = NULL;
int cbLen;
#endif
switch (uMsg)
{
case ( CDM_GETSPEC ) :
case ( CDM_GETFILEPATH ) :
case ( CDM_GETFOLDERPATH ) :
{
#ifdef UNICODE
if (lpOFI->ApiType == COMDLG_ANSI)
{
if (pBufW = (LPWSTR)LocalAlloc( LPTR,
(int)wParam * sizeof(WCHAR) ))
{
pBuf = pBufW;
}
else
{
break;
}
}
#endif
if (uMsg == CDM_GETSPEC)
{
lResult = GetFullEditName(pBuf, wParam);
break;
}
// else, fall thru...
}
case ( CDM_GETFOLDERIDLIST ) :
{
pidl = pCurrentLocation->pidlFull;
lResult = ILGetSize(pidl);
if (uMsg == CDM_GETFOLDERIDLIST)
{
if ((LONG)wParam < lResult)
{
break;
}
CopyMemory((LPBYTE)pBuf, (LPBYTE)pidl, lResult);
break;
}
TCHAR szDir[MAX_PATH];
if (!SHGetPathFromIDList(pidl, szDir))
{
lResult = -1;
break;
}
if (uMsg == CDM_GETFOLDERPATH)
{
CopyAndReturn:
lResult = lstrlen(szDir) + 1;
if ((int)wParam > lResult)
{
wParam = lResult;
}
lstrcpyn(pBuf, szDir, wParam);
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)) >
ARRAYSIZE(szFile) - 5 )
{
//
// Oops! It looks like we filled our buffer!
//
lResult = -1;
break;
}
PathCombine(szDir, szDir, szFile);
goto CopyAndReturn;
}
case ( CDM_SETCONTROLTEXT ) :
{
#ifdef UNICODE
if (lpOFI->ApiType == COMDLG_ANSI)
{
//
// Need to convert pBuf (lParam) to Unicode.
//
cbLen = lstrlenA((LPSTR)pBuf) + 1;
if (pBufW = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR))))
{
MultiByteToWideChar( CP_ACP,
0,
(LPSTR)pBuf,
-1,
pBufW,
cbLen );
pBuf = pBufW;
}
}
#endif
if (bSave && wParam == IDOK)
{
tszDefSave.StrCpy(pBuf);
//
// Do this to set the OK button correctly.
//
SelFocusChange(TRUE);
}
else
{
SetDlgItemText(hwndDlg, wParam, pBuf);
}
break;
}
case ( CDM_HIDECONTROL ) :
{
ShowWindow(GetDlgItem(hwndDlg, wParam), SW_HIDE);
break;
}
case ( CDM_SETDEFEXT ) :
{
#ifdef UNICODE
if (lpOFI->ApiType == COMDLG_ANSI)
{
//
// Need to convert pBuf (lParam) to Unicode.
//
cbLen = lstrlenA((LPSTR)pBuf) + 1;
if (pBufW = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR))))
{
MultiByteToWideChar( CP_ACP,
0,
(LPSTR)pBuf,
-1,
pBufW,
cbLen );
pBuf = pBufW;
}
}
#endif
pszDefExt.StrCpy(pBuf);
bNoInferDefExt = TRUE;
break;
}
default:
{
lResult = -1;
break;
}
}
SetWindowLong(hwndDlg, DWL_MSGRESULT, lResult);
#ifdef UNICODE
if (lpOFI->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.
//
WideCharToMultiByte( CP_ACP,
0,
pBuf,
-1,
(LPSTR)lParam,
wParam,
NULL,
NULL );
break;
}
}
if (pBufW)
{
LocalFree(pBufW);
}
}
#endif
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)
{
switch (msg)
{
case ( WM_SETFOCUS ) :
{
HWND hwndDlg = ::GetParent(hOK);
CFileOpenBrowser *pDlgStruct = HwndToBrowser(hwndDlg);
if (pDlgStruct != NULL)
{
pDlgStruct->hwndLastFocus = (HWND)wParam;
}
}
break;
}
return (::CallWindowProc(::lpOKProc, 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 = NODE_DRIVES; 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;
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;
pidl = ppidl[0];
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 ) :
{
pidl = ppidl[0];
iNode = GetNodeFromIDList(pidl);
break;
}
case ( SHCNE_DRIVEREMOVED ) :
case ( SHCNE_DRIVEADD ) :
case ( SHCNE_MEDIAINSERTED ) :
case ( SHCNE_MEDIAREMOVED ) :
{
iNode = NODE_DRIVES;
break;
}
#if 0
case ( SHCNE_SERVERDISCONNECT ) :
{
//
// Nuke all our kids and mark ourselves invalid.
//
pidl = ppidl[0];
lpNode = GetNodeFromIDList(pidl, 0);
if (lpNode && NodeHasKids(lpNode))
{
int i;
for (i = GetKidCount(lpNode) - 1; i >= 0; i--)
{
OTRelease(GetNthKid(lpNode, i));
}
DPA_Destroy(lpNode->hdpaKids);
lpNode->hdpaKids = KIDSUNKNOWN;
OTInvalidateNode(lpNode);
SFCFreeNode(lpNode);
}
else
{
lpNode = NULL;
}
}
#endif
}
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, wID);
wID -= TIMER_FSCHANGE;
Assert(this->bDropped);
switch (wID)
{
case ( NODE_DESKTOP ) :
case ( NODE_DRIVES ) :
{
HWND hwndCB;
MYLISTBOXITEM *pParentItem;
hwndCB = GetDlgItem(hwndDlg, cmb2);
pParentItem = GetListboxItem(hwndCB, wID);
UpdateLevel(hwndCB, wID + 1, pParentItem);
break;
}
default :
{
return;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// OpenDlgProc
//
// Main dialog procedure for file open dialogs.
//
////////////////////////////////////////////////////////////////////////////
BOOL 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);
switch (message)
{
case ( WM_INITDIALOG ) :
{
//
// Initialize dialog box.
//
LPOFNINITINFO poii = (LPOFNINITINFO)lParam;
if (!InitLocation(hDlg, poii))
{
::EndDialog(hDlg, FALSE);
}
//
// Always return FALSE to indicate we have already set the focus.
//
return (FALSE);
}
case ( WM_DESTROY ) :
{
//
// Make sure we do not respond to any more messages.
//
StoreBrowser(hDlg, NULL);
ClearListbox(GetDlgItem(hDlg, cmb2));
if (pDlgStruct)
{
pDlgStruct->Release();
}
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;
}
break;
}
case ( WM_COMMAND ) :
{
//
// Received a command.
//
if (pDlgStruct)
{
return (pDlgStruct->OnCommandMessage(wParam, lParam));
}
break;
}
case ( WM_DRAWITEM ) :
{
if (pDlgStruct)
{
pDlgStruct->PaintDriveLine((DRAWITEMSTRUCT *)lParam);
}
return (TRUE);
}
case ( WM_MEASUREITEM ) :
{
MeasureDriveItems(hDlg, (MEASUREITEMSTRUCT *)lParam);
return (TRUE);
}
case ( WM_NOTIFY ) :
{
if (pDlgStruct)
{
return (pDlgStruct->OnNotify((LPNMHDR)lParam));
}
break;
}
case ( WM_SETCURSOR ) :
{
if (pDlgStruct && pDlgStruct->iWaitCount)
{
SetCursor(LoadCursor(NULL, IDC_WAIT));
SetDlgMsgResult(hDlg, message, (LRESULT)TRUE);
return (TRUE);
}
break;
}
case ( WM_HELP ) :
{
if ( ((HWND)((LPHELPINFO)lParam)->hItemHandle) !=
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,
(DWORD)(LPTSTR)(pDlgStruct && pDlgStruct->bSave
? aFileSaveHelpIDs
: aFileOpenHelpIDs) );
}
return (TRUE);
}
case ( WM_CONTEXTMENU ) :
{
if (((HWND)wParam) != pDlgStruct->hwndToolbar)
{
WinHelp( (HWND)wParam,
NULL,
HELP_CONTEXTMENU,
(DWORD)(LPVOID)(pDlgStruct && pDlgStruct->bSave
? aFileSaveHelpIDs
: aFileOpenHelpIDs) );
}
return (TRUE);
}
case ( CWM_GETISHELLBROWSER ) :
{
::SetWindowLong(hDlg, DWL_MSGRESULT, (LRESULT)pDlgStruct);
return (TRUE);
}
case ( CDM_SETSAVEBUTTON ) :
{
if (pDlgStruct)
{
pDlgStruct->RealSetSaveButton((UINT)wParam);
}
break;
}
case ( CDM_FSNOTIFY ) :
{
LPITEMIDLIST *ppidl;
LONG lEvent;
BOOL bRet;
LPSHChangeNotificationLock pLock;
if (!pDlgStruct)
{
return (0L);
}
//
// 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)
{
return (0L);
}
bRet = pDlgStruct->FSChange(lEvent, (LPCITEMIDLIST *)ppidl);
//
// Release the shared block.
//
SHChangeNotification_Unlock(pLock);
return (bRet);
}
case ( CDM_SELCHANGE ) :
{
if (!pDlgStruct)
{
break;
}
pDlgStruct->fSelChangedPending = FALSE;
pDlgStruct->SelFocusChange(TRUE);
if (pDlgStruct->hSubDlg)
{
CD_SendSelChangeNotify( pDlgStruct->hSubDlg,
hDlg,
pDlgStruct->lpOFN,
pDlgStruct->lpOFI );
}
break;
}
case ( WM_TIMER ) :
{
pDlgStruct->Timer(wParam);
break;
}
default :
{
if (IsInRange(message, CDM_FIRST, CDM_LAST) && pDlgStruct)
{
return (pDlgStruct->OnCDMessage(message, wParam, lParam));
}
break;
}
}
//
// Did not process the message.
//
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// OpenFileHookProc
//
////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK OpenFileHookProc(
int nCode,
WPARAM wParam,
LPARAM lParam)
{
CFileOpenBrowser *pDlgStruct;
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);
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 };
LPOPENFILENAME lpOFN = lpOFI->pOFN;
BOOL bHooked = FALSE;
WORD wErrorMode;
if (lpOFN->lStructSize != sizeof(OPENFILENAME))
{
StoreExtendedError(CDERR_STRUCTSIZE);
return (FALSE);
}
if (!InitImports())
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
wErrorMode = (WORD)SetErrorMode(SEM_NOERROR);
SetErrorMode(SEM_NOERROR | wErrorMode);
//
// These hooks are REALLY stupid. 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) );
}
HIMAGELIST himl;
Shell_GetImageLists(NULL, &himl);
ImageList_GetIconSize(himl, &g_cxSmIcon, &g_cySmIcon);
int nRet = DialogBoxParam( ::g_hinst,
MAKEINTRESOURCE(NEWFILEOPENORD),
lpOFN->hwndOwner,
OpenDlgProc,
(LPARAM)(LPOFNINITINFO)&oii );
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 ((!bUserPressedCancel) && (!GetStoredExtendedError()))
{
StoreExtendedError(CDERR_DIALOGFAILURE);
}
break;
}
default :
{
StoreExtendedError(CDERR_DIALOGFAILURE);
nRet = FALSE;
break;
}
}
//
// BUGBUG.
// 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"
////////////////////////////////////////////////////////////////////////////
//
// Overloaded allocation operators.
//
////////////////////////////////////////////////////////////////////////////
static inline void * __cdecl operator new(
unsigned int size)
{
return ((void *)LocalAlloc(LPTR, size));
}
static inline void __cdecl operator delete(
void *ptr)
{
LocalFree(ptr);
}
extern "C" inline __cdecl _purecall(void)
{
return (0);
}
////////////////////////////////////////////////////////////////////////////
//
// InitImports
//
// Import all the APIs we need from shell32.dll and comctl32.dll, so we
// don't have hard links to them.
//
////////////////////////////////////////////////////////////////////////////
BOOL InitImports(void)
{
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// FreeImports
//
// Unload the DLLs we loaded in InitImports().
// This should only be called at ProcessDetach time, since we do not NULL
// out all the things that are now invalid.
//
////////////////////////////////////////////////////////////////////////////
const TCHAR c_szShellDll[] = TEXT("shell32.dll");
const TCHAR c_szComCtl32[] = TEXT("comctl32.dll");
typedef struct _DLLINFO
{
HINSTANCE hInst;
LPCTSTR pszInst;
} DLLINFO;
DLLINFO diShellDll =
{
NULL, c_szShellDll
};
DLLINFO diComCtl32 =
{
NULL, c_szComCtl32
};
void FreeImports(void)
{
//
// No critical section needed since only touching process globals
// during process detach.
//
if (diShellDll.hInst)
{
FreeLibrary(diShellDll.hInst);
}
if (diComCtl32.hInst)
{
FreeLibrary(diComCtl32.hInst);
}
}