You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1099 lines
37 KiB
1099 lines
37 KiB
#include "precomp.h" // pch file
|
|
#include "sendto.h"
|
|
#pragma hdrstop
|
|
|
|
CLIPFORMAT g_cfShellURL = 0;
|
|
CLIPFORMAT g_cfFileContents = 0;
|
|
CLIPFORMAT g_cfFileDescA = 0;
|
|
CLIPFORMAT g_cfFileDescW = 0;
|
|
CLIPFORMAT g_cfHIDA = 0;
|
|
|
|
|
|
// registry key for recompressing settings
|
|
|
|
struct
|
|
{
|
|
int cx;
|
|
int cy;
|
|
int iQuality;
|
|
}
|
|
_aQuality[] =
|
|
{
|
|
{ 640, 480, 80 }, // low quality
|
|
{ 800, 600, 80 }, // medium quality
|
|
{ 1024, 768, 80 }, // high quality
|
|
};
|
|
|
|
#define QUALITY_LOW 0
|
|
#define QUALITY_MEDIUM 1
|
|
#define QUALITY_HIGH 2
|
|
|
|
#define RESPONSE_UNKNOWN 0
|
|
#define RESPONSE_CANCEL 1
|
|
#define RESPONSE_ORIGINAL 2
|
|
#define RESPONSE_RECOMPRESS 3
|
|
|
|
#define RECTWIDTH(rc) ((rc).right-(rc).left)
|
|
#define RECTHEIGHT(rc) ((rc).bottom-(rc).top)
|
|
|
|
|
|
// these bits are set by the user (holding down the keys) durring drag drop,
|
|
// but more importantly, they are set in the SimulateDragDrop() call that the
|
|
// browser implements to get the "Send Page..." vs "Send Link..." feature
|
|
|
|
#define IS_FORCE_LINK(grfKeyState) ((grfKeyState == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) || \
|
|
(grfKeyState == (MK_LBUTTON | MK_ALT)))
|
|
|
|
#define IS_FORCE_COPY(grfKeyState) (grfKeyState == (MK_LBUTTON | MK_CONTROL))
|
|
|
|
|
|
|
|
// constructor / destructor
|
|
|
|
CSendTo::CSendTo(CLSID clsid) :
|
|
_clsid(clsid), _cRef(1), _iRecompSetting(QUALITY_LOW)
|
|
{
|
|
|
|
DllAddRef();
|
|
}
|
|
|
|
CSendTo::~CSendTo()
|
|
{
|
|
if (_pStorageTemp)
|
|
_pStorageTemp->Release();
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
STDMETHODIMP CSendTo::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CSendTo, IShellExtInit),
|
|
QITABENT(CSendTo, IDropTarget),
|
|
QITABENT(CSendTo, IPersistFile),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CSendTo::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CSendTo::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
STDMETHODIMP CSendTo::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
TraceMsg(DM_TRACE, "CSendTo::DragEnter");
|
|
_grfKeyStateLast = grfKeyState;
|
|
_dwEffectLast = *pdwEffect;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSendTo::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
*pdwEffect &= ~DROPEFFECT_MOVE;
|
|
|
|
if (IS_FORCE_COPY(grfKeyState))
|
|
*pdwEffect &= DROPEFFECT_COPY;
|
|
else if (IS_FORCE_LINK(grfKeyState))
|
|
*pdwEffect &= DROPEFFECT_LINK;
|
|
|
|
_grfKeyStateLast = grfKeyState;
|
|
_dwEffectLast = *pdwEffect;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSendTo::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
HRESULT hr = v_DropHandler(pdtobj, _grfKeyStateLast, _dwEffectLast);
|
|
*pdwEffect = DROPEFFECT_COPY; // don't let source delete data
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// helper methods
|
|
//
|
|
|
|
int CSendTo::_PathCleanupSpec(/*IN OPTIONAL*/ LPCTSTR pszDir, /*IN OUT*/ LPTSTR pszSpec)
|
|
{
|
|
WCHAR wzDir[MAX_PATH];
|
|
WCHAR wzSpec[MAX_PATH];
|
|
LPWSTR pwszDir = wzDir;
|
|
int iRet;
|
|
|
|
if (pszDir)
|
|
SHTCharToUnicode(pszDir, wzDir, ARRAYSIZE(wzDir));
|
|
else
|
|
pwszDir = NULL;
|
|
|
|
SHTCharToUnicode(pszSpec, wzSpec, ARRAYSIZE(wzSpec));
|
|
iRet = PathCleanupSpec((LPTSTR)pwszDir, (LPTSTR)wzSpec);
|
|
|
|
SHUnicodeToTChar(wzSpec, pszSpec, MAX_PATH);
|
|
return iRet;
|
|
}
|
|
|
|
HRESULT CSendTo::_CreateShortcutToPath(LPCTSTR pszPath, LPCTSTR pszTarget)
|
|
{
|
|
IUnknown *punk;
|
|
HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punk));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ShellLinkSetPath(punk, pszTarget);
|
|
|
|
IPersistFile *ppf;
|
|
hr = punk->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR wszPath[MAX_PATH];
|
|
SHTCharToUnicode(pszPath, wszPath, ARRAYSIZE(wszPath));
|
|
|
|
hr = ppf->Save(wszPath, TRUE);
|
|
ppf->Release();
|
|
}
|
|
punk->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// thunk A/W funciton to access A/W FILEGROUPDESCRIPTOR
|
|
// this relies on the fact that the first part of the A/W structures are
|
|
// identical. only the string buffer part is different. so all accesses to the
|
|
// cFileName field need to go through this function.
|
|
|
|
FILEDESCRIPTOR* CSendTo::_GetFileDescriptor(FILEGROUPDESCRIPTOR *pfgd, BOOL fUnicode, int nIndex, LPTSTR pszName)
|
|
{
|
|
if (fUnicode)
|
|
{
|
|
// Yes, so grab the data because it matches.
|
|
FILEGROUPDESCRIPTORW * pfgdW = (FILEGROUPDESCRIPTORW *)pfgd; // cast to what this really is
|
|
if (pszName)
|
|
SHUnicodeToTChar(pfgdW->fgd[nIndex].cFileName, pszName, MAX_PATH);
|
|
|
|
return (FILEDESCRIPTOR *)&pfgdW->fgd[nIndex]; // cast assume the non string parts are the same!
|
|
}
|
|
else
|
|
{
|
|
FILEGROUPDESCRIPTORA *pfgdA = (FILEGROUPDESCRIPTORA *)pfgd; // cast to what this really is
|
|
|
|
if (pszName)
|
|
SHAnsiToTChar(pfgdA->fgd[nIndex].cFileName, pszName, MAX_PATH);
|
|
|
|
return (FILEDESCRIPTOR *)&pfgdA->fgd[nIndex]; // cast assume the non string parts are the same!
|
|
}
|
|
}
|
|
|
|
|
|
// our own impl since URLMON IStream::CopyTo is busted, danpoz will be fixing this
|
|
HRESULT CSendTo::_StreamCopyTo(IStream *pstmFrom, IStream *pstmTo, LARGE_INTEGER cb, LARGE_INTEGER *pcbRead, LARGE_INTEGER *pcbWritten)
|
|
{
|
|
BYTE buf[512];
|
|
ULONG cbRead;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pcbRead)
|
|
{
|
|
pcbRead->LowPart = 0;
|
|
pcbRead->HighPart = 0;
|
|
}
|
|
if (pcbWritten)
|
|
{
|
|
pcbWritten->LowPart = 0;
|
|
pcbWritten->HighPart = 0;
|
|
}
|
|
|
|
ASSERT(cb.HighPart == 0);
|
|
|
|
while (cb.LowPart)
|
|
{
|
|
hr = pstmFrom->Read(buf, min(cb.LowPart, SIZEOF(buf)), &cbRead);
|
|
|
|
if (pcbRead)
|
|
pcbRead->LowPart += cbRead;
|
|
|
|
if (FAILED(hr) || (cbRead == 0))
|
|
break;
|
|
|
|
cb.LowPart -= cbRead;
|
|
|
|
hr = pstmTo->Write(buf, cbRead, &cbRead);
|
|
|
|
if (pcbWritten)
|
|
pcbWritten->LowPart += cbRead;
|
|
|
|
if (FAILED(hr) || (cbRead == 0))
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// create a temporary shortcut to a file
|
|
// FEATURE: Colision is not handled here
|
|
|
|
BOOL CSendTo::_CreateTempFileShortcut(LPCTSTR pszTarget, LPTSTR pszShortcut, int cchShortcut)
|
|
{
|
|
TCHAR szShortcutPath[MAX_PATH + 1];
|
|
BOOL bSuccess = FALSE;
|
|
|
|
if (GetTempPath(ARRAYSIZE(szShortcutPath), szShortcutPath))
|
|
{
|
|
PathAppend(szShortcutPath, PathFindFileName(pszTarget));
|
|
|
|
if (IsShortcut(pszTarget))
|
|
{
|
|
TCHAR szTarget[MAX_PATH + 1];
|
|
SHFILEOPSTRUCT shop = {0};
|
|
shop.wFunc = FO_COPY;
|
|
shop.pFrom = szTarget;
|
|
shop.pTo = szShortcutPath;
|
|
shop.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
|
|
|
|
StrCpyN(szTarget, pszTarget, ARRAYSIZE(szTarget));
|
|
szTarget[lstrlen(szTarget) + 1] = TEXT('\0');
|
|
|
|
szShortcutPath[lstrlen(szShortcutPath) + 1] = TEXT('\0');
|
|
|
|
bSuccess = (0 == SHFileOperation(&shop));
|
|
}
|
|
else
|
|
{
|
|
PathRenameExtension(szShortcutPath, TEXT(".lnk"));
|
|
bSuccess = SUCCEEDED(_CreateShortcutToPath(szShortcutPath, pszTarget));
|
|
}
|
|
|
|
if (bSuccess)
|
|
StrCpyN(pszShortcut, szShortcutPath, cchShortcut);
|
|
}
|
|
return bSuccess;
|
|
}
|
|
|
|
|
|
HRESULT CSendTo::_GetFileNameFromData(IDataObject *pdtobj, FORMATETC *pfmtetc, LPTSTR pszDescription, int cchDescription)
|
|
{
|
|
STGMEDIUM medium;
|
|
HRESULT hr = pdtobj->GetData(pfmtetc, &medium);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// NOTE: this is a TCHAR format, we depend on how we are compiled, we really
|
|
// should test both the A and W formats
|
|
FILEGROUPDESCRIPTOR *pfgd = (FILEGROUPDESCRIPTOR *)GlobalLock(medium.hGlobal);
|
|
if (pfgd)
|
|
{
|
|
TCHAR szFdName[MAX_PATH]; // pfd->cFileName
|
|
FILEDESCRIPTOR *pfd;
|
|
|
|
// &pfgd->fgd[0], w/ thunk
|
|
ASSERT(pfmtetc->cfFormat == g_cfFileDescW
|
|
|| pfmtetc->cfFormat == g_cfFileDescA);
|
|
// for now, all callers are ANSI (other untested)
|
|
//ASSERT(pfmtetc->cfFormat == g_cfFileDescA);
|
|
pfd = _GetFileDescriptor(pfgd, pfmtetc->cfFormat == g_cfFileDescW, 0, szFdName);
|
|
|
|
StrCpyN(pszDescription, szFdName, cchDescription); // pfd->cFileName
|
|
|
|
GlobalUnlock(medium.hGlobal);
|
|
hr = S_OK;
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// construct a nice title "<File Name> (<File Type>)"
|
|
|
|
void CSendTo::_GetFileAndTypeDescFromPath(LPCTSTR pszPath, LPTSTR pszDesc, int cchDesc)
|
|
{
|
|
SHFILEINFO sfi;
|
|
if (!SHGetFileInfo(pszPath, 0, &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME | SHGFI_DISPLAYNAME))
|
|
{
|
|
StrCpyN(sfi.szDisplayName, PathFindFileName(pszPath), ARRAYSIZE(sfi.szDisplayName));
|
|
sfi.szTypeName[0] = 0;
|
|
}
|
|
StrCpyN(pszDesc, sfi.szDisplayName, cchDesc);
|
|
}
|
|
|
|
|
|
// pcszURL -> "ftp://ftp.microsoft.com"
|
|
// pcszPath -> "c:\windows\desktop\internet\Microsoft FTP.url"
|
|
|
|
HRESULT CSendTo::_CreateNewURLShortcut(LPCTSTR pcszURL, LPCTSTR pcszURLFile)
|
|
{
|
|
IUniformResourceLocator *purl;
|
|
HRESULT hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUniformResourceLocator, &purl));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = purl->SetURL(pcszURL, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IPersistFile *ppf;
|
|
hr = purl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR wszFile[INTERNET_MAX_URL_LENGTH];
|
|
SHTCharToUnicode(pcszURLFile, wszFile, ARRAYSIZE(wszFile));
|
|
|
|
hr = ppf->Save(wszFile, TRUE);
|
|
ppf->Release();
|
|
}
|
|
}
|
|
purl->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CSendTo::_CreateURLFileToSend(IDataObject *pdtobj, MRPARAM *pmp)
|
|
{
|
|
MRFILEENTRY *pFile = pmp->pFiles;
|
|
HRESULT hr = CSendTo::_CreateNewURLShortcut(pFile->pszTitle, pFile->pszFileName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_GetFileAndTypeDescFromPath(pFile->pszFileName, pFile->pszTitle, pmp->cchTitle);
|
|
pFile->dwFlags |= MRFILE_DELETE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CSendTo::_GetHDROPFromData(IDataObject *pdtobj, FORMATETC *pfmtetc, STGMEDIUM *pmedium, DWORD grfKeyState, MRPARAM *pmp)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HDROP hDrop = (HDROP)pmedium->hGlobal;
|
|
|
|
pmp->nFiles = DragQueryFile(hDrop, -1, NULL, 0);
|
|
|
|
if (pmp->nFiles && AllocatePMP(pmp, MAX_PATH, MAX_PATH))
|
|
{
|
|
int i;
|
|
CFileEnum MREnum(pmp, NULL);
|
|
MRFILEENTRY *pFile;
|
|
|
|
for (i = 0; (pFile = MREnum.Next()) && DragQueryFile(hDrop, i, pFile->pszFileName, pmp->cchFile); ++i)
|
|
{
|
|
if (IS_FORCE_LINK(grfKeyState) || PathIsDirectory(pFile->pszFileName))
|
|
{
|
|
// Want to send a link even for the real file, we will create links to the real files
|
|
// and send it.
|
|
_CreateTempFileShortcut(pFile->pszFileName, pFile->pszFileName, pmp->cchFile);
|
|
pFile->dwFlags |= MRFILE_DELETE;
|
|
}
|
|
|
|
_GetFileAndTypeDescFromPath(pFile->pszFileName, pFile->pszTitle, pmp->cchTitle);
|
|
}
|
|
|
|
// If loop terminates early update our item count
|
|
pmp->nFiles = i;
|
|
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// "Uniform Resource Locator" format
|
|
|
|
HRESULT CSendTo::_GetURLFromData(IDataObject *pdtobj, FORMATETC *pfmtetc, STGMEDIUM *pmedium, DWORD grfKeyState, MRPARAM *pmp)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// This DataObj is from the internet
|
|
// NOTE: We only allow to send one file here.
|
|
pmp->nFiles = 1;
|
|
if (AllocatePMP(pmp, INTERNET_MAX_URL_LENGTH, MAX_PATH))
|
|
{
|
|
// n.b. STR not TSTR! since URLs only support ansi
|
|
//lstrcpyn(pmp->pszTitle, (LPSTR)GlobalLock(pmedium->hGlobal), INTERNET_MAX_URL_LENGTH);
|
|
MRFILEENTRY *pFile = pmp->pFiles;
|
|
SHAnsiToTChar((LPSTR)GlobalLock(pmedium->hGlobal), pFile->pszTitle, INTERNET_MAX_URL_LENGTH);
|
|
GlobalUnlock(pmedium->hGlobal);
|
|
|
|
if (pFile->pszTitle[0])
|
|
{
|
|
// Note some of these functions depend on which OS we
|
|
// are running on to know if we should pass ansi or unicode strings
|
|
// to it Windows 95
|
|
|
|
if (GetTempPath(MAX_PATH, pFile->pszFileName))
|
|
{
|
|
TCHAR szFileName[MAX_PATH];
|
|
|
|
// it's an URL, which is always ANSI, but the filename
|
|
// can still be wide (?)
|
|
FORMATETC fmteW = {g_cfFileDescW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
FORMATETC fmteA = {g_cfFileDescA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
|
|
if (FAILED(_GetFileNameFromData(pdtobj, &fmteW, szFileName, ARRAYSIZE(szFileName))))
|
|
{
|
|
if (FAILED(_GetFileNameFromData(pdtobj, &fmteA, szFileName, ARRAYSIZE(szFileName))))
|
|
{
|
|
LoadString(g_hinst, IDS_SENDMAIL_URL_FILENAME, szFileName, ARRAYSIZE(szFileName));
|
|
}
|
|
}
|
|
|
|
_PathCleanupSpec(pFile->pszFileName, szFileName);
|
|
hr = _CreateURLFileToSend(pdtobj, pmp);
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// transfer FILECONTENTS/FILEGROUPDESCRIPTOR data to a temp file then send that in mail
|
|
|
|
HRESULT CSendTo::_GetFileContentsFromData(IDataObject *pdtobj, FORMATETC *pfmtetc, STGMEDIUM *pmedium, DWORD grfKeyState, MRPARAM *pmp)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
MRFILEENTRY *pFile = NULL;
|
|
|
|
// NOTE: We only allow to send one file here.
|
|
pmp->nFiles = 1;
|
|
if (AllocatePMP(pmp, INTERNET_MAX_URL_LENGTH, MAX_PATH))
|
|
{
|
|
pFile = pmp->pFiles;
|
|
|
|
FILEGROUPDESCRIPTOR *pfgd = (FILEGROUPDESCRIPTOR *)GlobalLock(pmedium->hGlobal);
|
|
if (pfgd)
|
|
{
|
|
TCHAR szFdName[MAX_PATH]; // pfd->cFileName
|
|
FILEDESCRIPTOR *pfd;
|
|
|
|
// &pfgd->fgd[0], w/ thunk
|
|
ASSERT((pfmtetc->cfFormat == g_cfFileDescW) || (pfmtetc->cfFormat == g_cfFileDescA));
|
|
pfd = _GetFileDescriptor(pfgd, pfmtetc->cfFormat == g_cfFileDescW, 0, szFdName);
|
|
|
|
// the file we're about to create has contents from the internet.
|
|
// use the internet cache to mark it as "unsafe" so other pages that might know
|
|
// the filename can't refer to it and elevate their privileges.
|
|
// createurlcacheentry says first param has to be a unique string so just use this GUID.
|
|
if (CreateUrlCacheEntry(L"67a3caff-bedc-4090-a21e-492dd8935102", 0, NULL, pFile->pszFileName, 0))
|
|
{
|
|
PathRemoveFileSpec(pFile->pszFileName); // only interested in the dir that it sits in.
|
|
DeleteUrlCacheEntry(L"67a3caff-bedc-4090-a21e-492dd8935102");
|
|
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {g_cfFileContents, NULL, pfmtetc->dwAspect, 0, TYMED_ISTREAM | TYMED_HGLOBAL};
|
|
hr = pdtobj->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
PathAppend(pFile->pszFileName, szFdName); // pfd->cFileName
|
|
_PathCleanupSpec(pFile->pszFileName, PathFindFileName(pFile->pszFileName));
|
|
PathYetAnotherMakeUniqueName(pFile->pszFileName, pFile->pszFileName, NULL, NULL);
|
|
|
|
IStream *pstmFile;
|
|
hr = SHCreateStreamOnFile(pFile->pszFileName, STGM_WRITE | STGM_CREATE, &pstmFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
switch (medium.tymed)
|
|
{
|
|
case TYMED_ISTREAM:
|
|
{
|
|
const LARGE_INTEGER li = {-1, 0}; // the whole thing
|
|
hr = _StreamCopyTo(medium.pstm, pstmFile, li, NULL, NULL);
|
|
break;
|
|
}
|
|
|
|
case TYMED_HGLOBAL:
|
|
hr = pstmFile->Write(GlobalLock(medium.hGlobal),
|
|
pfd->dwFlags & FD_FILESIZE ? pfd->nFileSizeLow:(DWORD)GlobalSize(medium.hGlobal),
|
|
NULL);
|
|
GlobalUnlock(medium.hGlobal);
|
|
break;
|
|
|
|
default:
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
pstmFile->Release();
|
|
if (FAILED(hr))
|
|
DeleteFile(pFile->pszFileName);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
GlobalUnlock(pmedium->hGlobal);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_GetFileAndTypeDescFromPath(pFile->pszFileName, pFile->pszTitle, pmp->cchTitle);
|
|
pFile->dwFlags |= MRFILE_DELETE;
|
|
|
|
if (pfmtetc->dwAspect == DVASPECT_COPY)
|
|
{
|
|
pmp->dwFlags |= MRPARAM_DOC; // we are sending the document
|
|
|
|
// get the code page if there is one
|
|
IQueryCodePage *pqcp;
|
|
if (SUCCEEDED(pdtobj->QueryInterface(IID_PPV_ARG(IQueryCodePage, &pqcp))))
|
|
{
|
|
if (SUCCEEDED(pqcp->GetCodePage(&pmp->uiCodePage)))
|
|
pmp->dwFlags |= MRPARAM_USECODEPAGE;
|
|
pqcp->Release();
|
|
}
|
|
}
|
|
}
|
|
else if (pfmtetc->dwAspect == DVASPECT_COPY)
|
|
{
|
|
TCHAR szFailureMsg[MAX_PATH], szFailureMsgTitle[40];
|
|
LoadString(g_hinst, IDS_SENDMAIL_FAILUREMSG, szFailureMsg, ARRAYSIZE(szFailureMsg));
|
|
LoadString(g_hinst, IDS_SENDMAIL_TITLE, szFailureMsgTitle, ARRAYSIZE(szFailureMsgTitle));
|
|
|
|
int iRet = MessageBox(NULL, szFailureMsg, szFailureMsgTitle, MB_YESNO);
|
|
if (iRet == IDNO)
|
|
hr = S_FALSE; // convert to success to we don't try DVASPECT_LINK
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// generate a set of files from the data object
|
|
|
|
typedef struct
|
|
{
|
|
INT format;
|
|
FORMATETC fmte;
|
|
} DATA_HANDLER;
|
|
|
|
#define GET_FILECONTENT 0
|
|
#define GET_HDROP 1
|
|
#define GET_URL 2
|
|
|
|
// Note: If this function returns E_CANCELLED that tells the caller that the user requested us to cancel the
|
|
// sendmail operation.
|
|
HRESULT CSendTo::CreateSendToFilesFromDataObj(IDataObject *pdtobj, DWORD grfKeyState, MRPARAM *pmp)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwAspectPrefered;
|
|
IEnumFORMATETC *penum;
|
|
|
|
if (g_cfShellURL == 0)
|
|
{
|
|
g_cfShellURL = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLURL); // URL is always ANSI
|
|
g_cfFileContents = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILECONTENTS);
|
|
g_cfFileDescA = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
|
|
g_cfFileDescW = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
|
|
}
|
|
|
|
if (IS_FORCE_COPY(grfKeyState))
|
|
dwAspectPrefered = DVASPECT_COPY;
|
|
else if (IS_FORCE_LINK(grfKeyState))
|
|
dwAspectPrefered = DVASPECT_LINK;
|
|
else
|
|
dwAspectPrefered = DVASPECT_CONTENT;
|
|
|
|
hr = pdtobj->EnumFormatEtc(DATADIR_GET, &penum);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DATA_HANDLER rg_data_handlers[] = {
|
|
GET_FILECONTENT, {g_cfFileDescW, NULL, dwAspectPrefered, -1, TYMED_HGLOBAL},
|
|
GET_FILECONTENT, {g_cfFileDescW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
|
|
GET_FILECONTENT, {g_cfFileDescA, NULL, dwAspectPrefered, -1, TYMED_HGLOBAL},
|
|
GET_FILECONTENT, {g_cfFileDescA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
|
|
GET_HDROP, {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
|
|
GET_URL, {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
|
|
};
|
|
|
|
FORMATETC fmte;
|
|
while (penum->Next(1, &fmte, NULL) == S_OK)
|
|
{
|
|
SHFree(fmte.ptd);
|
|
fmte.ptd = NULL; // so nobody looks at it
|
|
int i;
|
|
for (i = 0; i < ARRAYSIZE(rg_data_handlers); i++)
|
|
{
|
|
if (rg_data_handlers[i].fmte.cfFormat == fmte.cfFormat &&
|
|
rg_data_handlers[i].fmte.dwAspect == fmte.dwAspect)
|
|
{
|
|
STGMEDIUM medium;
|
|
if (SUCCEEDED(pdtobj->GetData(&rg_data_handlers[i].fmte, &medium)))
|
|
{
|
|
switch ( rg_data_handlers[i].format )
|
|
{
|
|
case GET_FILECONTENT:
|
|
hr = _GetFileContentsFromData(pdtobj, &fmte, &medium, grfKeyState, pmp);
|
|
break;
|
|
|
|
case GET_HDROP:
|
|
hr = _GetHDROPFromData(pdtobj, &fmte, &medium, grfKeyState, pmp);
|
|
break;
|
|
|
|
case GET_URL:
|
|
hr = _GetURLFromData(pdtobj, &fmte, &medium, grfKeyState, pmp);
|
|
break;
|
|
}
|
|
|
|
ReleaseStgMedium(&medium);
|
|
|
|
if (SUCCEEDED(hr))
|
|
goto Done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Done:
|
|
penum->Release();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = FilterPMP(pmp);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// allocate and free a file list. pmp->nFiles MUST be initialized before calling this function!
|
|
|
|
BOOL CSendTo::AllocatePMP(MRPARAM *pmp, DWORD cchTitle, DWORD cchFiles)
|
|
{
|
|
// Remember the array sizes for overflow checks, etc.
|
|
pmp->cchFile = cchFiles;
|
|
pmp->cchTitle = cchTitle;
|
|
|
|
// compute size of each file entry and allocate enough for the number of files we have. Also
|
|
// add a TCHAR to the end of the buffer so we can do a double null termination safely while deleting files
|
|
|
|
pmp->cbFileEntry = sizeof(MRFILEENTRY) + ((cchTitle + cchFiles) * sizeof(TCHAR));
|
|
pmp->pFiles = (MRFILEENTRY *)GlobalAlloc(GPTR, (pmp->cbFileEntry * pmp->nFiles) + sizeof(TCHAR));
|
|
if (!pmp->pFiles)
|
|
return FALSE;
|
|
|
|
CFileEnum MREnum(pmp, NULL);
|
|
MRFILEENTRY *pFile;
|
|
|
|
// Note: The use of the enumerator here is questionable since this is the loop that initializes the
|
|
// data structure. If the implementation changes in the future be sure this assumption still holds.
|
|
|
|
while (pFile = MREnum.Next())
|
|
{
|
|
pFile->pszFileName = (LPTSTR)pFile->chBuf;
|
|
pFile->pszTitle = pFile->pszFileName + cchFiles;
|
|
|
|
ASSERTMSG(pFile->dwFlags == 0, "Expected zero-inited memory allocation");
|
|
ASSERTMSG(pFile->pszFileName[cchFiles-1] == 0, "Expected zero-inited memory allocation");
|
|
ASSERTMSG(pFile->pszTitle[cchTitle-1] == 0, "Expected zero-inited memory allocation");
|
|
ASSERTMSG(pFile->pStream == NULL, "Expected zero-inited memory allocation");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CSendTo::CleanupPMP(MRPARAM *pmp)
|
|
{
|
|
CFileEnum MREnum(pmp, NULL);
|
|
MRFILEENTRY *pFile;
|
|
|
|
while (pFile = MREnum.Next())
|
|
{
|
|
// delete the file if we are supposed to
|
|
if (pFile->dwFlags & MRFILE_DELETE)
|
|
DeleteFile(pFile->pszFileName);
|
|
|
|
// If we held on to a temporary stream release it so the underlying data will be deleted.
|
|
ATOMICRELEASE(pFile->pStream);
|
|
}
|
|
|
|
if (pmp->pFiles)
|
|
{
|
|
GlobalFree((LPVOID)pmp->pFiles);
|
|
pmp->pFiles = NULL;
|
|
}
|
|
|
|
GlobalFree(pmp);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// allow files to be massaged before sending
|
|
|
|
HRESULT CSendTo::FilterPMP(MRPARAM *pmp)
|
|
{
|
|
// lets handle the initialization of the progress dialog
|
|
IActionProgress *pap;
|
|
HRESULT hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IActionProgress, &pap));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szBuffer[MAX_PATH];
|
|
IActionProgressDialog *papd;
|
|
hr = pap->QueryInterface(IID_PPV_ARG(IActionProgressDialog, &papd));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LoadString(g_hinst, IDS_SENDMAIL_TITLE, szBuffer, ARRAYSIZE(szBuffer));
|
|
hr = papd->Initialize(0x0, szBuffer, NULL);
|
|
papd->Release();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LoadString(g_hinst, IDS_SENDMAIL_RECOMPRESS, szBuffer, ARRAYSIZE(szBuffer));
|
|
pap->UpdateText(SPTEXT_ACTIONDESCRIPTION, szBuffer, FALSE);
|
|
pap->Begin(SPACTION_COPYING, SPBEGINF_NORMAL);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
pap->Release();
|
|
pap = NULL;
|
|
}
|
|
}
|
|
|
|
// walk the files and perform the recompress if we need to.
|
|
|
|
int iResponse = RESPONSE_UNKNOWN;
|
|
|
|
CFileEnum MREnum(pmp, pap);
|
|
MRFILEENTRY *pFile;
|
|
for (hr = S_OK; (pFile = MREnum.Next()) && SUCCEEDED(hr); )
|
|
{
|
|
if (pap)
|
|
pap->UpdateText(SPTEXT_ACTIONDETAIL, pFile->pszFileName, TRUE);
|
|
|
|
// if this is a picture then lets off the option to recompress the image
|
|
|
|
if (PathIsImage(pFile->pszFileName))
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
hr = SHILCreateFromPath(pFile->pszFileName, &pidl, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHCreateShellItem(NULL, NULL, pidl, &_psi);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// if the response is unknown then we need to prompt for which type of optimization
|
|
// needs to be performed.
|
|
|
|
if (iResponse == RESPONSE_UNKNOWN)
|
|
{
|
|
// we need the link control window
|
|
|
|
INITCOMMONCONTROLSEX initComctl32;
|
|
initComctl32.dwSize = sizeof(initComctl32);
|
|
initComctl32.dwICC = (ICC_STANDARD_CLASSES | ICC_LINK_CLASS);
|
|
InitCommonControlsEx(&initComctl32);
|
|
|
|
// we need a parent window
|
|
|
|
HWND hwnd = GetActiveWindow();
|
|
if (pap)
|
|
IUnknown_GetWindow(pap, &hwnd);
|
|
|
|
iResponse = (int)DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_RECOMPRESS),
|
|
hwnd, s_ConfirmDlgProc, (LPARAM)this);
|
|
|
|
}
|
|
|
|
// based on the response we either have cache or the dialog lets perform
|
|
// that operation as needed.
|
|
|
|
if (iResponse == RESPONSE_CANCEL)
|
|
{
|
|
hr = E_CANCELLED;
|
|
}
|
|
else if (iResponse == RESPONSE_RECOMPRESS)
|
|
{
|
|
IStorage *pstg;
|
|
hr = _GetTempStorage(&pstg);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IImageRecompress *pir;
|
|
hr = CoCreateInstance(CLSID_ImageRecompress, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IImageRecompress, &pir));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IStream *pstrm;
|
|
hr = pir->RecompressImage(_psi, _aQuality[_iRecompSetting].cx, _aQuality[_iRecompSetting].cy, _aQuality[_iRecompSetting].iQuality, pstg, &pstrm);
|
|
if (hr == S_OK)
|
|
{
|
|
STATSTG stat;
|
|
hr = pstrm->Stat(&stat, STATFLAG_DEFAULT);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// its OK to delete this file now, b/c we are going to replace it with the recompressed
|
|
// stream we have just generated from the source.
|
|
if (pFile->dwFlags & MRFILE_DELETE)
|
|
DeleteFile(pFile->pszFileName);
|
|
|
|
// get the information on the recompressed object.
|
|
StrCpyNW(pFile->pszFileName, _szTempPath, pmp->cchFile);
|
|
PathAppend(pFile->pszFileName, stat.pwcsName);
|
|
_GetFileAndTypeDescFromPath(pFile->pszFileName, pFile->pszTitle, pmp->cchTitle);
|
|
|
|
pFile->dwFlags |= MRFILE_DELETE;
|
|
pstrm->QueryInterface(IID_PPV_ARG(IStream, &pFile->pStream));
|
|
|
|
CoTaskMemFree(stat.pwcsName);
|
|
}
|
|
pstrm->Release();
|
|
}
|
|
pir->Release();
|
|
}
|
|
pstg->Release();
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && pap)
|
|
{
|
|
BOOL fCancelled;
|
|
if (SUCCEEDED(pap->QueryCancel(&fCancelled)) && fCancelled)
|
|
{
|
|
hr = E_CANCELLED;
|
|
}
|
|
}
|
|
|
|
_psi->Release();
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pap)
|
|
{
|
|
pap->End();
|
|
pap->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CSendTo::_GetTempStorage(IStorage **ppStorage)
|
|
{
|
|
*ppStorage = NULL;
|
|
|
|
HRESULT hr;
|
|
if (_pStorageTemp == NULL)
|
|
{
|
|
if (GetTempPath(ARRAYSIZE(_szTempPath), _szTempPath))
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
hr = SHILCreateFromPath(_szTempPath, &pidl, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHBindToObjectEx(NULL, pidl, NULL, IID_PPV_ARG(IStorage, ppStorage));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = (*ppStorage)->QueryInterface(IID_PPV_ARG(IStorage, &_pStorageTemp));
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = _pStorageTemp->QueryInterface(IID_PPV_ARG(IStorage, ppStorage));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CSendTo::_CollapseOptions(HWND hwnd, BOOL fHide)
|
|
{
|
|
_fOptionsHidden = fHide;
|
|
|
|
RECT rc1, rc2;
|
|
GetWindowRect(GetDlgItem(hwnd, IDC_RECOMPORIGINAL), &rc1);
|
|
GetWindowRect(GetDlgItem(hwnd, IDC_RECOMPLARGE), &rc2);
|
|
int cyAdjust = (rc2.top - rc1.top) * (fHide ? -1:1);
|
|
|
|
// show/hide the controls we are not going to use
|
|
|
|
UINT idHide[] = { IDC_RECOMPMAKETHEM, IDC_RECOMPSMALL, IDC_RECOMPMEDIUM, IDC_RECOMPLARGE };
|
|
for (int i = 0; i < ARRAYSIZE(idHide); i++)
|
|
{
|
|
ShowWindow(GetDlgItem(hwnd, idHide[i]), fHide ? SW_HIDE:SW_SHOW);
|
|
}
|
|
|
|
// move the buttons at the bottom of the dialog
|
|
|
|
UINT idMove[] = { IDC_RECOMPSHOWHIDE, IDOK, IDCANCEL };
|
|
for (int i = 0; i < ARRAYSIZE(idMove); i++)
|
|
{
|
|
RECT rcItem;
|
|
GetWindowRect(GetDlgItem(hwnd, idMove[i]), &rcItem);
|
|
MapWindowPoints(NULL, hwnd, (LPPOINT)&rcItem, 2);
|
|
|
|
SetWindowPos(GetDlgItem(hwnd, idMove[i]), NULL,
|
|
rcItem.left, rcItem.top + cyAdjust, 0, 0,
|
|
SWP_NOSIZE|SWP_NOZORDER);
|
|
}
|
|
|
|
// resize the dialog accordingly
|
|
|
|
RECT rcWindow;
|
|
GetWindowRect(hwnd, &rcWindow);
|
|
SetWindowPos(hwnd, NULL,
|
|
0, 0, RECTWIDTH(rcWindow), RECTHEIGHT(rcWindow) + cyAdjust,
|
|
SWP_NOZORDER|SWP_NOMOVE);
|
|
|
|
// update the link control
|
|
|
|
TCHAR szBuffer[MAX_PATH];
|
|
LoadString(g_hinst, fHide ? IDS_SENDMAIL_SHOWMORE:IDS_SENDMAIL_SHOWLESS, szBuffer, ARRAYSIZE(szBuffer));
|
|
SetDlgItemText(hwnd, IDC_RECOMPSHOWHIDE, szBuffer);
|
|
}
|
|
|
|
|
|
// dialog proc for the recompress prompt
|
|
|
|
BOOL_PTR CSendTo::s_ConfirmDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CSendTo *pst = (CSendTo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
if (msg == WM_INITDIALOG)
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, lParam);
|
|
pst = (CSendTo*)lParam;
|
|
}
|
|
return pst->_ConfirmDlgProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
BOOL_PTR CSendTo::_ConfirmDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
HWND hwndThumbnail = GetDlgItem(hwnd, IDC_RECOMPTHUMBNAIL);
|
|
LONG_PTR dwStyle = GetWindowLongPtr(hwndThumbnail, GWL_STYLE);
|
|
|
|
// set the default state of the dialog
|
|
_CollapseOptions(hwnd, TRUE);
|
|
|
|
// set the default state of the buttons
|
|
CheckRadioButton(hwnd, IDC_RECOMPORIGINAL, IDC_RECOMPALL, IDC_RECOMPALL);
|
|
CheckRadioButton(hwnd, IDC_RECOMPSMALL, IDC_RECOMPLARGE, IDC_RECOMPSMALL + _iRecompSetting);
|
|
|
|
// get the thumbnail and show it.
|
|
IExtractImage *pei;
|
|
HRESULT hr = _psi->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IExtractImage, &pei));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
RECT rcThumbnail;
|
|
GetClientRect(GetDlgItem(hwnd, IDC_RECOMPTHUMBNAIL), &rcThumbnail);
|
|
|
|
SIZE sz = {RECTWIDTH(rcThumbnail), RECTHEIGHT(rcThumbnail)};
|
|
WCHAR szImage[MAX_PATH];
|
|
DWORD dwFlags = 0;
|
|
|
|
hr = pei->GetLocation(szImage, ARRAYSIZE(szImage), NULL, &sz, 24, &dwFlags);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HBITMAP hbmp;
|
|
hr = pei->Extract(&hbmp);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SetWindowLongPtr(hwndThumbnail, GWL_STYLE, dwStyle | SS_BITMAP);
|
|
HBITMAP hbmp2 = (HBITMAP)SendMessage(hwndThumbnail, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
|
|
if (hbmp2)
|
|
{
|
|
DeleteObject(hbmp2);
|
|
}
|
|
}
|
|
}
|
|
pei->Release();
|
|
}
|
|
|
|
// if that failed then lets get the icon for the file and place that into the dialog,
|
|
// this is less likely to fail - I hope.
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
IPersistIDList *ppid;
|
|
hr = _psi->QueryInterface(IID_PPV_ARG(IPersistIDList, &ppid));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
hr = ppid->GetIDList(&pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SHFILEINFO sfi = {0};
|
|
if (SHGetFileInfo((LPCWSTR)pidl, -1, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_PIDL|SHGFI_ADDOVERLAYS))
|
|
{
|
|
SetWindowLongPtr(hwndThumbnail, GWL_STYLE, dwStyle | SS_ICON);
|
|
HICON hIcon = (HICON)SendMessage(hwndThumbnail, STM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)sfi.hIcon);
|
|
if (hIcon)
|
|
{
|
|
DeleteObject(hIcon);
|
|
}
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
ppid->Release();
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
// Did they click/keyboard on the alter settings link?
|
|
NMHDR *pnmh = (NMHDR *)lParam;
|
|
if ((wParam == IDC_RECOMPSHOWHIDE) &&
|
|
(pnmh->code == NM_CLICK || pnmh->code == NM_RETURN))
|
|
{
|
|
_CollapseOptions(hwnd, !_fOptionsHidden);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case IDOK:
|
|
{
|
|
// read back the quality index and store
|
|
if (IsDlgButtonChecked(hwnd, IDC_RECOMPSMALL))
|
|
_iRecompSetting = QUALITY_LOW;
|
|
else if (IsDlgButtonChecked(hwnd, IDC_RECOMPMEDIUM))
|
|
_iRecompSetting = QUALITY_MEDIUM;
|
|
else
|
|
_iRecompSetting = QUALITY_HIGH;
|
|
|
|
// dismiss the dialog, returning the radio button state
|
|
EndDialog(hwnd,(IsDlgButtonChecked(hwnd, IDC_RECOMPALL)) ? RESPONSE_RECOMPRESS:RESPONSE_ORIGINAL);
|
|
return FALSE;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, RESPONSE_CANCEL);
|
|
return FALSE;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|