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