1539 lines
46 KiB
C++
1539 lines
46 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1994
|
|
//
|
|
// File: persist.cxx
|
|
//
|
|
// Contents: Implmentation of Office9 Thicket Save API
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
#include "priv.h"
|
|
|
|
//#include "headers.hxx"
|
|
//#include "formkrnl.hxx"
|
|
#include <platform.h>
|
|
#include <mlang.h>
|
|
#include "resource.h"
|
|
#include "impexp.h"
|
|
#include "reload.h"
|
|
//#include <siterc.h>
|
|
#include "packager.h"
|
|
#include "iehelpid.h"
|
|
#include "thicket.h"
|
|
#include "apithk.h"
|
|
|
|
#include <mluisupp.h>
|
|
#include <mshtmcid.h>
|
|
|
|
#define NUM_OLE_CMDS 1
|
|
|
|
#define SAVEAS_OK 0x00000001
|
|
#define SAVEAS_NEVER_ASK_AGAIN 0x00000002
|
|
|
|
#define CODEPAGE_UNICODE 0x000004B0
|
|
#define CODEPAGE_UTF8 0x0000FDE9
|
|
#define UNICODE_TEXT TEXT("Unicode")
|
|
|
|
#define REGSTR_VAL_SAVEDIRECTORY TEXT("Save Directory")
|
|
#define REGKEY_SAVEAS_WARNING_RESTRICTION TEXT("SOFTWARE\\Microsoft\\Internet Explorer\\Main")
|
|
#define REGVALUE_SAVEAS_WARNING TEXT("NoSaveAsPOSTWarning")
|
|
|
|
#define WM_WORKER_THREAD_COMPLETED WM_USER + 1000
|
|
|
|
#define MAX_ENCODING_DESC_LEN 1024
|
|
|
|
|
|
const static DWORD aSaveAsHelpIDs[] =
|
|
{
|
|
IDC_SAVE_CHARSET, IDH_CHAR_SET_SAVE_AS,
|
|
0, 0
|
|
};
|
|
|
|
INT_PTR CALLBACK SaveAsWarningDlgProc(HWND hDlg, UINT msg, WPARAM wParam,
|
|
LPARAM lParam);
|
|
|
|
HRESULT SaveToThicket( HWND hwnd, LPCTSTR pszFileName, IHTMLDocument2 *pDoc,
|
|
UINT codepageSrc, UINT codepageDst,
|
|
UINT iPackageStyle );
|
|
|
|
HRESULT
|
|
FormsGetFileName(
|
|
HWND hwndOwner,
|
|
LPTSTR pstrFile,
|
|
int cchFile,
|
|
LPARAM lCustData,
|
|
DWORD *pnFilterIndex,
|
|
BOOL bForceHTMLOnly);
|
|
HRESULT
|
|
GetFileNameFromURL( LPWSTR pwszURL, LPTSTR pszFile, DWORD cchFile);
|
|
|
|
void ReportThicketError( HWND hwnd, HRESULT hr );
|
|
|
|
#define DOWNLOAD_PROGRESS 0x9001
|
|
#define DOWNLOAD_COMPLETE 0x9002
|
|
#define THICKET_TIMER 0x9003
|
|
#define THICKET_INTERVAL 1000
|
|
|
|
#define MDLGMSG(psz, x) TraceMsg(0, "shd TR-MODELESS::%s %x", psz, x)
|
|
|
|
static DWORD s_dwInetComVerMS = 0;
|
|
static DWORD s_dwInetComVerLS = 0;
|
|
|
|
struct ThicketCPInfo
|
|
{
|
|
UINT cpSrc;
|
|
UINT cpDst;
|
|
LPWSTR lpwstrDocCharSet;
|
|
};
|
|
|
|
class CThicketUI
|
|
{
|
|
public:
|
|
CThicketUI(void) :
|
|
_hDlg(NULL),
|
|
_hWndProg(NULL),
|
|
_iErrorDL(0),
|
|
_hrDL(E_FAIL),
|
|
#ifndef NO_MARSHALLING
|
|
_pstmDoc(NULL),
|
|
#else
|
|
_pDoc(NULL),
|
|
#endif
|
|
_pszFileName(NULL),
|
|
_dwDLMax(0),
|
|
_codepageSrc(0),
|
|
_codepageDst(0),
|
|
_iPackageStyle(PACKAGE_THICKET),
|
|
_fCancel(FALSE) {};
|
|
|
|
~CThicketUI(void)
|
|
{
|
|
#ifndef NO_MARSHALLING
|
|
SAFERELEASE(_pstmDoc);
|
|
#endif
|
|
SAFELOCALFREE(_pszFileName);
|
|
};
|
|
|
|
// CThicketUI methods
|
|
HRESULT SaveDocument( HWND hWnd, LPCTSTR pszFileName, IHTMLDocument2 *pDoc,
|
|
UINT codepageSrc, UINT codepageDst,
|
|
UINT iPackageStyle );
|
|
|
|
protected:
|
|
static BOOL_PTR ThicketUIDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
static DWORD WINAPI ThicketUIThreadProc( LPVOID );
|
|
|
|
BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
HWND _hDlg;
|
|
HWND _hWndProg;
|
|
int _iErrorDL;
|
|
HRESULT _hrDL;
|
|
#ifndef UNIX
|
|
IStream *_pstmDoc; // marshalled IHTMLDocument2
|
|
#else
|
|
IHTMLDocument2 *_pDoc;
|
|
#endif
|
|
LPTSTR _pszFileName;
|
|
DWORD _dwDLMax;
|
|
UINT _codepageSrc;
|
|
UINT _codepageDst;
|
|
BOOL _fThreadStarted;
|
|
UINT _iPackageStyle;
|
|
BOOL _fCancel;
|
|
};
|
|
|
|
HRESULT
|
|
CThicketUI::SaveDocument( HWND hWnd, LPCTSTR pszFileName, IHTMLDocument2 *pDoc,
|
|
UINT codepageSrc, UINT codepageDst,
|
|
UINT iPackageStyle)
|
|
{
|
|
_pszFileName = StrDup(pszFileName);
|
|
_codepageSrc = codepageSrc;
|
|
_codepageDst = codepageDst;
|
|
_iPackageStyle = iPackageStyle;
|
|
|
|
#ifndef NO_MARSHALLING
|
|
// We don't do anything with pDoc until we're on the worker thread,
|
|
// so marshall it.
|
|
_hrDL = CoMarshalInterThreadInterfaceInStream(IID_IHTMLDocument2, pDoc, &_pstmDoc);
|
|
|
|
if (SUCCEEDED(_hrDL))
|
|
#else
|
|
_pDoc = pDoc;
|
|
#endif
|
|
{
|
|
// Needs to be modal cuz we're going to work with pDoc on the worker thread
|
|
// so we don't want the user to navigate away from it, close the window, etc.
|
|
DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_SAVETHICKET),
|
|
hWnd, CThicketUI::ThicketUIDlgProc, (LPARAM)this);
|
|
|
|
// HWND hwnd = MLCreateDialogParamWrap(MLGetHinst(), MAKEINTRESOURCE(IDD_SAVETHICKET),
|
|
// NULL, CThicketUI::ThicketUIDlgProc, (LPARAM)this);
|
|
// if (!hwnd)
|
|
// _hrDL = E_FAIL;
|
|
}
|
|
|
|
return _hrDL;
|
|
}
|
|
|
|
BOOL_PTR
|
|
CThicketUI::ThicketUIDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
CThicketUI* ptui = NULL;
|
|
|
|
if (msg == WM_INITDIALOG)
|
|
{
|
|
ptui = (CThicketUI*)lParam;
|
|
}
|
|
else
|
|
ptui = (CThicketUI*)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
if (ptui)
|
|
{
|
|
fRet = ptui->DlgProc(hDlg, msg, wParam, lParam);
|
|
|
|
if (msg == WM_DESTROY || msg == WM_WORKER_THREAD_COMPLETED)
|
|
{
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL
|
|
CThicketUI::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
_hDlg = hDlg;
|
|
_hWndProg = GetDlgItem(hDlg, IDC_THICKETPROGRESS);
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
|
|
_hrDL = S_FALSE;
|
|
|
|
#ifndef NO_MARSHALLING
|
|
//_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThicketUIThreadProc, this, 0, &_idThread);
|
|
if (!(_fThreadStarted = SHQueueUserWorkItem(ThicketUIThreadProc,
|
|
this,
|
|
0,
|
|
(DWORD_PTR)NULL,
|
|
(DWORD_PTR *)NULL,
|
|
"shdocvw.dll",
|
|
TPS_LONGEXECTIME)))
|
|
_hrDL = E_FAIL;
|
|
#else
|
|
ThicketUIThreadProc((LPVOID)this);
|
|
#endif
|
|
|
|
if (FAILED(_hrDL))
|
|
EndDialog(hDlg, 0);
|
|
else
|
|
{
|
|
ShowWindow(hDlg, SW_SHOWNORMAL);
|
|
Animate_OpenEx(GetDlgItem(hDlg, IDD_ANIMATE), HINST_THISDLL, IDA_DOWNLOAD);
|
|
ShowWindow(GetDlgItem(hDlg, IDD_DOWNLOADICON), SW_HIDE);
|
|
}
|
|
fRet = FALSE;
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDCANCEL:
|
|
_fCancel = TRUE;
|
|
// and wait for the worker thread to quit, polling at WM_TIMER
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_WORKER_THREAD_COMPLETED:
|
|
_hrDL = (DWORD) wParam;
|
|
EndDialog(hDlg,0);
|
|
break;
|
|
|
|
//case WM_CLOSE:
|
|
// KillTimer( hDlg, THICKET_TIMER );
|
|
// _fCancel = TRUE;
|
|
// while( _hrDL == S_FALSE );
|
|
// break;
|
|
|
|
case WM_DESTROY:
|
|
_fCancel = TRUE;
|
|
while( _hrDL == S_FALSE )
|
|
{
|
|
Sleep(0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fRet = FALSE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
DWORD WINAPI CThicketUI::ThicketUIThreadProc( LPVOID ppv )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CThicketUI* ptui = (CThicketUI *)ppv;
|
|
|
|
ASSERT(ptui);
|
|
|
|
hr = CoInitialize(NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IHTMLDocument2 *pDoc = NULL;
|
|
|
|
#ifndef NO_MARSHALLING
|
|
hr = CoGetInterfaceAndReleaseStream( ptui->_pstmDoc, IID_IHTMLDocument2,(LPVOID*)&pDoc);
|
|
|
|
// CoGetInterfaceAndReleaseStream always releases the stream
|
|
ptui->_pstmDoc = NULL;
|
|
#else
|
|
pDoc = ptui->_pDoc;
|
|
pDoc->AddRef();
|
|
#endif
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CThicketProgress tprog( ptui->_hDlg );
|
|
CDocumentPackager docPkgr(ptui->_iPackageStyle);
|
|
|
|
hr = S_FALSE;
|
|
|
|
hr = docPkgr.PackageDocument( pDoc, ptui->_pszFileName,
|
|
&ptui->_fCancel, &tprog,
|
|
0, 100,
|
|
ptui->_codepageDst );
|
|
|
|
pDoc->Release(); // release marshalled interface
|
|
}
|
|
|
|
CoUninitialize();
|
|
}
|
|
|
|
PostMessage(ptui->_hDlg, WM_WORKER_THREAD_COMPLETED, hr, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
HRESULT
|
|
SaveToThicket( HWND hWnd, LPCTSTR pszFileName, IHTMLDocument2 *pDoc,
|
|
UINT codepageSrc, UINT codepageDst, UINT iPackageStyle )
|
|
{
|
|
HRESULT hr;
|
|
CThicketUI* ptui;
|
|
|
|
#ifdef OLD_THICKET
|
|
LPTSTR lpszURL;
|
|
|
|
lpszURL = bstrDocURL;
|
|
|
|
const DWORD dwMaxPathLen = 24;
|
|
|
|
URL_COMPONENTS urlComp;
|
|
TCHAR rgchUrlPath[MAX_PATH];
|
|
TCHAR rgchCanonicalUrl[MAX_URL_STRING];
|
|
DWORD dwLen;
|
|
|
|
dwLen = ARRAYSIZE(rgchCanonicalUrl);
|
|
|
|
hr = UrlCanonicalize( lpszURL, rgchCanonicalUrl, &dwLen, 0);
|
|
if (FAILED(hr))
|
|
return E_FAIL;
|
|
|
|
ZeroMemory(&urlComp, sizeof(urlComp));
|
|
|
|
urlComp.dwStructSize = sizeof(urlComp);
|
|
urlComp.lpszUrlPath = rgchUrlPath;
|
|
urlComp.dwUrlPathLength = ARRAYSIZE(rgchUrlPath);
|
|
|
|
hr = InternetCrackUrl(rgchCanonicalUrl, lstrlen(rgchCanonicalUrl), ICU_DECODE, &urlComp);
|
|
if (FAILED(hr))
|
|
return E_FAIL;
|
|
|
|
// Since this is not a snap-shot, saving the doc over itself is a no-op.
|
|
// This means we can avoid some nasty issues with the save-over, safe-save,
|
|
// et al, by short circuiting the save here.
|
|
if ( StrCmpI(pszFileName, rgchUrlPath) == 0 )
|
|
{
|
|
if (PathFileExists(pszFileName))
|
|
return S_OK;
|
|
else
|
|
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
#endif //OLD_THICKET
|
|
|
|
ptui = new CThicketUI;
|
|
if (ptui)
|
|
{
|
|
hr = ptui->SaveDocument( hWnd, pszFileName, pDoc, codepageSrc, codepageDst, iPackageStyle );
|
|
delete ptui;
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
|
|
void SaveBrowserFile( HWND hwnd, LPUNKNOWN punk )
|
|
{
|
|
HRESULT hr;
|
|
TCHAR szFileDst[MAX_PATH];
|
|
DWORD iFilter = 1;
|
|
IHTMLDocument2 *pDoc;
|
|
BSTR bstrURL = NULL;
|
|
ThicketCPInfo tcpi;
|
|
BSTR bstrCharSet = NULL;
|
|
BSTR bstrTitle = NULL;
|
|
BSTR bstrMime = NULL;
|
|
IOleCommandTarget *pOleCommandTarget = NULL;
|
|
WCHAR *pwzExt = NULL;
|
|
OLECMD pCmd[NUM_OLE_CMDS];
|
|
ULONG nCmds = NUM_OLE_CMDS;
|
|
BOOL bForceHTMLOnly = FALSE;
|
|
|
|
static const WCHAR *wzImage = L" Image";
|
|
|
|
|
|
hr = punk->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
if (FAILED(pDoc->get_URL( &bstrURL )))
|
|
goto Cleanup;
|
|
|
|
hr = pDoc->get_charset( &bstrCharSet );
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
tcpi.cpSrc = CP_ACP;
|
|
tcpi.lpwstrDocCharSet = bstrCharSet;
|
|
|
|
// If it is an image file, then bring up trident to do the save.
|
|
// APPCOMPAT: This is a crappy way to do this. We are hard-coding the
|
|
// image types, so we know to put up the "Save as image" dialog.
|
|
// We originally tried looking at the MIME type, but Trident returns
|
|
// inconsistent MIME types to us (ex. under some platforms we get
|
|
// "JPG Image" and under others we get "JPG File"!).
|
|
|
|
ASSERT(bstrURL);
|
|
|
|
pwzExt = bstrURL + lstrlenW(bstrURL);
|
|
|
|
while (pwzExt > bstrURL && *pwzExt != L'.')
|
|
{
|
|
pwzExt--;
|
|
}
|
|
|
|
hr = pDoc->QueryInterface(IID_IOleCommandTarget,
|
|
(void **)&pOleCommandTarget);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pwzExt > bstrURL) {
|
|
|
|
// Found a "dot". Now pwzExt points to what we think is the extension
|
|
|
|
if (!StrCmpIW(pwzExt, L".JPG") ||
|
|
!StrCmpIW(pwzExt, L".GIF") ||
|
|
!StrCmpIW(pwzExt, L".BMP") ||
|
|
!StrCmpIW(pwzExt, L".XBM") ||
|
|
!StrCmpIW(pwzExt, L".ART") ||
|
|
!StrCmpIW(pwzExt, L".PNG") ||
|
|
!StrCmpIW(pwzExt, L".WMF") ||
|
|
!StrCmpIW(pwzExt, L".TIFF") ||
|
|
!StrCmpIW(pwzExt, L".JPEG"))
|
|
{
|
|
hr = pOleCommandTarget->Exec(&CGID_MSHTML, IDM_SAVEPICTURE, 0,
|
|
NULL, NULL);
|
|
|
|
// FEATURE: Handle a failed HR here. It is very unlikely that
|
|
// this will fail, yet regular save-as code (that follows)
|
|
// will succeed. We always exit out of here, so we will
|
|
// never get two UI dialogs thrown at the user. We should
|
|
// come up with a good scheme to propagate an error dialog
|
|
// to the user. Possible scenario: low disk space causing
|
|
// a fail-out.
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
// IE5 RAID #54672: Save-as has problems saving pages generated by POSTs
|
|
// This code is to detect if the page was generated by POST data and
|
|
// warn the user that saving may not work.
|
|
|
|
pCmd[0].cmdID = SHDVID_PAGEFROMPOSTDATA;
|
|
hr = pOleCommandTarget->QueryStatus(&CGID_ShellDocView, nCmds, pCmd, NULL);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pCmd[0].cmdf & OLECMDF_LATCHED)
|
|
{
|
|
HKEY hkeySaveAs = 0;
|
|
DWORD dwValue = 0;
|
|
DWORD dwSize = 0;
|
|
INT_PTR iFlags = 0;
|
|
|
|
bForceHTMLOnly = TRUE;
|
|
|
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
REGKEY_SAVEAS_WARNING_RESTRICTION, 0,
|
|
KEY_READ, &hkeySaveAs) == ERROR_SUCCESS)
|
|
{
|
|
dwSize = sizeof(DWORD);
|
|
|
|
if (RegQueryValueEx(hkeySaveAs, REGVALUE_SAVEAS_WARNING, NULL,
|
|
NULL, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS)
|
|
{
|
|
|
|
if (dwValue)
|
|
{
|
|
// restriction set, don't show dialog
|
|
RegCloseKey(hkeySaveAs);
|
|
goto Continue;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hkeySaveAs);
|
|
}
|
|
|
|
iFlags = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(DLG_SAVEAS_WARNING),
|
|
hwnd, SaveAsWarningDlgProc, (LPARAM)0);
|
|
|
|
if (!(iFlags & SAVEAS_OK))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (iFlags & SAVEAS_NEVER_ASK_AGAIN)
|
|
{
|
|
HKEY hkey = 0;
|
|
DWORD dwNeverAsk = 1;
|
|
|
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
REGKEY_SAVEAS_WARNING_RESTRICTION, 0,
|
|
KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
|
|
{
|
|
|
|
RegSetValueEx(hkey, REGVALUE_SAVEAS_WARNING, 0, REG_DWORD,
|
|
(CONST BYTE *)&dwNeverAsk,
|
|
sizeof(dwNeverAsk));
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
}
|
|
|
|
Continue:
|
|
|
|
// Suggest a file name
|
|
|
|
szFileDst[0] = 0;
|
|
|
|
// Our favorite candidate is the title, fall back on the file name.
|
|
hr = pDoc->get_title(&bstrTitle);
|
|
if (SUCCEEDED(hr) && lstrlenW(bstrTitle))
|
|
{
|
|
StrCpyN(szFileDst, bstrTitle, ARRAYSIZE(szFileDst));
|
|
}
|
|
else
|
|
hr = GetFileNameFromURL(bstrURL, szFileDst, ARRAYSIZE(szFileDst));
|
|
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
PathCleanupSpec(NULL, szFileDst);
|
|
|
|
hr = FormsGetFileName(hwnd, szFileDst, ARRAYSIZE(szFileDst),
|
|
(LONG_PTR)&tcpi, &iFilter, bForceHTMLOnly);
|
|
|
|
if (hr==S_OK)
|
|
hr = SaveToThicket( hwnd, szFileDst, pDoc, tcpi.cpSrc, tcpi.cpDst, iFilter);
|
|
|
|
Cleanup:
|
|
|
|
if (FAILED(hr))
|
|
ReportThicketError(hwnd, hr);
|
|
|
|
if (pOleCommandTarget)
|
|
pOleCommandTarget->Release();
|
|
|
|
if (pDoc)
|
|
pDoc->Release();
|
|
|
|
if (bstrURL)
|
|
SysFreeString(bstrURL);
|
|
|
|
if (bstrCharSet)
|
|
SysFreeString(bstrCharSet);
|
|
|
|
if (bstrTitle)
|
|
SysFreeString(bstrTitle);
|
|
|
|
return;
|
|
}
|
|
|
|
void ReportThicketError( HWND hwnd, HRESULT hr )
|
|
{
|
|
LPTSTR lpstrMsg = NULL;
|
|
|
|
switch (hr)
|
|
{
|
|
case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY):
|
|
case E_OUTOFMEMORY:
|
|
lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRMEM);
|
|
break;
|
|
|
|
case E_ACCESSDENIED:
|
|
case STG_E_ACCESSDENIED:
|
|
lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRACC);
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DISK_FULL):
|
|
case STG_E_MEDIUMFULL:
|
|
lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRFULL);
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
|
|
lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRFNF);
|
|
break;
|
|
|
|
case E_ABORT:
|
|
// Ray says we don't want a canceled message box.
|
|
//lpstrMsg = MAKEINTRESOURCE(IDS_THICKETABORT);
|
|
break;
|
|
|
|
case E_FAIL:
|
|
default:
|
|
lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRMISC);
|
|
break;
|
|
}
|
|
|
|
if ( lpstrMsg )
|
|
{
|
|
MLShellMessageBox(
|
|
hwnd,
|
|
lpstrMsg,
|
|
MAKEINTRESOURCE(IDS_THICKETERRTITLE),
|
|
MB_OK | MB_ICONERROR);
|
|
}
|
|
}
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// File: file.cxx
|
|
//
|
|
// Contents: Import/export dialog helpers
|
|
//
|
|
// History: 16-May-95 RobBear Taken from formtool
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
const CHAR c_szNT4ResourceLocale[] = ".DEFAULT\\Control Panel\\International";
|
|
const CHAR c_szWin9xResourceLocale[] = ".Default\\Control Panel\\desktop\\ResourceLocale";
|
|
const CHAR c_szLocale[] = "Locale";
|
|
|
|
LANGID
|
|
MLGetShellLanguage()
|
|
{
|
|
LANGID lidShell = 0;
|
|
|
|
// FEATURE: this fn is copied from shlwapi. there really should be a
|
|
// shlwapi export. if MLGetUILanguage has any merit, then
|
|
// MLGetShellLanguage has merit as well.
|
|
|
|
if (IsOS(OS_WIN2000ORGREATER))
|
|
{
|
|
static LANGID (CALLBACK* pfnGetUserDefaultUILanguage)(void) = NULL;
|
|
|
|
if (pfnGetUserDefaultUILanguage == NULL)
|
|
{
|
|
HMODULE hmod = GetModuleHandle(TEXT("KERNEL32"));
|
|
|
|
if (hmod)
|
|
pfnGetUserDefaultUILanguage = (LANGID (CALLBACK*)(void))GetProcAddress(hmod, "GetUserDefaultUILanguage");
|
|
}
|
|
if (pfnGetUserDefaultUILanguage)
|
|
lidShell = pfnGetUserDefaultUILanguage();
|
|
}
|
|
else
|
|
{
|
|
CHAR szLangID[12];
|
|
DWORD cb, dwRet;
|
|
|
|
cb = sizeof(szLangID) - 2*sizeof(szLangID[0]); // avoid 2 byte buffer overrun
|
|
if (IsOS(OS_NT))
|
|
dwRet = SHGetValueA(HKEY_USERS, c_szNT4ResourceLocale, c_szLocale, NULL, szLangID + 2, &cb);
|
|
else
|
|
dwRet = SHGetValueA(HKEY_USERS, c_szWin9xResourceLocale, NULL, NULL, szLangID + 2, &cb);
|
|
|
|
if (ERROR_SUCCESS == dwRet)
|
|
{
|
|
// IE uses a string rep of the hex value
|
|
szLangID[0] = '0';
|
|
szLangID[1] = 'x';
|
|
StrToIntExA(szLangID, STIF_SUPPORT_HEX, (LPINT)&lidShell);
|
|
}
|
|
}
|
|
|
|
return lidShell;
|
|
}
|
|
|
|
/*
|
|
* Stolen from Trident's src\core\cdutil\file.cxx
|
|
*/
|
|
|
|
// Hook procedure for open file dialog.
|
|
|
|
UINT_PTR APIENTRY SaveOFNHookProc(HWND hdlg,
|
|
UINT uiMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
ULONG i, iCurSel;
|
|
BOOL bFoundEncoding = FALSE;
|
|
WCHAR wzEncoding[MAX_ENCODING_DESC_LEN];
|
|
|
|
switch (uiMsg)
|
|
{
|
|
// Populate the dropdown.
|
|
case WM_INITDIALOG:
|
|
{
|
|
HRESULT hr;
|
|
LPOPENFILENAME pofn = (LPOPENFILENAME)lParam;
|
|
ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
|
|
IMultiLanguage2 *pMultiLanguage = NULL;
|
|
IEnumCodePage *pEnumCodePage = NULL;
|
|
//UINT codepageDefault = ptcpi->cp;
|
|
MIMECSETINFO csetInfo;
|
|
LANGID langid;
|
|
|
|
#ifdef UNIX
|
|
SetWindowLongPtr(hdlg, DWLP_USER, (LONG_PTR)ptcpi);
|
|
#endif /* UNIX */
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_CMultiLanguage,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IMultiLanguage2,
|
|
(void**)&pMultiLanguage);
|
|
if (hr)
|
|
break;
|
|
|
|
hr = pMultiLanguage->GetCharsetInfo(ptcpi->lpwstrDocCharSet,&csetInfo);
|
|
if (hr)
|
|
break;
|
|
|
|
#ifndef UNIX
|
|
// the shell combobox where this stuff shows up
|
|
// doesn't know how to fontlink... so we have
|
|
// to stay in the shell's codepage
|
|
langid = MLGetShellLanguage();
|
|
#else
|
|
langid = GetSystemDefaultLangID();
|
|
#endif /* UNIX */
|
|
if (pMultiLanguage->EnumCodePages( MIMECONTF_SAVABLE_BROWSER | MIMECONTF_VALID,
|
|
langid,
|
|
&pEnumCodePage) == S_OK)
|
|
{
|
|
MIMECPINFO cpInfo;
|
|
ULONG ccpInfo;
|
|
UINT cpDefault;
|
|
|
|
if (pMultiLanguage->GetCodePageInfo(csetInfo.uiInternetEncoding, langid, &cpInfo) == S_OK &&
|
|
!(cpInfo.dwFlags & MIMECONTF_SAVABLE_BROWSER))
|
|
{
|
|
// If the codepage selected is not savable (eg JP_AUTO),
|
|
// use the family codepage.
|
|
cpDefault = cpInfo.uiFamilyCodePage;
|
|
}
|
|
else
|
|
cpDefault = csetInfo.uiInternetEncoding;
|
|
|
|
ptcpi->cpSrc = csetInfo.uiInternetEncoding;
|
|
|
|
if (cpDefault == CODEPAGE_UNICODE &&
|
|
pofn->nFilterIndex == PACKAGE_MHTML) {
|
|
cpDefault = CODEPAGE_UTF8;
|
|
}
|
|
|
|
for (i = 0; pEnumCodePage->Next(1, &cpInfo, &ccpInfo) == S_OK; ++i)
|
|
{
|
|
TCHAR *lpszDesc;
|
|
INT_PTR iIdx;
|
|
|
|
if (cpInfo.uiCodePage == CODEPAGE_UNICODE &&
|
|
pofn->nFilterIndex == PACKAGE_MHTML) {
|
|
i--;
|
|
continue;
|
|
}
|
|
|
|
if (cpDefault == cpInfo.uiCodePage)
|
|
{
|
|
StrCpyNW(wzEncoding, cpInfo.wszDescription,
|
|
lstrlen(cpInfo.wszDescription) + 1);
|
|
bFoundEncoding = TRUE;
|
|
}
|
|
|
|
lpszDesc = cpInfo.wszDescription;
|
|
|
|
iIdx = SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_ADDSTRING, 0,
|
|
(LPARAM)lpszDesc);
|
|
SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_SETITEMDATA, iIdx,
|
|
(LPARAM)cpInfo.uiCodePage);
|
|
}
|
|
|
|
if (bFoundEncoding)
|
|
{
|
|
INT_PTR iIndex = 0;
|
|
|
|
iIndex = SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_FINDSTRINGEXACT, -1,
|
|
(LPARAM)wzEncoding);
|
|
|
|
SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
|
|
(WPARAM)iIndex, 0);
|
|
}
|
|
else
|
|
{
|
|
// No encoding found! Bad error. Recover by selecting
|
|
// the first one.
|
|
|
|
SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
|
|
0, 0);
|
|
}
|
|
|
|
}
|
|
SAFERELEASE(pEnumCodePage);
|
|
SAFERELEASE(pMultiLanguage);
|
|
break;
|
|
}
|
|
|
|
#ifdef UNIX
|
|
case WM_COMMAND:
|
|
{
|
|
switch (GET_WM_COMMAND_ID(wParam,lParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
ThicketCPInfo *ptcpi = (ThicketCPInfo *)GetWindowLongPtr(hdlg,DWLP_USER);
|
|
|
|
ptcpi->cpDst = CP_ACP;
|
|
iCurSel = (int) SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
|
|
ptcpi->cpDst =
|
|
(UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
|
|
(WPARAM)iCurSel, (LPARAM)0);
|
|
|
|
// To spare us from re-instantiating MLANG, we'll set the src and dest
|
|
// to CP_ACP if no change is indicated.
|
|
if (ptcpi->cpDst == ptcpi->cpSrc)
|
|
ptcpi->cpDst = ptcpi->cpSrc = CP_ACP;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
#endif /* UNIX */
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
LPOFNOTIFY phdr = (LPOFNOTIFY)lParam;
|
|
|
|
switch (phdr->hdr.code)
|
|
{
|
|
case CDN_FILEOK:
|
|
{
|
|
LPOPENFILENAME pofn = (LPOPENFILENAME)phdr->lpOFN;
|
|
ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
|
|
|
|
iCurSel = (int) SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
|
|
ptcpi->cpDst = //*(UINT *)phdr->lpOFN->lCustData =
|
|
(UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
|
|
(WPARAM)iCurSel, (LPARAM)0);
|
|
}
|
|
|
|
// HACK! This case is implemented to implement a hack for
|
|
// IE5 RAID #60672. MIMEOLE cannot save UNICODE encoding,
|
|
// so when the user selects MHTML saves, we should remove
|
|
// this option. This code should be removed when MIMEOLE
|
|
// fixes their bug (targeted for NT5 RTM). Contact SBailey
|
|
// for the status of this.
|
|
|
|
case CDN_TYPECHANGE:
|
|
{
|
|
LPOPENFILENAME pofn = (LPOPENFILENAME)phdr->lpOFN;
|
|
ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
|
|
UINT uiCPSel, uiCP;
|
|
int iType = pofn->nFilterIndex;
|
|
UINT iCount;
|
|
int iCurSel;
|
|
int iSet = -1;
|
|
|
|
if (iType == PACKAGE_MHTML)
|
|
{
|
|
iCurSel = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
|
|
|
|
uiCPSel = (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
|
|
(WPARAM)iCurSel, (LPARAM)0);
|
|
|
|
// If you selected unicode, make it look like you
|
|
// really selected UTF-8
|
|
|
|
if (uiCPSel == CODEPAGE_UNICODE)
|
|
{
|
|
uiCPSel = CODEPAGE_UTF8;
|
|
}
|
|
|
|
i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_FINDSTRINGEXACT,
|
|
(WPARAM)0,
|
|
(LPARAM)UNICODE_TEXT);
|
|
if (i != CB_ERR)
|
|
{
|
|
SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_DELETESTRING, i, (LPARAM)0);
|
|
}
|
|
|
|
iCount = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_GETCOUNT, 0, 0);
|
|
|
|
// Set selected item back
|
|
|
|
for (i = 0; i < iCount; i++)
|
|
{
|
|
uiCP = (UINT)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
|
|
(WPARAM)i, (LPARAM)0);
|
|
if (uiCP == uiCPSel)
|
|
{
|
|
iSet = i;
|
|
}
|
|
}
|
|
|
|
if (iSet != 0xffffffff)
|
|
{
|
|
SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
|
|
(WPARAM)iSet, (LPARAM)0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Store current selection
|
|
|
|
iCurSel = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
|
|
|
|
uiCPSel = (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
|
|
(WPARAM)iCurSel, (LPARAM)0);
|
|
|
|
// Add unicode back in, if it was removed
|
|
|
|
i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_FINDSTRINGEXACT,
|
|
(WPARAM)0,
|
|
(LPARAM)UNICODE_TEXT);
|
|
|
|
if (i == CB_ERR) {
|
|
// Unicode does not exist, add it back in
|
|
i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_ADDSTRING, 0,
|
|
(LPARAM)UNICODE_TEXT);
|
|
|
|
SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_SETITEMDATA, i,
|
|
(LPARAM)CODEPAGE_UNICODE);
|
|
|
|
|
|
// Make sure the same encoding selected before is
|
|
// still selected.
|
|
iCount = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
|
|
CB_GETCOUNT, 0, 0);
|
|
for (i = 0; i < iCount; i++)
|
|
{
|
|
uiCP = (UINT)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
|
|
(WPARAM)i, (LPARAM)0);
|
|
if (uiCP == uiCPSel)
|
|
{
|
|
iSet = i;
|
|
}
|
|
}
|
|
|
|
if (iCurSel != 0xffffffff)
|
|
{
|
|
SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
|
|
(WPARAM)iSet, (LPARAM)0);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_HELP:
|
|
{
|
|
SHWinHelpOnDemandWrap((HWND)((LPHELPINFO) lParam)->hItemHandle,
|
|
c_szHelpFile,
|
|
HELP_WM_HELP,
|
|
(DWORD_PTR)aSaveAsHelpIDs);
|
|
}
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
{
|
|
SHWinHelpOnDemandWrap((HWND) wParam,
|
|
c_szHelpFile,
|
|
HELP_CONTEXTMENU,
|
|
(DWORD_PTR)aSaveAsHelpIDs);
|
|
}
|
|
break;
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Protect the naive users from themselves, if somebody enters a filename
|
|
// of microsoft.com when saving http://www.microsoft.com we don't want
|
|
// to save a .COM file since this will be interpreted as an executable.
|
|
// bad things will happen
|
|
//
|
|
void CleanUpFilename(LPTSTR pszFile, int iPackageStyle)
|
|
{
|
|
//
|
|
// If we find .COM as the file extension replace it with the file extension
|
|
// of the filetype they are saving the file as
|
|
//
|
|
LPTSTR pszExt = PathFindExtension(pszFile);
|
|
|
|
ASSERT(pszExt);
|
|
if (StrCmpI(pszExt, TEXT(".COM")) == 0) // REVIEW any other file types???
|
|
{
|
|
//
|
|
// Map the package style to a default extension. NOTE this relies on
|
|
// the fact that the filter index maps to the PACKAGE style enum
|
|
// (as does the rest of the thicket code).
|
|
//
|
|
switch (iPackageStyle)
|
|
{
|
|
case PACKAGE_THICKET:
|
|
case PACKAGE_HTML:
|
|
StrCatBuff(pszFile, TEXT(".htm"), MAX_PATH);
|
|
break;
|
|
|
|
case PACKAGE_MHTML:
|
|
StrCatBuff(pszFile, TEXT(".mht"), MAX_PATH);
|
|
break;
|
|
|
|
case PACKAGE_TEXT:
|
|
StrCatBuff(pszFile, TEXT(".txt"), MAX_PATH);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE); // Unknown package type
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FormsGetFileName
|
|
//
|
|
// Synopsis: Gets a file name using either the GetOpenFileName or
|
|
// GetSaveFileName functions.
|
|
//
|
|
// Arguments: [fSaveFile] -- TRUE means use GetSaveFileName
|
|
// FALSE means use GetOpenFileName
|
|
//
|
|
// [idFilterRes] -- The string resource specifying text in the
|
|
// dialog box. It must have the
|
|
// following format:
|
|
// Note: the string has to be _one_ contiguous string.
|
|
// The example is broken up to make it fit
|
|
// on-screen. The verical bar ("pipe") characters
|
|
// are changed to '\0'-s on the fly.
|
|
// This allows the strings to be localized
|
|
// using Espresso.
|
|
//
|
|
// IDS_FILENAMERESOURCE, "Save Dialog As| // the title
|
|
// odg| // default extension
|
|
// Forms3 Dialog (*.odg)| // pairs of filter strings
|
|
// *.odg|
|
|
// Any File (*.*)|
|
|
// *.*|"
|
|
//
|
|
// [pstrFile] -- Buffer for file name.
|
|
// [cchFile] -- Size of buffer in characters.
|
|
//
|
|
// Modifies: [pstrFile]
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
#ifdef _MAC
|
|
extern "C" {
|
|
char * __cdecl _p2cstr(unsigned char *);
|
|
}
|
|
#endif
|
|
|
|
#define CHAR_DOT TEXT('.')
|
|
#define CHAR_DOT_REPLACEMENT TEXT('_')
|
|
|
|
void ReplaceDotsInFileName(LPTSTR pszFileName)
|
|
{
|
|
ASSERT(pszFileName);
|
|
|
|
while (*pszFileName)
|
|
{
|
|
if (*pszFileName == CHAR_DOT)
|
|
{
|
|
*pszFileName = CHAR_DOT_REPLACEMENT;
|
|
}
|
|
pszFileName++;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
FormsGetFileName(
|
|
HWND hwndOwner,
|
|
LPTSTR pstrFile,
|
|
int cchFile,
|
|
LPARAM lCustData,
|
|
DWORD *pnFilterIndex,
|
|
BOOL bForceHTMLOnly)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fOK;
|
|
DWORD dwCommDlgErr;
|
|
LPTSTR pstr;
|
|
OPENFILENAME ofn;
|
|
TCHAR achBuffer[4096]; // Max. size of a string resource
|
|
TCHAR * cp;
|
|
TCHAR * pstrExt;
|
|
int cbBuffer;
|
|
TCHAR achPath[MAX_PATH];
|
|
DWORD dwType = REG_SZ;
|
|
DWORD cbData = MAX_PATH * sizeof(TCHAR);
|
|
int idFilterRes;
|
|
|
|
|
|
// Initialize ofn struct
|
|
memset(&ofn, 0, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwndOwner;
|
|
ofn.Flags = OFN_FILEMUSTEXIST |
|
|
OFN_PATHMUSTEXIST |
|
|
OFN_OVERWRITEPROMPT |
|
|
OFN_HIDEREADONLY |
|
|
#ifndef UNIX
|
|
OFN_NOCHANGEDIR |
|
|
OFN_EXPLORER;
|
|
#else
|
|
OFN_NOCHANGEDIR;
|
|
#endif /* UNIX */
|
|
|
|
ofn.lpfnHook = NULL;
|
|
ofn.nMaxFile = cchFile;
|
|
ofn.lCustData = lCustData;
|
|
ofn.lpstrFile = pstrFile;
|
|
#ifndef NO_IME
|
|
// We add an extra control to the save file dialog.
|
|
|
|
if (lCustData)
|
|
{
|
|
ofn.Flags |= OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
|
|
ofn.lpfnHook = SaveOFNHookProc;
|
|
ofn.lpTemplateName = MAKEINTRESOURCE(IDD_ADDTOSAVE_DIALOG);
|
|
ofn.hInstance = g_hinst;
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Find the extension and set the filter index based on what the
|
|
// extension is. After these loops pstrExt will either be NULL if
|
|
// we didn't find an extension, or will point to the extension starting
|
|
// at the '.'
|
|
|
|
pstrExt = pstrFile;
|
|
while (*pstrExt)
|
|
pstrExt++;
|
|
while ( pstrExt >= pstrFile )
|
|
{
|
|
if( *pstrExt == TEXT('.') )
|
|
break;
|
|
pstrExt--;
|
|
}
|
|
if( pstrExt < pstrFile )
|
|
pstrExt = NULL;
|
|
|
|
// Load the filter spec.
|
|
// FEATURE: Convert table to stringtable for localization
|
|
|
|
if ( SHRestricted2W( REST_NoBrowserSaveWebComplete, NULL, 0 ) )
|
|
idFilterRes = IDS_NOTHICKET_SAVE;
|
|
else if ( s_dwInetComVerMS != 0xFFFFFFFF )
|
|
{
|
|
#ifndef UNIX
|
|
if (s_dwInetComVerMS == 0)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
GetSystemDirectory( szPath, MAX_PATH );
|
|
StrCatBuff( szPath, TEXT("\\INETCOMM.DLL"), MAX_PATH );
|
|
if (FAILED(GetVersionFromFile(szPath, &s_dwInetComVerMS, &s_dwInetComVerLS)))
|
|
s_dwInetComVerMS = 0xFFFFFFFF;
|
|
}
|
|
|
|
if (s_dwInetComVerMS >= 0x50000 && s_dwInetComVerMS != 0xFFFFFFFF)
|
|
idFilterRes = IDS_THICKET_SAVE;
|
|
else
|
|
idFilterRes = IDS_NOMHTML_SAVE;
|
|
#else
|
|
// on UNIX we don't have inetcomm.dll if oe is not installed
|
|
{
|
|
HINSTANCE hInetComm = NULL;
|
|
|
|
if ((hInetComm = LoadLibrary(TEXT("INETCOMM.DLL"))))
|
|
{
|
|
idFilterRes = IDS_THICKET_SAVE;
|
|
FreeLibrary(hInetComm);
|
|
}
|
|
else
|
|
idFilterRes = IDS_NOMHTML_SAVE;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
idFilterRes = IDS_THICKET_SAVE;
|
|
|
|
cbBuffer = MLLoadShellLangString(idFilterRes, achBuffer, ARRAYSIZE(achBuffer));
|
|
ASSERT(cbBuffer > 0);
|
|
if ( ! cbBuffer )
|
|
return E_FAIL;
|
|
|
|
ofn.lpstrTitle = achBuffer;
|
|
|
|
for ( cp = achBuffer; *cp; cp++ )
|
|
{
|
|
if ( *cp == TEXT('|') )
|
|
{
|
|
*cp = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
ASSERT(ofn.lpstrTitle);
|
|
|
|
// Default extension is second string.
|
|
pstr = (LPTSTR) ofn.lpstrTitle;
|
|
while (*pstr++)
|
|
{
|
|
}
|
|
|
|
// N.B. (johnv) Here we assume that filter index one corresponds with the default
|
|
// extension, otherwise we would have to introduce a default filter index into
|
|
// the resource string.
|
|
ofn.nFilterIndex = ((pnFilterIndex)? *pnFilterIndex : 1);
|
|
ofn.lpstrDefExt = pstr;
|
|
|
|
// Filter is third string.
|
|
while(*pstr++)
|
|
{
|
|
}
|
|
|
|
ofn.lpstrFilter = pstr;
|
|
|
|
// Try to match the extension with an entry in the filter list
|
|
// If we match, remove the extension from the incoming path string,
|
|
// set the default extension to the one we found, and appropriately
|
|
// set the filter index.
|
|
|
|
if (pstrExt && !bForceHTMLOnly)
|
|
{
|
|
// N.B. (johnv) We are searching more than we need to.
|
|
|
|
int iIndex = 0;
|
|
const TCHAR* pSearch = ofn.lpstrFilter;
|
|
|
|
while( pSearch )
|
|
{
|
|
if( StrStr( pSearch, pstrExt ) )
|
|
{
|
|
ofn.nFilterIndex = (iIndex / 2) + 1;
|
|
ofn.lpstrDefExt = pstrExt + 1;
|
|
|
|
// Remove the extension from the file name we pass in
|
|
*pstrExt = TEXT('\0');
|
|
|
|
break;
|
|
}
|
|
pSearch += lstrlen(pSearch);
|
|
if( pSearch[1] == 0 )
|
|
break;
|
|
|
|
pSearch++;
|
|
iIndex++;
|
|
}
|
|
}
|
|
|
|
// Suggest HTML Only as default save-type
|
|
|
|
if (bForceHTMLOnly)
|
|
{
|
|
// NOTE: These are hard-coded indices based on shdoclc.rc's
|
|
// IDS_THICKET_SAVE, IDS_NOMHTML_SAVE, IDS_NOTHICKET_SAVE ordering.
|
|
// This saves us the perf hit of doing string comparisons to find
|
|
// HTML only
|
|
|
|
switch (idFilterRes)
|
|
{
|
|
case IDS_NOTHICKET_SAVE:
|
|
ofn.nFilterIndex = 1;
|
|
break;
|
|
|
|
case IDS_NOMHTML_SAVE:
|
|
ofn.nFilterIndex = 2;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(idFilterRes == IDS_THICKET_SAVE);
|
|
ofn.nFilterIndex = 3;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_MAIN, REGSTR_VAL_SAVEDIRECTORY,
|
|
&dwType, achPath, &cbData) != ERROR_SUCCESS ||
|
|
!PathFileExists(achPath))
|
|
{
|
|
SHGetSpecialFolderPath(hwndOwner, achPath, CSIDL_PERSONAL, FALSE);
|
|
}
|
|
|
|
ofn.lpstrInitialDir = achPath;
|
|
|
|
// We don't want to suggest dots in the filename
|
|
ReplaceDotsInFileName(pstrFile);
|
|
|
|
// Now, at last, we're ready to call the save file dialog
|
|
fOK = GetSaveFileName(&ofn);
|
|
|
|
// if working with the abbreviated format list, adjust the index
|
|
if (idFilterRes == IDS_NOTHICKET_SAVE)
|
|
ofn.nFilterIndex += 2;
|
|
else if ( idFilterRes == IDS_NOMHTML_SAVE && ofn.nFilterIndex > 1 )
|
|
ofn.nFilterIndex += 1;
|
|
|
|
if (fOK)
|
|
{
|
|
//
|
|
// Protect the naive users from themselves, if somebody enters a filename
|
|
// of microsoft.com when saving http://www.microsoft.com we don't want
|
|
// to save a .COM file since this will be interpreted as an executable.
|
|
// bad things will happen
|
|
//
|
|
CleanUpFilename(pstrFile, ofn.nFilterIndex);
|
|
|
|
TCHAR *lpszFileName;
|
|
|
|
StrCpyN( achPath, pstrFile, ARRAYSIZE(achPath) );
|
|
|
|
lpszFileName = PathFindFileName( achPath );
|
|
*lpszFileName = 0;
|
|
|
|
SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_MAIN, REGSTR_VAL_SAVEDIRECTORY,
|
|
REG_SZ, achPath, (lstrlen(achPath) * sizeof(TCHAR)));
|
|
|
|
if (pnFilterIndex)
|
|
*pnFilterIndex = ofn.nFilterIndex;
|
|
|
|
if (ofn.nFilterIndex != PACKAGE_MHTML)
|
|
{
|
|
// we can only do this if we're not packaging MHTML
|
|
// because MHTML requires that we tag with the explicit
|
|
// charset, even if it was the default. unlike thicket
|
|
// which inherits the charset from the original document,
|
|
// MHTML must be explicitly tagged or else some system
|
|
// charset tags will sneak in.
|
|
|
|
ThicketCPInfo * ptcpi = (ThicketCPInfo *)lCustData;
|
|
|
|
// To spare us from re-instantiating MLANG, we'll set the src and dest
|
|
// to CP_ACP if no change is indicated.
|
|
if (ptcpi->cpDst == ptcpi->cpSrc)
|
|
ptcpi->cpDst = ptcpi->cpSrc = CP_ACP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifndef WINCE
|
|
dwCommDlgErr = CommDlgExtendedError();
|
|
if (dwCommDlgErr)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwCommDlgErr);
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
#else // WINCE
|
|
hr = E_FAIL;
|
|
#endif // WINCE
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
GetFileNameFromURL( LPWSTR pwszURL, LPTSTR pszFile, DWORD cchFile)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PARSEDURLW puw = {0};
|
|
int cchUrl;
|
|
|
|
cchUrl = SysStringLen(pwszURL);
|
|
|
|
if (cchUrl)
|
|
{
|
|
puw.cbSize = sizeof(PARSEDURLW);
|
|
if (SUCCEEDED(ParseURLW(pwszURL, &puw)))
|
|
{
|
|
OLECHAR *pwchBookMark;
|
|
DWORD dwSize;
|
|
INTERNET_CACHE_ENTRY_INFOW ceiT;
|
|
LPINTERNET_CACHE_ENTRY_INFOW pcei = NULL;
|
|
|
|
// Temporarily, null out the '#' in the url
|
|
pwchBookMark = StrRChrW(puw.pszSuffix, NULL,'#');
|
|
if (pwchBookMark)
|
|
{
|
|
*pwchBookMark = 0;
|
|
}
|
|
|
|
dwSize = sizeof(INTERNET_CACHE_ENTRY_INFO);
|
|
if ( !GetUrlCacheEntryInfoW( pwszURL, &ceiT, &dwSize ) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pcei = (LPINTERNET_CACHE_ENTRY_INFOW)new BYTE[dwSize]) != NULL &&
|
|
GetUrlCacheEntryInfoW( pwszURL, pcei, &dwSize ) )
|
|
{
|
|
StrCpyN(pszFile, PathFindFileName(pcei->lpszLocalFileName), cchFile);
|
|
PathUndecorate(pszFile);
|
|
}
|
|
|
|
if(pcei)
|
|
delete[] pcei;
|
|
|
|
if (pwchBookMark)
|
|
*pwchBookMark = '#';
|
|
|
|
if ( !pszFile[0] )
|
|
{
|
|
OLECHAR *pwchQuery;
|
|
TCHAR szFileT[MAX_PATH];
|
|
|
|
// Temporarily, null out the '?' in the url
|
|
pwchQuery = StrRChrW(puw.pszSuffix, NULL,'?');
|
|
if (pwchQuery)
|
|
{
|
|
*pwchQuery = 0;
|
|
}
|
|
|
|
// IE5 bug 15055 - http://my.excite.com/?uid=B56E4E2D34DF3FED.save_uid
|
|
// fails to save because we were passing "my.excite.com/" as the file
|
|
// name to the file dialog. It doesn't like this.
|
|
if (!pwchQuery || (pwchQuery[-1] != '/' && pwchQuery[-1] != '\\'))
|
|
{
|
|
dwSize = ARRAYSIZE(szFileT);
|
|
|
|
StrCpyN(szFileT, PathFindFileName(puw.pszSuffix), dwSize);
|
|
|
|
if ( !InternetCanonicalizeUrl( szFileT, pszFile, &dwSize, ICU_DECODE | ICU_NO_ENCODE) )
|
|
StrCpyN(pszFile, szFileT, cchFile);
|
|
|
|
pszFile[cchFile - 1] = 0;
|
|
}
|
|
|
|
if (pwchQuery)
|
|
*pwchQuery = '?';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pszFile[0])
|
|
{
|
|
MLLoadString(IDS_UNTITLED, pszFile, cchFile);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
INT_PTR CALLBACK SaveAsWarningDlgProc(HWND hDlg, UINT msg, WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
HRESULT hr = S_OK;
|
|
int iFlags = 0;
|
|
INT_PTR bChecked = 0;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
MessageBeep(MB_ICONEXCLAMATION);
|
|
fRet = TRUE;
|
|
|
|
case WM_COMMAND:
|
|
bChecked = SendDlgItemMessage(hDlg, IDC_SAVEAS_WARNING_CB,
|
|
BM_GETCHECK, 0, 0 );
|
|
iFlags = (bChecked) ? (SAVEAS_NEVER_ASK_AGAIN) : (0);
|
|
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDYES:
|
|
iFlags |= SAVEAS_OK;
|
|
// fall through
|
|
|
|
case IDNO:
|
|
EndDialog(hDlg, iFlags);
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
fRet = FALSE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|