mirror of https://github.com/lianthony/NT4.0
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
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);
|
|
}
|
|
}
|
|
|