|
|
#include "ctlspriv.h"
#include "help.h" // Help IDs
#include "prshti.h"
#include "dlgcvt.h"
#ifdef WX86
#include <wx86ofl.h>
#endif
#define FLAG_CHANGED 0x0001
#define DEFAULTHEADERHEIGHT 58 // in pixels
#define DEFAULTTEXTDIVIDERGAP 5
#define DEFAULTCTRLWIDTH 501 // page list window in new wizard style
#define DEFAULTCTRLHEIGHT 253 // page list window in new wizard style
#define TITLEX 22
#define TITLEY 10
#define SUBTITLEX 44
#define SUBTITLEY 25
// fixed sizes for the bitmap painted in the header section
#define HEADERBITMAP_Y 5
#define HEADERBITMAP_WIDTH 49
#define HEADERBITMAP_CXBACK (5 + HEADERBITMAP_WIDTH)
#define HEADERBITMAP_HEIGHT 49
#define HEADERSUBTITLE_WRAPOFFSET 10
// Fixed sizes for the watermark bitmap (Wizard97IE5 style)
#define BITMAP_WIDTH 164
#define BITMAP_HEIGHT 312
#define DRAWTEXT_WIZARD97FLAGS (DT_LEFT | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL)
LPVOID WINAPI MapSLFix(HANDLE); VOID WINAPI UnMapSLFixArray(int, HANDLE *);
LRESULT CALLBACK WizardWndProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData);
void ResetWizButtons(LPPROPDATA ppd);
typedef struct // tie
{ TC_ITEMHEADER tci; HWND hwndPage; UINT state; } TC_ITEMEXTRA;
#define CB_ITEMEXTRA (sizeof(TC_ITEMEXTRA) - sizeof(TC_ITEMHEADER))
#define IS_WIZARDPSH(psh) ((psh).dwFlags & (PSH_WIZARD | PSH_WIZARD97 | PSH_WIZARD_LITE))
#define IS_WIZARD(ppd) IS_WIZARDPSH(ppd->psh)
void PageChange(LPPROPDATA ppd, int iAutoAdj); void RemovePropPageData(LPPROPDATA ppd, int nPage); HRESULT GetPageLanguage(PISP pisp, WORD *pwLang); UINT GetDefaultCharsetFromLang(LANGID wLang); LANGID NT5_GetUserDefaultUILanguage(void);
//
// IMPORTANT: The IDHELP ID should always be LAST since we just subtract
// 1 from the number of IDs if no help in the page.
// IDD_APPLYNOW should always be the FIRST ID for standard IDs since it
// is sometimes not displayed and we'll start with index 1.
//
const static int IDs[] = {IDOK, IDCANCEL, IDD_APPLYNOW, IDHELP}; const static int WizIDs[] = {IDD_BACK, IDD_NEXT, IDD_FINISH, IDCANCEL, IDHELP}; const static WORD wIgnoreIDs[] = {IDD_PAGELIST, IDD_DIVIDER, IDD_TOPDIVIDER};
// Prsht_PrepareTemplate action matrix. Please do not change without contacting [msadek]...
const PSPT_ACTION g_PSPT_Action [PSPT_TYPE_MAX][PSPT_OS_MAX][PSPT_OVERRIDE_MAX]={ PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_WIN9XCOMPAT, // PSPT_TYPE_MIRRORED, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_WIN9XCOMPAT, // PSPT_TYPE_MIRRORED, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_FLIP, // PSPT_TYPE_MIRRORED, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_FLIP, // PSPT_TYPE_MIRRORED, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_WINNT5, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_WINNT5, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_OTHER, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_NOMIRRORING, // PSPT_TYPE_MIRRORED, PSPT_OS_OTHER, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_NOACTION, // PSPT_TYPE_ENABLED, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_NOACTION, // PSPT_TYPE_ENABLED, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WINNT5, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WINNT5, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_NOACTION, // PSPT_TYPE_ENABLED, PSPT_OS_OTHER, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_NOMIRRORING, // PSPT_TYPE_ENABLED, PSPT_OS_OTHER, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_NOACTION, // PSPT_TYPE_ENGLISH, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_NOACTION, // PSPT_TYPE_ENGLISH, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_LOADENGLISH, // PSPT_TYPE_ENGLISH, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_FLIP, // PSPT_TYPE_ENGLISH, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_FLIP, // PSPT_TYPE_ENGLISH, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_FLIP, // PSPT_TYPE_ENGLISH, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_LOADENGLISH, // PSPT_TYPE_ENGLISH, PSPT_OS_WINNT5, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_FLIP, // PSPT_TYPE_ENGLISH, PSPT_OS_WINNT5, PSPT_OVERRIDE_USEPAGELANG
PSPT_ACTION_NOACTION, // PSPT_TYPE_ENGLISH, PSPT_OS_OTHER, PSPT_OVERRIDE_NOOVERRIDE
PSPT_ACTION_NOMIRRORING, // PSPT_TYPE_ENGLISH, PSPT_OS_OTHER, PSPT_OVERRIDE_USEPAGELANG
};
// HACK FOR HIJAAK 95!
//
// Instead of creating
// property sheet pages with CreatePropertySheetPage, they merely
// take a pointer to a PROPSHEETPAGE structure and cast it to
// HPROPSHEETPAGE. They got away with this on Win95 because Win95's
// HPROPSHEETPAGE actually was 95% identical to a PROPSHEETPAGE.
// (The missing 5% causes RIPs at property sheet destruction, which
// Hijaak no doubt ignored.)
//
// On NT and IE5, this coincidence is not true.
//
// So validate that what we have is really a property sheet
// structure by checking if it's on the heap at the
// right place. If not, then make one.
//
HPROPSHEETPAGE WINAPI _Hijaak95Hack(LPPROPDATA ppd, HPROPSHEETPAGE hpage) { if (hpage && !LocalSize(PropSheetBase(hpage))) { // SLACKERS! Have to call CreatePropertySheetPage for them
RIPMSG(0, "App passed HPROPSHEETPAGE not created by us; trying to cope"); hpage = _CreatePropertySheetPage((LPCPROPSHEETPAGE)hpage, ppd->fFlags & PD_NEEDSHADOW, ppd->fFlags & PD_WX86); } return hpage; }
void _SetTitle(HWND hDlg, LPPROPDATA ppd) { TCHAR szFormat[50]; TCHAR szTitle[128]; TCHAR szTemp[128 + 50]; LPCTSTR pCaption = ppd->psh.pszCaption;
if (IS_INTRESOURCE(pCaption)) { LoadString(ppd->psh.hInstance, (UINT)LOWORD(pCaption), szTitle, ARRAYSIZE(szTitle)); pCaption = (LPCTSTR)szTitle; }
if (ppd->psh.dwFlags & PSH_PROPTITLE) { if (*pCaption == 0) { // Hey, no title, we need a different resource for localization
LocalizedLoadString(IDS_PROPERTIES, szTemp, ARRAYSIZE(szTemp)); pCaption = szTemp; } else { LocalizedLoadString(IDS_PROPERTIESFOR, szFormat, ARRAYSIZE(szFormat)); if ((lstrlen(pCaption) + 1 + lstrlen(szFormat) + 1) < ARRAYSIZE(szTemp)) { StringCchPrintf(szTemp, ARRAYSIZE(szTemp), szFormat, pCaption); pCaption = szTemp; } } }
if(ppd->psh.dwFlags & PSH_RTLREADING) { SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) | WS_EX_RTLREADING); }
SetWindowText(hDlg, pCaption); }
BOOL _SetHeaderFonts(HWND hDlg, LPPROPDATA ppd) { HFONT hFont; LOGFONT LogFont;
GetObject(GetWindowFont(hDlg), sizeof(LogFont), &LogFont);
CCAdjustForBold(&LogFont); if ((hFont = CreateFontIndirect(&LogFont)) == NULL) { ppd->hFontBold = NULL; return FALSE; } ppd->hFontBold = hFont; // Save the font as a window prop so we can delete it later
return TRUE; }
int _WriteHeaderTitle(LPPROPDATA ppd, HDC hdc, LPRECT prc, LPCTSTR pszTitle, BOOL bTitle, DWORD dwDrawFlags) { LPCTSTR pszOut; int cch; int cx, cy; TCHAR szTitle[MAX_PATH*4]; HFONT hFontOld = NULL; HFONT hFont; int yDrawHeight = 0;
if (IS_INTRESOURCE(pszTitle)) { LoadString(GETPPSP(ppd, ppd->nCurItem)->hInstance, (UINT)LOWORD(pszTitle), szTitle, ARRAYSIZE(szTitle)); pszOut = szTitle; } else pszOut = pszTitle;
cch = lstrlen(pszOut);
if (bTitle && ppd->hFontBold) hFont = ppd->hFontBold; else hFont = GetWindowFont(ppd->hDlg);
hFontOld = SelectObject(hdc, hFont);
if (bTitle) { cx = TITLEX; cy = TITLEY; ExtTextOut(hdc, cx, cy, 0, prc, pszOut, cch, NULL); } else { RECT rcWrap; CopyRect(&rcWrap, prc);
rcWrap.left = SUBTITLEX; rcWrap.top = ppd->ySubTitle; yDrawHeight = DrawText(hdc, pszOut, cch, &rcWrap, dwDrawFlags); }
if (hFontOld) SelectObject(hdc, hFontOld);
return yDrawHeight; }
// In Wizard97 only:
// The subtitles user passed in could be larger than the two line spaces we give
// them, especially in localization cases. So here we go through all subtitles and
// compute the max space they need and set the header height so that no text is clipped
int _ComputeHeaderHeight(LPPROPDATA ppd, int dxMax) { int dyHeaderHeight; int dyTextDividerGap; HDC hdc; dyHeaderHeight = DEFAULTHEADERHEIGHT; hdc = GetDC(ppd->hDlg);
// First, let's get the correct text height and spacing, this can be used
// as the title height and the between-lastline-and-divider spacing.
{ HFONT hFont, hFontOld; TEXTMETRIC tm; if (ppd->hFontBold) hFont = ppd->hFontBold; else hFont = GetWindowFont(ppd->hDlg);
hFontOld = SelectObject(hdc, hFont); if (GetTextMetrics(hdc, &tm)) { dyTextDividerGap = tm.tmExternalLeading; ppd->ySubTitle = max ((tm.tmHeight + tm.tmExternalLeading + TITLEY), SUBTITLEY); } else { dyTextDividerGap = DEFAULTTEXTDIVIDERGAP; ppd->ySubTitle = SUBTITLEY; }
if (hFontOld) SelectObject(hdc, hFontOld); }
// Second, get the subtitle text block height
// should make into a function if shared
{ RECT rcWrap; UINT uPages;
//
// WIZARD97IE5 subtracts out the space used by the header bitmap.
// WIZARD97IE4 uses the full width since the header bitmap
// in IE4 is a watermark and occupies no space.
//
if (ppd->psh.dwFlags & PSH_WIZARD97IE4) rcWrap.right = dxMax; else rcWrap.right = dxMax - HEADERBITMAP_CXBACK - HEADERSUBTITLE_WRAPOFFSET; for (uPages = 0; uPages < ppd->psh.nPages; uPages++) { PROPSHEETPAGE *ppsp = GETPPSP(ppd, uPages); if (!(ppsp->dwFlags & PSP_HIDEHEADER) && (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)) { int iSubHeaderHeight = _WriteHeaderTitle(ppd, hdc, &rcWrap, ppsp->pszHeaderSubTitle, FALSE, DT_CALCRECT | DRAWTEXT_WIZARD97FLAGS); if ((iSubHeaderHeight + ppd->ySubTitle) > dyHeaderHeight) dyHeaderHeight = iSubHeaderHeight + ppd->ySubTitle; } } }
// If the header height has been recomputed, set the correct gap between
// the text and the divider.
if (dyHeaderHeight != DEFAULTHEADERHEIGHT) { ASSERT(dyHeaderHeight > DEFAULTHEADERHEIGHT); dyHeaderHeight += dyTextDividerGap; }
ReleaseDC(ppd->hDlg, hdc); return dyHeaderHeight; }
void MoveAllButtons(HWND hDlg, const int *pids, int idLast, int dx, int dy) { do { HWND hCtrl; RECT rcCtrl;
int iCtrl = *pids; hCtrl = GetDlgItem(hDlg, iCtrl); GetWindowRect(hCtrl, &rcCtrl);
//
// If the dialog wizard window is mirrored, then rcl.right
// in terms of screen coord is the near edge (lead). [samera]
//
if (IS_WINDOW_RTL_MIRRORED(hDlg)) rcCtrl.left = rcCtrl.right;
ScreenToClient(hDlg, (LPPOINT)&rcCtrl); SetWindowPos(hCtrl, NULL, rcCtrl.left + dx, rcCtrl.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); } while(*(pids++) != idLast); }
void RemoveButton(HWND hDlg, int idRemove, const int *pids) { int idPrev = 0; HWND hRemove = NULL; HWND hPrev; RECT rcRemove, rcPrev; int iWidth = 0; const int *pidRemove;
// get the previous id
for (pidRemove = pids; *pidRemove != idRemove; pidRemove++) idPrev = *pidRemove;
if (idPrev) { hRemove = GetDlgItem(hDlg, idRemove); hPrev = GetDlgItem(hDlg, idPrev); GetWindowRect(hRemove, &rcRemove); GetWindowRect(hPrev, &rcPrev);
//
// If the dialog window is mirrored, then the prev button
// will be ahead (to the right) of the button-to-be-removed.
// As a result, the subtraction will be definitely negative,
// so let's convert it to be positive. [samera]
//
if (IS_WINDOW_RTL_MIRRORED(hDlg)) iWidth = rcPrev.right - rcRemove.right; else iWidth = rcRemove.right - rcPrev.right; }
MoveAllButtons(hDlg, pids, idRemove, iWidth, 0);
if (hRemove) ShowWindow(hRemove, SW_HIDE);
// Cannot disable the window; see Prsht_ButtonSubclassProc for explanation.
// WRONG - EnableWindow(hRemove, FALSE);
}
typedef struct LOGPALETTE256 { WORD palVersion; WORD palNumEntries; union { PALETTEENTRY rgpal[256]; RGBQUAD rgq[256]; } u; } LOGPALETTE256;
HPALETTE PaletteFromBmp(HBITMAP hbm) { LOGPALETTE256 pal; int i,n; HDC hdc; HPALETTE hpl;
hdc = CreateCompatibleDC(NULL); SelectObject(hdc, hbm); n = GetDIBColorTable(hdc, 0, 256, pal.u.rgq);
if (n) // DIB section with color table
{ // Palettes are such a hassle. GetDIBColorTable returns RGBQUADs, whereas
// LOGPALETTE wants PALETTEENTRYss, and the two are reverse-endian
// of each other.
for (i= 0 ; i < n; i++) { PALETTEENTRY pe; pe.peRed = pal.u.rgq[i].rgbRed; pe.peGreen = pal.u.rgq[i].rgbGreen; pe.peBlue = pal.u.rgq[i].rgbBlue; pe.peFlags = 0; pal.u.rgpal[i] = pe; }
pal.palVersion = 0x0300; pal.palNumEntries = (WORD)n;
hpl = CreatePalette((LPLOGPALETTE)&pal); } else // Not a DIB section or no color table
{ hpl = CreateHalftonePalette(hdc); }
DeleteDC(hdc); return hpl; }
// -------------- stolen from user code -------------------------------------
//
// GetCharDimensions(hDC, psiz)
//
// This function loads the Textmetrics of the font currently selected into
// the given hDC and saves the height and Average char width of the font
// (NOTE: the
// AveCharWidth value returned by the text metrics call is wrong for
// proportional fonts -- so, we compute them).
//
// -------------- stolen from user code --------------------------------------
TCHAR AveCharWidthData[52+1] = TEXT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); void GetCharDimensions(HDC hDC, SIZE *psiz) { TEXTMETRIC tm;
// Store the System Font metrics info.
GetTextMetrics(hDC, &tm);
if (!(tm.tmPitchAndFamily & TMPF_FIXED_PITCH)) // the name is opposite:)
psiz->cx = tm.tmAveCharWidth; else { // Change from tmAveCharWidth. We will calculate a true average as
// opposed to the one returned by tmAveCharWidth. This works better
// when dealing with proportional spaced fonts. -- ROUND UP
if (GetTextExtentPoint32(hDC, AveCharWidthData, 52, psiz) == TRUE) { psiz->cx = ((psiz->cx / 26) + 1) / 2; } else psiz->cx = tm.tmAveCharWidth; }
psiz->cy = tm.tmHeight; }
//
// It is a feature that USER considers keyboard accelerators live even if
// the control is hidden. This lets you put a hidden static in front of
// a custom control to get an accelerator attached to the custom control.
//
// Unfortunately, it means that the &F accelerator for "Finish" activates
// the Finish button even when the Finish button is hidden. The normal
// workaround for this is to disable the control, but that doesn't work
// because Microsoft PhotoDraw runs around and secretly hides and shows
// buttons without going through PSM_SETWIZBUTTONS, so they end up showing
// a disabled window and their wizard stops working.
//
// So instead, we subclass the buttons and customize their WM_GETDLGCODE
// so that when the control is hidden, they disable their accelerators.
//
LRESULT CALLBACK Prsht_ButtonSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT_PTR uID, ULONG_PTR dwRefData) { LRESULT lres;
switch (wm) {
case WM_GETDLGCODE: lres = DefSubclassProc(hwnd, wm, wp, lp); if (!IsWindowVisible(hwnd)) { // To remove yourself from the mnemonic search, you have to
// return DLGC_WANTCHAR if you are give a NULL LPMSG pointer.
// Normally, the dialog manager sends a real LPMSG containing
// the message that just got received, but when it's poking
// around looking for accelerators, it doesn't give you a
// message at all. It is in that case that you want to
// say, "Hey, I will process the (nonexistent) message".
// This tricks USER into thinking you're an edit control, so
// it won't scan your for mnemonics.
if ((LPMSG)lp == NULL) lres |= DLGC_WANTCHARS;
} break;
case WM_NCDESTROY: // Clean up subclass
RemoveWindowSubclass(hwnd, Prsht_ButtonSubclassProc, 0); lres = DefSubclassProc(hwnd, wm, wp, lp); break;
default: lres = DefSubclassProc(hwnd, wm, wp, lp); break; }
return lres; }
void Prsht_SubclassButton(HWND hDlg, UINT idd) { SetWindowSubclass(GetDlgItem(hDlg, idd), Prsht_ButtonSubclassProc, 0, 0); }
BOOL CompareFontFaceW(LPCWSTR lpwz1, LPCWSTR lpwz2, BOOL fBitCmp) { return lstrcmpiW(lpwz1, lpwz2); }
//
// GetPageFontMetrics
//
// synopsis:
//
// Get the real font metrics from PAGEFONTDATA. Used in InitPropSheetDlg() to
// calculate the physical page size based on the font specified in page templates
//
// fML is set if we are in here because of an ML scenario, in which case the
// font names need to be mapped.
//
BOOL GetPageFontMetrics(LPPROPDATA ppd, PPAGEFONTDATA ppfd, BOOL fML) { LOGFONT lf = {0}; HFONT hFont; HRESULT fRc = FALSE; HDC hdc; if (ppfd && (ppfd->PointSize > 0) && ppfd->szFace[0]) {
// font name mapping
// should be done only for the platform less than NT5
// NT5 is supposed to work with native typeface on any system locale.
//
if (!staticIsOS(OS_WIN2000ORGREATER) && fML) { // replace native font face name to single byte name for non-native platform
typedef struct tagFontFace { BOOL fBitCmp; LPCWSTR lpEnglish; LPCWSTR lpNative; } FONTFACE, *LPFONTFACE; const static FONTFACE s_FontTbl[] = { { FALSE, L"MS Gothic", L"MS UI Gothic" }, { TRUE, L"MS Gothic", L"\xff2d\xff33 \xff30\x30b4\x30b7\x30c3\x30af" }, { TRUE, L"GulimChe", L"\xad74\xb9bc" }, { TRUE, L"MS Song", L"\x5b8b\x4f53" }, { TRUE, L"MingLiU", L"\x65b0\x7d30\x660e\x9ad4" } };
int i;
for (i = 0; i < ARRAYSIZE(s_FontTbl); i++) { if (!CompareFontFaceW(ppfd->szFace, s_FontTbl[i].lpNative, s_FontTbl[i].fBitCmp)) { StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), s_FontTbl[i].lpEnglish); break; } }
if (i >= ARRAYSIZE(s_FontTbl)) { StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), ppfd->szFace); } } else { StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), ppfd->szFace); }
// Try to use the cache
if (ppfd->iCharset == ppd->pfdCache.iCharset && ppfd->bItalic == ppd->pfdCache.bItalic && ppfd->PointSize == ppd->pfdCache.PointSize && lstrcmpiW(ppfd->szFace, ppd->pfdCache.szFace) == 0) { fRc = TRUE; } else { if (hdc = GetDC(ppd->hDlg)) { lf.lfHeight = -MulDiv(ppfd->PointSize, GetDeviceCaps(hdc,LOGPIXELSY), 72); lf.lfCharSet = (BYTE)ppfd->iCharset; lf.lfItalic = (BYTE)ppfd->bItalic; lf.lfWeight = FW_NORMAL;
hFont = CreateFontIndirectW(&lf); if (hFont) { HFONT hFontOld = SelectObject(hdc, hFont);
GetCharDimensions(hdc, &ppd->sizCache); if (hFontOld) SelectObject(hdc, hFontOld);
DeleteObject(hFont);
// Save these font metrics into the cache
ppd->pfdCache = *ppfd; fRc = TRUE; } ReleaseDC(ppd->hDlg, hdc);
} } } return fRc; }
//
// The "ideal page size" of a property sheet is the maximum size of all
// pages.
//
// GIPS_SKIPINTERIOR97HEIGHT and GIPS_SKIPEXTERIOR97HEIGHT selective
// exclude Wiz97 pages from the height computation. They are important
// because interior pages are shorter than exterior pages by
// ppd->cyHeaderHeight.
//
#define GIPS_SKIPINTERIOR97HEIGHT 1
#define GIPS_SKIPEXTERIOR97HEIGHT 2
void Prsht_GetIdealPageSize(LPPROPDATA ppd, PSIZE psiz, UINT flags) { UINT uPages;
*psiz = ppd->sizMin;
for (uPages = 0; uPages < ppd->psh.nPages; uPages++) { PISP pisp = GETPISP(ppd, uPages); int cy = pisp->_pfx.siz.cy;
if (ppd->psh.dwFlags & PSH_WIZARD97) { if (pisp->_psp.dwFlags & PSP_HIDEHEADER) { if (flags & GIPS_SKIPEXTERIOR97HEIGHT) goto skip; } else { if (flags & GIPS_SKIPINTERIOR97HEIGHT) goto skip; } }
if (psiz->cy < cy) psiz->cy = cy;
skip:; if (psiz->cx < pisp->_pfx.siz.cx) psiz->cx = pisp->_pfx.siz.cx; }
}
#define IsMSShellDlgMapped(langid) (PRIMARYLANGID(langid) == LANG_JAPANESE)
//
// Given a page, decide what size it wants to be and save it in the
// pisp->_pfx.siz.
//
void Prsht_ComputeIdealPageSize(LPPROPDATA ppd, PISP pisp, PAGEINFOEX *ppi) { BOOL fUsePageFont;
// pressume page and frame dialog are in same character set
LANGID wPageLang = ppd->wFrameLang; int iPageCharset = DEFAULT_CHARSET;
if (SUCCEEDED(GetPageLanguage(pisp, &wPageLang))) { // GetPageLanguage fails if page is marked PSP_DLGINDIRECT;
// we'll try to recover from that later. For now,
// we leave pagelang to DEFAULT_CHARSET and see if we can take
// the charset info from template EX.
//
// if PSH_USEPAGELANG is specified, we can assume that
// page charset == frame charset and no need for ML adjustment
// *except for* the case of NT Japanese version that replaces
// frame's MS Shell Dlg to their native font. We handle this
// exception later where we set up fUsePageFont;
//
if (!(ppd->psh.dwFlags & PSH_USEPAGELANG) && wPageLang != ppd->wFrameLang) { iPageCharset = GetDefaultCharsetFromLang(wPageLang); } else iPageCharset = ppd->iFrameCharset; }
// Use the font in the page if any of these conditions are met:
//
// A) It's a SHELLFONT page. Do this even if the font is not
// "MS Shell Dlg 2". This gives apps a way to specify that
// their custom-font page should be measured against the
// font in the page rather than in the frame font.
//
// B) ML scenario - complicated original comment below...
//
// 1) we've detected lang in the caller's resource and
// it's different from the frame dialog
// 2) the caller's page doesn't have lang info or we've
// failed to get it (iPageCharset == DEFAULT_CHARSET),
// then we find the page is described with DLGTEMPLATEEX
// and has meaningful charset specified (!= defaultcharset)
// *and* the charset is different from frame's
// 3) the exception for NT Japanese platform that maps
// MS Shell Dlg to their native font. For US Apps to
// work on these platforms they typically specify
// PSH_USEPAGELANG to get English buttons on frame
// but they still need to get the frame sized based on
// page font
//
// Otherwise, IE4 compat **requires** that we use the frame font.
// ISVs have hacked around this historical bug by having large
// dialog templates with extra space in them.
//
fUsePageFont = /* --- A) It's a SHELLFONT page --- */ IsPageInfoSHELLFONT(ppi) || /* --- B) ML scenario --- */ ((ppd->psh.dwFlags & PSH_USEPAGELANG) && IsMSShellDlgMapped(NT5_GetUserDefaultUILanguage())) || (ppd->iFrameCharset != iPageCharset && (iPageCharset != DEFAULT_CHARSET || (ppi->pfd.iCharset != DEFAULT_CHARSET && ppi->pfd.iCharset != ppd->iFrameCharset)));
if (fUsePageFont && GetPageFontMetrics(ppd, &ppi->pfd, MLIsMLHInstance(pisp->_psp.hInstance))) { // Compute Real Dialog Unit for the page
pisp->_pfx.siz.cx = MulDiv(ppi->pt.x, ppd->sizCache.cx, 4); pisp->_pfx.siz.cy = MulDiv(ppi->pt.y, ppd->sizCache.cy, 8); } else { RECT rcT; // IE4 compat - Use the frame font
rcT.top = rcT.left = 0; // Win95 will fault if these are uninit
rcT.right = ppi->pt.x; rcT.bottom = ppi->pt.y; MapDialogRect(ppd->hDlg, &rcT); pisp->_pfx.siz.cx = rcT.right; pisp->_pfx.siz.cy = rcT.bottom;
//
// If this is PSP_DLGINDIRECT but the character set and face name
// say this is a "generic" property sheet, then take the frame
// font or the page font, whichever is bigger.
//
// This fixes the Chinese MingLiu font, which is not as tall as
// the English MS Sans Serif font. Without this fix, we would
// use MingLui (the frame font), and then your MS Shell Dlg pages
// would get truncated.
//
// (Truncated property sheets is what you got in NT4, but I guess
// looking pretty is more important than bug-for-bug compatibility.
// Who knows what apps will be broken by this change.)
//
if ((pisp->_psp.dwFlags & PSP_DLGINDIRECT) && ppi->pfd.iCharset == DEFAULT_CHARSET && lstrcmpiW(ppi->pfd.szFace, L"MS Shell Dlg") == 0) { int i; GetPageFontMetrics(ppd, &ppi->pfd, FALSE); i = MulDiv(ppi->pt.x, ppd->sizCache.cx, 4); if (pisp->_pfx.siz.cx < i) pisp->_pfx.siz.cx = i; i = MulDiv(ppi->pt.y, ppd->sizCache.cy, 8); if (pisp->_pfx.siz.cy < i) pisp->_pfx.siz.cy = i;
} } }
void InitPropSheetDlg(HWND hDlg, LPPROPDATA ppd) { PAGEINFOEX pi; int dxDlg, dyDlg, dyGrow, dxGrow; RECT rcMinSize, rcDlg, rcPage, rcOrigTabs; UINT uPages; HIMAGELIST himl = NULL; TC_ITEMEXTRA tie; TCHAR szStartPage[128]; LPCTSTR pStartPage = NULL; UINT nStartPage; BOOL fPrematurePages = FALSE; #ifdef DEBUG
BOOL fStartPageFound = FALSE; #endif
LANGID langidMUI; MONITORINFO mMonitorInfo; HMONITOR hMonitor; BOOL bMirrored = FALSE; // set our instance data pointer
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)ppd);
// Make sure this gets inited early on.
ppd->nCurItem = 0;
// By default we allow the "Apply" button to be enabled
ppd->fAllowApply = TRUE;
if (IS_WIZARD(ppd)) { // Subclass our buttons so their mnemonics won't mess up applications
// that run around hiding and showing the buttons behind our back.
Prsht_SubclassButton(hDlg, IDD_BACK); Prsht_SubclassButton(hDlg, IDD_NEXT); Prsht_SubclassButton(hDlg, IDD_FINISH); } else _SetTitle(hDlg, ppd);
if (ppd->psh.dwFlags & PSH_USEICONID) { ppd->psh.H_hIcon = LoadImage(ppd->psh.hInstance, ppd->psh.H_pszIcon, IMAGE_ICON, g_cxSmIcon, g_cySmIcon, LR_DEFAULTCOLOR); }
if ((ppd->psh.dwFlags & (PSH_USEICONID | PSH_USEHICON)) && ppd->psh.H_hIcon) SendMessage(hDlg, WM_SETICON, FALSE, (LPARAM)(UINT_PTR)ppd->psh.H_hIcon);
ppd->hDlg = hDlg;
// IDD_PAGELIST should definitely exist
ppd->hwndTabs = GetDlgItem(hDlg, IDD_PAGELIST); ASSERT(ppd->hwndTabs); TabCtrl_SetItemExtra(ppd->hwndTabs, CB_ITEMEXTRA);
// nStartPage is either ppd->psh.H_nStartPage or the page pStartPage
nStartPage = ppd->psh.H_nStartPage; if (ppd->psh.dwFlags & PSH_USEPSTARTPAGE) { nStartPage = 0; // Assume we don't find the page
pStartPage = ppd->psh.H_pStartPage;
if (IS_INTRESOURCE(pStartPage)) { szStartPage[0] = TEXT('\0'); LoadString(ppd->psh.hInstance, (UINT)LOWORD(pStartPage), szStartPage, ARRAYSIZE(szStartPage)); pStartPage = szStartPage; } }
tie.hwndPage = NULL; tie.tci.pszText = pi.szCaption; tie.state = 0;
SendMessage(ppd->hwndTabs, WM_SETREDRAW, FALSE, 0L);
// load langid we chose for frame dialog template
ppd->wFrameLang = LANGIDFROMLCID(CCGetProperThreadLocale(NULL)); // it's charset that really matters to font
ppd->iFrameCharset = GetDefaultCharsetFromLang(ppd->wFrameLang); langidMUI = GetMUILanguage();
for (uPages = 0; uPages < ppd->psh.nPages; uPages++) { PISP pisp = GETPISP(ppd, uPages);
if (GetPageInfoEx(ppd, pisp, &pi, langidMUI, GPI_ALL)) { Prsht_ComputeIdealPageSize(ppd, pisp, &pi);
// Add the page to the end of the tab list
tie.tci.iImage = -1; tie.tci.mask = TCIF_TEXT | TCIF_PARAM | TCIF_IMAGE | (pi.bRTL ? TCIF_RTLREADING : 0); if (pi.hIcon) { if (!himl) { UINT flags = ILC_MASK; if(IS_WINDOW_RTL_MIRRORED(ppd->hwndTabs)) { flags |= ILC_MIRROR; } himl = ImageList_Create(g_cxSmIcon, g_cySmIcon, flags, 8, 4); TabCtrl_SetImageList(ppd->hwndTabs, himl); }
tie.tci.iImage = ImageList_AddIcon(himl, pi.hIcon); // QUESTION raymondc - we always destroy even if PSP_USEHICON?
DestroyIcon(pi.hIcon); }
// QUESTION? What if this fails? Do we want to destroy the page?
if (TabCtrl_InsertItem(ppd->hwndTabs, 1000, &tie.tci) >= 0) { // Nothing to do; all the code that was here got moved elsewhere
}
// remember if any page wants premature init
if (pisp->_psp.dwFlags & PSP_PREMATURE) fPrematurePages = TRUE;
// if the user is specifying the startpage via title, check it here
if ((ppd->psh.dwFlags & PSH_USEPSTARTPAGE) && !lstrcmpi(pStartPage, pi.szCaption)) { nStartPage = uPages; #ifdef DEBUG
fStartPageFound = TRUE; #endif
} } else { DebugMsg(DM_ERROR, TEXT("PropertySheet failed to GetPageInfo")); RemovePropPageData(ppd, uPages--); } }
SendMessage(ppd->hwndTabs, WM_SETREDRAW, TRUE, 0L);
if (ppd->psh.pfnCallback) { #ifdef WX86
if (ppd->fFlags & PD_WX86) Wx86Callback(ppd->psh.pfnCallback, hDlg, PSCB_INITIALIZED, 0); else #endif
ppd->psh.pfnCallback(hDlg, PSCB_INITIALIZED, 0); }
//
// Now compute the size of the tab control.
//
// First get the rectangle for the whole dialog
GetWindowRect(hDlg, &rcDlg); // For WIZARD_LITE style wizards, we stretch the tabs page and sunken divider
// to cover the whole wizard (without the border)
if (ppd->psh.dwFlags & PSH_WIZARD_LITE) { // Stretch the divider to the whole width of the wizard
RECT rcDiv, rcDlgClient; HWND hDiv;
// we allow both PSH_WIZARD and PSH_WIZARD_LITE to be set
// it's exactly the same as setting just PSH_WIZARD_LITE
RIPMSG(!(ppd->psh.dwFlags & PSH_WIZARD97), "Cannot combine PSH_WIZARD_LITE with PSH_WIZARD97");
// but some people do it anyway, so turn off
ppd->psh.dwFlags &= ~PSH_WIZARD97;
// NOTE: GetDlgItemRect returns a rectangle relative to hDlg
hDiv = GetDlgItemRect(hDlg, IDD_DIVIDER, &rcDiv); if (hDiv) SetWindowPos(hDiv, NULL, 0, rcDiv.top, RECTWIDTH(rcDlg), RECTHEIGHT(rcDiv), SWP_NOZORDER | SWP_NOACTIVATE);
GetClientRect(hDlg, &rcDlgClient); // Stretch the page list control to cover the whole wizard client area above
// the divider
SetWindowPos(ppd->hwndTabs, NULL, 0, 0, RECTWIDTH(rcDlgClient), rcDiv.top, SWP_NOZORDER | SWP_NOACTIVATE); }
//
// While we're thinking about it, don't let people set both
// WIZARD97IE4 *and* WIZARD97IE5. That's just way too strange.
//
if (ppd->psh.dwFlags & PSH_WIZARD97IE4) ppd->psh.dwFlags &= ~PSH_WIZARD97IE5;
// Get the rectangle of the pagelist control in pixels.
GetClientRect(ppd->hwndTabs, &rcOrigTabs); ppd->sizMin.cx = rcOrigTabs.right; ppd->sizMin.cy = rcOrigTabs.bottom;
// Compute rcPage = Size of page area in pixels
// For now, we only care about interior pages; we'll deal with exterior
// pages later.
rcPage.left = rcPage.top = 0; Prsht_GetIdealPageSize(ppd, (SIZE *)&rcPage.right, GIPS_SKIPEXTERIOR97HEIGHT);
//
// IE4's Wizard97 assumed that all exterior pages were exactly
// DEFAULTHEADERHEIGHT dlu's taller than interior pages. That's
// right, DEFAULTHEADERHEIGHT is a pixel count, but IE4 messed up
// and used it as a dlu count here.
//
if (ppd->psh.dwFlags & PSH_WIZARD97IE4) { SIZE sizT; SetRect(&rcMinSize, 0, 0, 0, DEFAULTHEADERHEIGHT); MapDialogRect(hDlg, &rcMinSize); Prsht_GetIdealPageSize(ppd, &sizT, GIPS_SKIPINTERIOR97HEIGHT); if (rcPage.bottom < sizT.cy - rcMinSize.bottom) rcPage.bottom = sizT.cy - rcMinSize.bottom; }
// Now compute the minimum size for the page region
rcMinSize = rcPage;
//
// If this is a wizard then set the size of the page area to the entire
// size of the control. If it is a normal property sheet then adjust for
// the tabs, resize the control, and then compute the size of the page
// region only.
//
if (IS_WIZARD(ppd)) // initialize
rcPage = rcMinSize; else { int i; RECT rcAdjSize;
// initialize
for (i = 0; i < 2; i++) { rcAdjSize = rcMinSize; TabCtrl_AdjustRect(ppd->hwndTabs, TRUE, &rcAdjSize);
rcAdjSize.right -= rcAdjSize.left; rcAdjSize.bottom -= rcAdjSize.top; rcAdjSize.left = rcAdjSize.top = 0;
if (rcAdjSize.right < rcMinSize.right) rcAdjSize.right = rcMinSize.right; if (rcAdjSize.bottom < rcMinSize.bottom) rcAdjSize.bottom = rcMinSize.bottom;
SetWindowPos(ppd->hwndTabs, NULL, 0,0, rcAdjSize.right, rcAdjSize.bottom, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } rcPage = rcMinSize = rcAdjSize; TabCtrl_AdjustRect(ppd->hwndTabs, FALSE, &rcPage); } //
// rcMinSize now contains the size of the control, including the tabs, and
// rcPage is the rect containing the page portion (without the tabs).
//
// For wizard97:
// Now we have the correct width for our wizard, let's compute the
// header height based on that, shift the tab window and the pages
// window down accordingly.
//
dyGrow = 0; if (ppd->psh.dwFlags & PSH_WIZARD97) { RECT rcTabs; SIZE sizT;
// NOTE: we don't directly use rcPage because the verticle position for
// ppd->hwndTabs is not determined, yet, even though the horizontal is
// already computed. Therefore, we can only use rcPageCopy.right not
// rcPageCopy.bottom in the following code.
RECT rcTemp; CopyRect(&rcTemp, &rcPage); MapWindowPoints(ppd->hwndTabs, hDlg, (LPPOINT)&rcTemp, 2);
GetWindowRect(ppd->hwndTabs, &rcTabs); MapWindowRect(NULL, hDlg, &rcTabs);
// Set the header fonts first because we need to use the bold font
// to compute the title height
_SetHeaderFonts(hDlg, ppd);
// Adjust the header height
ppd->cyHeaderHeight = _ComputeHeaderHeight(ppd, rcTemp.right);
// Since the app can change the subheader text on the fly,
// our computation of the header height might end up wrong later.
// Allow ISVs to precompensate for that by setting their exterior
// pages larger than the interior pages by the amount they want
// to reserve.
// So if the largest external page is larger than the largest internal
// page, then expand to enclose the external pages too.
// IE4 Wizard97 didn't do this and MFC relies on the bug.
if (!(ppd->psh.dwFlags & PSH_WIZARD97IE4)) { // A margin of 7dlu's is placed above the page, and another
// margin of 7 dlu's is placed below.
SetRect(&rcTemp, 0, 0, 0, 7+7); MapDialogRect(hDlg, &rcTemp);
Prsht_GetIdealPageSize(ppd, &sizT, GIPS_SKIPINTERIOR97HEIGHT);
if (ppd->cyHeaderHeight < sizT.cy - RECTHEIGHT(rcPage) - rcTemp.bottom) ppd->cyHeaderHeight = sizT.cy - RECTHEIGHT(rcPage) - rcTemp.bottom; }
// Move the tab window right under the header
dyGrow += ppd->cyHeaderHeight; SetWindowPos(ppd->hwndTabs, NULL, rcTabs.left, rcTabs.top + dyGrow, RECTWIDTH(rcTabs), RECTHEIGHT(rcTabs), SWP_NOZORDER | SWP_NOACTIVATE); }
//
// Resize the dialog to make room for the control's new size. This can
// only grow the size.
//
dxGrow = rcMinSize.right - rcOrigTabs.right; dxDlg = rcDlg.right - rcDlg.left + dxGrow; dyGrow += rcMinSize.bottom - rcOrigTabs.bottom; dyDlg = rcDlg.bottom - rcDlg.top + dyGrow;
//
// Cascade property sheet windows (only for comctl32 and commctrl)
//
//
// HACK: Putting CW_USEDEFAULT in dialog template does not work because
// CreateWindowEx ignores it unless the window has WS_OVERLAPPED, which
// is not appropriate for a property sheet.
//
{ const TCHAR c_szStatic[] = TEXT("Static"); UINT swp = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE; if (!IsWindow(ppd->psh.hwndParent)) { HWND hwndT = CreateWindowEx(0, c_szStatic, NULL, WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, HINST_THISDLL, NULL); if (hwndT) { GetWindowRect(hwndT, &rcDlg); swp = SWP_NOZORDER | SWP_NOACTIVATE; DestroyWindow(hwndT); } } else { GetWindowRect(ppd->psh.hwndParent, &rcDlg); if (IsWindowVisible(ppd->psh.hwndParent)) { bMirrored = IS_WINDOW_RTL_MIRRORED(ppd->psh.hwndParent); rcDlg.top += g_cySmIcon; if(bMirrored) { rcDlg.left = rcDlg.right - g_cxSmIcon - dxDlg; } else { rcDlg.left += g_cxSmIcon; } } swp = SWP_NOZORDER | SWP_NOACTIVATE; } hMonitor = MonitorFromWindow(hDlg, MONITOR_DEFAULTTONEAREST); mMonitorInfo.cbSize = sizeof(MONITORINFO); if (GetMonitorInfo(hMonitor, &mMonitorInfo)) { if (mMonitorInfo.rcMonitor.right < (rcDlg.left + dxDlg)) { // Move the Window left.
rcDlg.left = mMonitorInfo.rcMonitor.right - dxDlg; } if (mMonitorInfo.rcMonitor.left > rcDlg.left) { // Move the Window Right.
rcDlg.left = mMonitorInfo.rcMonitor.left; } if (mMonitorInfo.rcMonitor.bottom < (rcDlg.top + dyDlg)) { // Move the Window Up.
rcDlg.top = mMonitorInfo.rcMonitor.bottom - dyDlg; } if (mMonitorInfo.rcMonitor.top > rcDlg.top) { // Move the Window Down.
rcDlg.top = mMonitorInfo.rcMonitor.top; } } SetWindowPos(hDlg, NULL, rcDlg.left, rcDlg.top, dxDlg, dyDlg, swp); }
// Now we'll figure out where the page needs to start relative
// to the bottom of the tabs.
MapWindowRect(ppd->hwndTabs, hDlg, &rcPage);
ppd->xSubDlg = rcPage.left; ppd->ySubDlg = rcPage.top; ppd->cxSubDlg = rcPage.right - rcPage.left; ppd->cySubDlg = rcPage.bottom - rcPage.top;
//
// move all the buttons down as needed and turn on appropriate buttons
// for a wizard.
//
{ RECT rcCtrl; HWND hCtrl; const int *pids;
if (ppd->psh.dwFlags & PSH_WIZARD97) { hCtrl = GetDlgItemRect(hDlg, IDD_TOPDIVIDER, &rcCtrl); if (hCtrl) SetWindowPos(hCtrl, NULL, rcCtrl.left, ppd->cyHeaderHeight, RECTWIDTH(rcCtrl) + dxGrow, RECTHEIGHT(rcCtrl), SWP_NOZORDER | SWP_NOACTIVATE); }
if (IS_WIZARD(ppd)) { pids = WizIDs;
hCtrl = GetDlgItemRect(hDlg, IDD_DIVIDER, &rcCtrl); if (hCtrl) SetWindowPos(hCtrl, NULL, rcCtrl.left, rcCtrl.top + dyGrow, RECTWIDTH(rcCtrl) + dxGrow, RECTHEIGHT(rcCtrl), SWP_NOZORDER | SWP_NOACTIVATE);
EnableWindow(GetDlgItem(hDlg, IDD_BACK), TRUE); ppd->idDefaultFallback = IDD_NEXT; } else { pids = IDs; ppd->idDefaultFallback = IDOK; }
// first move everything over by the same amount that
// the dialog grew by.
// If we flipped the buttons, it should be aligned to the left
// No move needed
MoveAllButtons(hDlg, pids, IDHELP, ppd->fFlipped ? 0 : dxGrow, dyGrow);
// If there's no help, then remove the help button.
if (!(ppd->psh.dwFlags & PSH_HASHELP)) { RemoveButton(hDlg, IDHELP, pids); }
// If we are not a wizard, and we should NOT show apply now
if ((ppd->psh.dwFlags & PSH_NOAPPLYNOW) && !IS_WIZARD(ppd)) { RemoveButton(hDlg, IDD_APPLYNOW, pids); }
if (IS_WIZARD(ppd) && (!(ppd->psh.dwFlags & PSH_WIZARDHASFINISH))) { DWORD dwStyle=0;
RemoveButton(hDlg, IDD_FINISH, pids);
// if there's no finish button showing, we need to place it where
// the next button is
GetWindowRect(GetDlgItem(hDlg, IDD_NEXT), &rcCtrl); MapWindowPoints(HWND_DESKTOP, hDlg, (LPPOINT)&rcCtrl, 2); SetWindowPos(GetDlgItem(hDlg, IDD_FINISH), NULL, rcCtrl.left, rcCtrl.top, RECTWIDTH(rcCtrl), RECTHEIGHT(rcCtrl), SWP_NOZORDER | SWP_NOACTIVATE); }
}
// (dli) compute the Pattern Brush for the watermark
// Note: This is done here because we need to know the size of the big dialog in
// case the user wants to stretch the bitmap
if (ppd->psh.dwFlags & PSH_WIZARD97) { int cx, cy; ASSERT(ppd->hbmHeader == NULL); ASSERT(ppd->hbmWatermark == NULL);
//
// WIZARD97IE4 disabled the watermark and header bitmap
// if high contrast was turned on.
//
if (ppd->psh.dwFlags & PSH_WIZARD97IE4) { HIGHCONTRAST hc = {sizeof(hc)}; if (SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0) && (hc.dwFlags & HCF_HIGHCONTRASTON)) { ppd->psh.dwFlags &= ~(PSH_WATERMARK | PSH_USEHBMWATERMARK | PSH_USEHPLWATERMARK | PSH_HEADER | PSH_USEHBMHEADER); } }
if ((ppd->psh.dwFlags & PSH_WATERMARK) && ppd->psh.H_hbmWatermark) { // Compute dimensions of final bitmap, which may be slightly
// goofy due to stretching
cx = cy = 0; // Assume no stretching
if (ppd->psh.dwFlags & PSH_STRETCHWATERMARK) { RECT rc; if (ppd->psh.dwFlags & PSH_WIZARD97IE4) { // The WIZARD97IE4 watermark covers the entire dialog
if (GetDlgItemRect(hDlg, IDD_DIVIDER, &rc)) { cx = dxDlg; cy = rc.top; } } else { // The WIZARD97IE5 watermark does not stretch
// (Too many people passed this flag when converting
// from WIZARD97IE4 to WIZARD97IE5 and relied on
// the nonstretchability.)
} }
if (ppd->psh.dwFlags & PSH_USEHBMWATERMARK) { // LR_COPYRETURNORG means "If no stretching was needed,
// then just return the original bitmap unaltered."
// Note that we need special cleanup if a stretch occurred.
ppd->hbmWatermark = (HBITMAP)CopyImage(ppd->psh.H_hbmWatermark, IMAGE_BITMAP, cx, cy, LR_COPYRETURNORG); } else { ppd->hbmWatermark = (HBITMAP)LoadImage(ppd->psh.hInstance, ppd->psh.H_pszbmWatermark, IMAGE_BITMAP, cx, cy, LR_CREATEDIBSECTION); }
if (ppd->hbmWatermark) { // If app provides custom palette, then use it,
// else create one based on the bmp. (And if the bmp
// doesn't have a palette, PaletteFromBmp will use the
// halftone palette.)
if (ppd->psh.dwFlags & PSH_USEHPLWATERMARK) ppd->hplWatermark = ppd->psh.hplWatermark; else ppd->hplWatermark = PaletteFromBmp(ppd->hbmWatermark);
// And WIZARD97IE4 needs to turn it into a bitmap brush.
if (ppd->psh.dwFlags & PSH_WIZARD97IE4) ppd->hbrWatermark = CreatePatternBrush(ppd->hbmWatermark);
}
}
if ((ppd->psh.dwFlags & PSH_HEADER) && ppd->psh.H_hbmHeader) { cx = cy = 0; // Assume no stretching
if (ppd->psh.dwFlags & PSH_STRETCHWATERMARK) { if (ppd->psh.dwFlags & PSH_WIZARD97IE4) { // The WIZARD97IE4 header covers the entire header
cx = dxDlg; cy = ppd->cyHeaderHeight; } else { // The WIZARD97IE5 header does not stretch
// (Too many people passed this flag when converting
// from WIZARD97IE4 to WIZARD97IE5 and relied on
// the nonstretchability.)
} }
if (ppd->psh.dwFlags & PSH_USEHBMHEADER) { // LR_COPYRETURNORG means "If no stretching was needed,
// then just return the original bitmap unaltered."
// Note that we need special cleanup if a stretch occurred.
ppd->hbmHeader = (HBITMAP)CopyImage(ppd->psh.H_hbmHeader, IMAGE_BITMAP, cx, cy, LR_COPYRETURNORG); } else { ppd->hbmHeader = (HBITMAP)LoadImage(ppd->psh.hInstance, ppd->psh.H_pszbmHeader, IMAGE_BITMAP, cx, cy, LR_CREATEDIBSECTION); }
// And WIZARD97IE4 needs to turn it into a bitmap brush.
if (ppd->hbmHeader && (ppd->psh.dwFlags & PSH_WIZARD97IE4)) ppd->hbrHeader = CreatePatternBrush(ppd->hbmHeader);
} else { // In case the user does not specify a header bitmap
// use the top portion of the watermark
ppd->hbmHeader = ppd->hbmWatermark; ppd->hbrHeader = ppd->hbrWatermark; }
}
// force the dialog to reposition itself based on its new size
SendMessage(hDlg, DM_REPOSITION, 0, 0L);
// do this here instead of using DS_SETFOREGROUND so we don't hose
// pages that do things that want to set the foreground window
SetForegroundWindow(hDlg);
// We set this to 1 if the user saves any changes.
// do this before initting or switching to any pages
ppd->nReturn = 0;
// AppHack - Some people forgot to initialize nStartPage, and they were
// lucky that the garbage value on the stack was zero. Lucky no longer.
if (nStartPage >= ppd->psh.nPages) { RIPMSG(0, "App forgot to initialize PROPSHEETHEADER.nStartPage field, assuming zero"); nStartPage = 0; }
// Now attempt to select the starting page.
TabCtrl_SetCurSel(ppd->hwndTabs, nStartPage); PageChange(ppd, 1);
// Now init any other pages that require it
if (fPrematurePages) { int nPage;
tie.tci.mask = TCIF_PARAM; for (nPage = 0; nPage < (int)ppd->psh.nPages; nPage++) { PISP pisp = GETPISP(ppd, nPage);
if (!(pisp->_psp.dwFlags & PSP_PREMATURE)) continue;
TabCtrl_GetItem(ppd->hwndTabs, nPage, &tie.tci);
if (tie.hwndPage) continue;
if ((tie.hwndPage = _CreatePage(ppd, pisp, hDlg, langidMUI)) == NULL) { RemovePropPageData(ppd, nPage--); continue; }
TabCtrl_SetItem(ppd->hwndTabs, nPage, &tie.tci); } } }
HWND _Ppd_GetPage(LPPROPDATA ppd, int nItem) { if (ppd->hwndTabs) { TC_ITEMEXTRA tie; tie.tci.mask = TCIF_PARAM; TabCtrl_GetItem(ppd->hwndTabs, nItem, &tie.tci); return tie.hwndPage; } return NULL; }
BOOL _Ppd_IsPageHidden(LPPROPDATA ppd, int nItem) { if (ppd->hwndTabs) { TCITEM tci; tci.mask = TCIF_STATE; tci.dwStateMask = TCIS_HIDDEN; if (TabCtrl_GetItem(ppd->hwndTabs, nItem, &tci)) return tci.dwState; } return FALSE; }
LRESULT _Ppd_SendNotify(LPPROPDATA ppd, int nItem, int code, LPARAM lParam) { PSHNOTIFY pshn;
pshn.lParam = lParam; return SendNotifyEx(_Ppd_GetPage(ppd,nItem), ppd->hDlg, code, (LPNMHDR)&pshn, FALSE); }
//
// dwFind = 0 means just move to the current item + iAutoAdjust
// dwFind != 0 means it's a dialog resource identifier we should look for
//
int FindPageIndex(LPPROPDATA ppd, int nCurItem, ULONG_PTR dwFind, LONG_PTR iAutoAdj) { LRESULT nActivate;
if (dwFind == 0) { nActivate = nCurItem + iAutoAdj; if (((UINT)nActivate) <= ppd->psh.nPages) { return((int)nActivate); } } else { for (nActivate = 0; (UINT)nActivate < ppd->psh.nPages; nActivate++) { if ((DWORD_PTR)GETPPSP(ppd, nActivate)->P_pszTemplate == dwFind) { return((int)nActivate); } } } return(-1); }
//
// If hpage != NULL, then return the index of the page which matches it,
// or -1 on failure.
//
int FindPageIndexByHpage(LPPROPDATA ppd, HPROPSHEETPAGE hpage) { int i;
//
// Notice that we explicitly do not do a InternalizeHPROPSHEETPAGE,
// because the app might be passing us garbage. We just want to
// say "Nope, can't find garbage here, sorry."
//
for (i = ppd->psh.nPages - 1; i >= 0; i--) { if (hpage == GETHPAGE(ppd, i)) break; } return i; }
// This WM_NEXTDLGCTL stuff works, except for ACT!4.0 which faults randomly
// I don't know why. The USER people said that removing a
// SetFocus(NULL) call from SetDlgFocus works, but I tried that
// and the app merely faulted in a different place. so I'm going
// back to the old IE4 way, which means that there are scenarios
// where the DEFID can get out of sync with reality.
#undef WM_NEXTDLGCTL_WORKS
#ifdef WM_NEXTDLGCTL_WORKS
//
// Helper function that manages dialog box focus in a manner that keeps
// USER in the loop, so we don't get "two buttons both with the bold
// defpushbutton border" problems.
//
// We have to use WM_NEXTDLGCTL to fix defid problems, such as this one:
//
// Right-click My Computer, Properties.
// Go to Advanced tab. Click Environment Variables.
// Click New. Type a name for a new dummy environment variable.
// Click OK.
//
// At this point (with the old code), the "New" button is a DEFPUSHBUTTON,
// but the DEFID is IDOK. The USER folks said I should use WM_NEXTDLGCTL
// to avoid this problem. But using WM_NEXTDLGCTL introduces its own big
// hairy mess of problems. All the code in this function aside from the
// SendMessage(WM_NEXTDLGCTL) are to work around "quirks" in WM_NEXTDLGCTL
// or workarounds for app bugs.
//
// THIS CODE IS SUBTLE AND QUICK TO ANGER!
//
void SetDlgFocus(LPPROPDATA ppd, HWND hwndFocus) { //
// HACK! It's possible that by the time we get around to changing
// the dialog focus, the dialog box doesn't have focus any more!
// This happens because PSM_SETWIZBUTTONS is a posted message, so
// it can arrive *after* focus has moved elsewhere (e.g., to a
// MessageBox).
//
// There is no way to update the dialog box focus without
// letting it change the real focus (another "quirk" of
// WM_NEXTDLGCTL), so instead we remember who used to have the
// focus, let the dialog box do its focus goo, and then restore
// the focus as necessary.
//
HWND hwndFocusPrev = GetFocus();
// If focus belonged to a window within our property sheet, then
// let the dialog box code push the focus around. Otherwise,
// focus belonged to somebody outside our property sheet, so
// remember to restore it after we're done.
if (hwndFocusPrev && IsChild(ppd->hDlg, hwndFocusPrev)) hwndFocusPrev = NULL;
// USER forgot to revalidate hwndOldFocus at this point, so we have
// to exit USER (by returning to comctl32) then re-enter USER
// (in the SendMessage below) so parameter validation will happen
// again. Sigh.
//
// Bug in Win9x and NT: WM_NEXTDLGCTL will crash if the previous
// focus window destroys itself in response to WM_KILLFOCUS.
// (WebTurbo by NetMetrics does this.) There's a missed
// revalidation so USER ends up using a window handle after
// it has been destroyed. Oops.
//
// (The NT folks consider this "Won't fix, because the system stays
// up; just the app crashes". The 9x folks will try to get the fix
// into Win98 OSR.)
//
//
// Do a manual SetFocus here to make the old focus (if any)
// do all its WM_KILLFOCUS stuff, and possibly destroy itself (grrr).
//
// We have to SetFocus to NULL because some apps (e.g.,
// Visual C 6.0 setup) do funky things on SetFocus, and our early
// SetFocus interferes with the EM_SETSEL that WM_NEXTDLGCTL will
// do later.
//
// APP HACK 2: But not if the target focus is the same as the
// curreng focus, because ACT!4.0 crashes if it receives a
// WM_KILLFOCUS when it is not expecting one.
if (hwndFocus != GetFocus()) SetFocus(NULL);
//
// Note that by manually shoving the focus around, we
// have gotten focus and DEFPUSHBUTTON and DEFID all out
// of sync, which is exactly the problem we're trying to
// avoid! Fortunately, USER also contains special
// recovery code to handle the case where somebody "mistakenly" called
// SetFocus() to change the focus. (I put "mistakenly" in quotes because
// in this case, we did it on purpose.)
//
SendMessage(ppd->hDlg, WM_NEXTDLGCTL, (WPARAM)hwndFocus, MAKELPARAM(TRUE, 0));
//
// If WM_NEXTDLGCTL damaged the focus, fix it.
//
if (hwndFocusPrev) SetFocus(hwndFocusPrev); } #endif
void SetNewDefID(LPPROPDATA ppd) { HWND hDlg = ppd->hDlg; HWND hwndFocus; hwndFocus = GetNextDlgTabItem(ppd->hwndCurPage, NULL, FALSE); ASSERT(hwndFocus); if (hwndFocus) { #ifndef WM_NEXTDLGCTL_WORKS
int id; if (((DWORD)SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0L)) & DLGC_HASSETSEL) { // select the text
Edit_SetSel(hwndFocus, 0, -1); }
id = GetDlgCtrlID(hwndFocus); #endif
//
// See if the handle give to us by GetNextDlgTabItem was any good.
// (For compatibility reasons, if the dialog contains no tabstops,
// it returns the first item.)
//
if ((GetWindowLong(hwndFocus, GWL_STYLE) & (WS_VISIBLE | WS_DISABLED | WS_TABSTOP)) == (WS_VISIBLE | WS_TABSTOP)) { //
// Give the page a chance to change the default focus.
//
HWND hwndT = (HWND)_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_QUERYINITIALFOCUS, (LPARAM)hwndFocus);
// The window had better be valid and a child of the page.
if (hwndT && IsWindow(hwndT) && IsChild(ppd->hwndCurPage, hwndT)) { hwndFocus = hwndT; } } else { // in prop sheet mode, focus on tabs,
// in wizard mode, tabs aren't visible, go to idDefFallback
if (IS_WIZARD(ppd)) hwndFocus = GetDlgItem(hDlg, ppd->idDefaultFallback); else hwndFocus = ppd->hwndTabs; }
#ifdef WM_NEXTDLGCTL_WORKS
//
// Aw-right. Go for it.
//
SetDlgFocus(ppd, hwndFocus);
//
// Hack for MFC: MFC relies on DM_SETDEFID to know when to
// update its wizard buttons.
//
SendMessage(hDlg, DM_SETDEFID, SendMessage(hDlg, DM_GETDEFID, 0, 0), 0); #else
SetFocus(hwndFocus); ResetWizButtons(ppd); if (SendDlgItemMessage(ppd->hwndCurPage, id, WM_GETDLGCODE, 0, 0L) & DLGC_UNDEFPUSHBUTTON) SendMessage(ppd->hwndCurPage, DM_SETDEFID, id, 0); else { SendMessage(hDlg, DM_SETDEFID, ppd->idDefaultFallback, 0); } #endif
} }
/*
** we are about to change pages. what a nice chance to let the current ** page validate itself before we go away. if the page decides not ** to be de-activated, then this'll cancel the page change. ** ** return TRUE iff this page failed validation */ BOOL PageChanging(LPPROPDATA ppd) { BOOL bRet = FALSE; if (ppd && ppd->hwndCurPage) { bRet = BOOLFROMPTR(_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_KILLACTIVE, 0)); } return bRet; }
void PageChange(LPPROPDATA ppd, int iAutoAdj) { HWND hwndCurPage; HWND hwndCurFocus; int nItem; HWND hDlg, hwndTabs;
TC_ITEMEXTRA tie; UINT FlailCount = 0; LRESULT lres;
if (!ppd) { return; }
hDlg = ppd->hDlg; hwndTabs = ppd->hwndTabs;
// NOTE: the page was already validated (PSN_KILLACTIVE) before
// the actual page change.
hwndCurFocus = GetFocus();
TryAgain: FlailCount++; if (FlailCount > ppd->psh.nPages) { DebugMsg(DM_TRACE, TEXT("PropSheet PageChange attempt to set activation more than 10 times.")); return; }
nItem = TabCtrl_GetCurSel(hwndTabs); if (nItem < 0) { return; }
tie.tci.mask = TCIF_PARAM;
TabCtrl_GetItem(hwndTabs, nItem, &tie.tci); hwndCurPage = tie.hwndPage;
if (!hwndCurPage) { if ((hwndCurPage = _CreatePage(ppd, GETPISP(ppd, nItem), hDlg, GetMUILanguage())) == NULL) { /* Should we put up some sort of error message here?
*/ RemovePropPageData(ppd, nItem); TabCtrl_SetCurSel(hwndTabs, 0); goto TryAgain; }
// tie.tci.mask = TCIF_PARAM;
tie.hwndPage = hwndCurPage; TabCtrl_SetItem(hwndTabs, nItem, &tie.tci);
if (HIDEWIZ97HEADER(ppd, nItem)) // Subclass for back ground watermark painting.
SetWindowSubclass(hwndCurPage, WizardWndProc, 0, (DWORD_PTR)ppd); }
// THI WAS REMOVED as part of the fix for bug 18327. The problem is we need to
// send a SETACTIVE message to a page if it is being activated.
// if (ppd->hwndCurPage == hwndCurPage)
// {
// /* we should be done at this point.
// */
// return;
// }
/* Size the dialog and move it to the top of the list before showing
** it in case there is size specific initializing to be done in the ** GETACTIVE message. */
if (IS_WIZARD(ppd)) { HWND hwndTopDivider= GetDlgItem(hDlg, IDD_TOPDIVIDER);
if (ppd->psh.dwFlags & PSH_WIZARD97) { HWND hwndDivider; RECT rcDlg, rcDivider; GetClientRect(hDlg, &rcDlg);
hwndDivider = GetDlgItemRect(hDlg, IDD_DIVIDER, &rcDivider); if (hwndDivider) SetWindowPos(hwndDivider, NULL, rcDlg.left, rcDivider.top, RECTWIDTH(rcDlg), RECTHEIGHT(rcDivider), SWP_NOZORDER | SWP_NOACTIVATE);
if (GETPPSP(ppd, nItem)->dwFlags & PSP_HIDEHEADER) { // In this case, we give the whole dialog box except for the portion under the
// Bottom divider to the property page
RECT rcTopDivider; ShowWindow(hwndTopDivider, SW_HIDE); ShowWindow(ppd->hwndTabs, SW_HIDE);
hwndTopDivider = GetDlgItemRect(hDlg, IDD_DIVIDER, &rcTopDivider); SetWindowPos(hwndCurPage, HWND_TOP, rcDlg.left, rcDlg.top, RECTWIDTH(rcDlg), rcTopDivider.top - rcDlg.top, 0); } else { ShowWindow(hwndTopDivider, SW_SHOW); ShowWindow(ppd->hwndTabs, SW_SHOW); SetWindowPos(hwndCurPage, HWND_TOP, ppd->xSubDlg, ppd->ySubDlg, ppd->cxSubDlg, ppd->cySubDlg, 0); } } else { ShowWindow(hwndTopDivider, SW_HIDE); SetWindowPos(hwndCurPage, HWND_TOP, ppd->xSubDlg, ppd->ySubDlg, ppd->cxSubDlg, ppd->cySubDlg, 0); } } else { RECT rcPage; GetClientRect(ppd->hwndTabs, &rcPage); TabCtrl_AdjustRect(ppd->hwndTabs, FALSE, &rcPage); MapWindowPoints(ppd->hwndTabs, hDlg, (LPPOINT)&rcPage, 2); SetWindowPos(hwndCurPage, HWND_TOP, rcPage.left, rcPage.top, rcPage.right - rcPage.left, rcPage.bottom - rcPage.top, 0); }
/* We want to send the SETACTIVE message before the window is visible
** to minimize on flicker if it needs to update fields. */
//
// If the page returns non-zero from the PSN_SETACTIVE call then
// we will set the activation to the resource ID returned from
// the call and set activation to it. This is mainly used by wizards
// to skip a step.
//
lres = _Ppd_SendNotify(ppd, nItem, PSN_SETACTIVE, 0);
if (lres) { int iPageIndex = FindPageIndex(ppd, nItem, (lres == -1) ? 0 : lres, iAutoAdj);
if ((lres == -1) && (nItem == iPageIndex || iPageIndex >= TabCtrl_GetItemCount(hwndTabs))) { iPageIndex = ppd->nCurItem; }
if (iPageIndex != -1) { TabCtrl_SetCurSel(hwndTabs, iPageIndex); ShowWindow(hwndCurPage, SW_HIDE); goto TryAgain; } }
if (ppd->psh.dwFlags & PSH_HASHELP) { // PSH_HASHELP controls the "Help" button at the bottom
// PSH_NOCONTEXTHELP controls the caption "?" button
Button_Enable(GetDlgItem(hDlg, IDHELP), (BOOL)(GETPPSP(ppd, nItem)->dwFlags & PSP_HASHELP)); }
//
// If this is a wizard then we'll set the dialog's title to the tab
// title.
//
if (IS_WIZARD(ppd)) { TC_ITEMEXTRA tie; TCHAR szTemp[128 + 50]; szTemp[0] = 0;
tie.tci.mask = TCIF_TEXT; tie.tci.pszText = szTemp; tie.tci.cchTextMax = ARRAYSIZE(szTemp); if (TabCtrl_GetItem(hwndTabs, nItem, &tie.tci)) { tie.tci.mask = TCIF_RTLREADING; tie.tci.cchTextMax = 0; // hack, use cchTextMax to query tab item reading order
TabCtrl_GetItem(hwndTabs, nItem, &tie.tci); if( (ppd->psh.dwFlags & PSH_RTLREADING) || (tie.tci.cchTextMax)) SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) | WS_EX_RTLREADING); else SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) & ~WS_EX_RTLREADING);
if (szTemp[0]) SetWindowText(hDlg, szTemp); } }
/* Disable all erasebkgnd messages that come through because windows
** are getting shuffled. Note that we need to call ShowWindow (and ** not show the window in some other way) because DavidDs is counting ** on the correct parameters to the WM_SHOWWINDOW message, and we may ** document how to keep your page from flashing. */ ppd->fFlags |= PD_NOERASE; ShowWindow(hwndCurPage, SW_SHOW); if (ppd->hwndCurPage && (ppd->hwndCurPage != hwndCurPage)) { ShowWindow(ppd->hwndCurPage, SW_HIDE); } ppd->fFlags &= ~PD_NOERASE;
ppd->hwndCurPage = hwndCurPage; ppd->nCurItem = nItem;
/* Newly created dialogs seem to steal the focus, so we steal it back
** to the page list, which must have had the focus to get to this ** point. If this is a wizard then set the focus to the dialog of ** the page. Otherwise, set the focus to the tabs. */ if (hwndCurFocus != hwndTabs) { SetNewDefID(ppd); } else { // The focus may have been stolen from us, bring it back
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hwndTabs, (LPARAM)TRUE); }
// make sure the header is repaint
if ((ppd->psh.dwFlags & PSH_WIZARD97) && (!(GETPPSP(ppd, nItem)->dwFlags & PSP_HIDEHEADER))) InvalidateRect(hDlg, NULL,TRUE); }
#define DECLAREWAITCURSOR HCURSOR hcursor_wait_cursor_save
#define SetWaitCursor() hcursor_wait_cursor_save = SetCursor(LoadCursor(NULL, IDC_WAIT))
#define ResetWaitCursor() SetCursor(hcursor_wait_cursor_save)
//
// HACKHACK (reinerf)
//
// This function sends the PSN_LASTCHANCEAPPLY right after the property sheets have had "ok"
// pressed. This allows the "General" tab on the file/folder properties to do a rename, so that
// it wont rename the file out from under the other pages, and have them barf when they go to
// persist their info.
//
void SendLastChanceApply(LPPROPDATA ppd) { TC_ITEMEXTRA tie; int nItem; int nItems = TabCtrl_GetItemCount(ppd->hwndTabs);
tie.tci.mask = TCIF_PARAM;
// we start with the last tab and count towards the first. This ensures
// that the more important tabs (such as the "General" tab) will be the last
// to recieve the PSN_LASTCHANCEAPPLY message.
for (nItem = nItems - 1; nItem >= 0; nItem--) { TabCtrl_GetItem(ppd->hwndTabs, nItem, &tie.tci);
if (tie.hwndPage) { // we ignore the return vale from the PSN_LASTCHANCEAPPLY message since
// there are probably prop sheet extensions that return both TRUE and
// FALSE for messages that they dont process...(sigh)
_Ppd_SendNotify(ppd, nItem, PSN_LASTCHANCEAPPLY, (LPARAM)TRUE); } } }
// return TRUE iff all sheets successfully handle the notification
BOOL ButtonPushed(LPPROPDATA ppd, WPARAM wParam) { HWND hwndTabs; int nItems, nItem; int nNotify; TC_ITEMEXTRA tie; BOOL bExit = FALSE; int nReturnNew = ppd->nReturn; int fSuccess = TRUE; DECLAREWAITCURSOR; LRESULT lres = 0; LPARAM lParam = FALSE; LPARAM nButtonPressed = 0;
switch (wParam) { case IDOK: nButtonPressed = PSBTN_OK; lParam = TRUE; bExit = TRUE; // Fall through...
case IDD_APPLYNOW: if (IDD_APPLYNOW == wParam) { nButtonPressed = PSBTN_APPLYNOW; }
// First allow the current dialog to validate itself.
if (_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_KILLACTIVE, 0)) return FALSE;
nReturnNew = 1;
nNotify = PSN_APPLY; break;
case IDCLOSE: nButtonPressed = PSBTN_FINISH; lParam = TRUE; // fall through
case IDCANCEL: if (IDCANCEL == wParam) { nButtonPressed = PSBTN_CANCEL; } bExit = TRUE; nNotify = PSN_RESET; break;
default: return FALSE; }
if (ppd && ppd->psh.pfnCallback && nButtonPressed) { ppd->psh.pfnCallback(NULL, PSCB_BUTTONPRESSED, nButtonPressed); }
SetWaitCursor();
hwndTabs = ppd->hwndTabs;
tie.tci.mask = TCIF_PARAM;
nItems = TabCtrl_GetItemCount(hwndTabs); for (nItem = 0; nItem < nItems; ++nItem) {
TabCtrl_GetItem(hwndTabs, nItem, &tie.tci);
if (tie.hwndPage) { /* If the dialog fails a PSN_APPY call (by returning TRUE),
** then it has invalid information on it (should be verified ** on the PSN_KILLACTIVE, but that is not always possible) ** and we want to abort the notifications. We select the failed ** page below. */ lres = _Ppd_SendNotify(ppd, nItem, nNotify, lParam);
if (lres) { fSuccess = FALSE; bExit = FALSE; break; } else { // if we need a restart (Apply or OK), then this is an exit
if ((nNotify == PSN_APPLY) && !bExit && ppd->nRestart) { DebugMsg(DM_TRACE, TEXT("PropertySheet: restart flags force close")); bExit = TRUE; } }
/* We have either reset or applied, so everything is
** up to date. */ tie.state &= ~FLAG_CHANGED; // tie.tci.mask = TCIF_PARAM; // already set
TabCtrl_SetItem(hwndTabs, nItem, &tie.tci); } }
/* If we leave ppd->hwndCurPage as NULL, it will tell the main
** loop to exit. */ if (fSuccess) { ppd->hwndCurPage = NULL; } else if (lres != PSNRET_INVALID_NOCHANGEPAGE) { // Need to change to the page that caused the failure.
// if lres == PSN_INVALID_NOCHANGEPAGE, then assume sheet has already
// changed to the page with the invalid information on it
TabCtrl_SetCurSel(hwndTabs, nItem); }
if (fSuccess) { // Set to the cached value
ppd->nReturn = nReturnNew; }
if (!bExit) { // before PageChange, so ApplyNow gets disabled faster.
if (fSuccess) { TCHAR szOK[30]; HWND hwndApply;
if (!IS_WIZARD(ppd)) { // The ApplyNow button should always be disabled after
// a successfull apply/cancel, since no change has been made yet.
hwndApply = GetDlgItem(ppd->hDlg, IDD_APPLYNOW); Button_SetStyle(hwndApply, BS_PUSHBUTTON, TRUE); EnableWindow(hwndApply, FALSE); ResetWizButtons(ppd); SendMessage(ppd->hDlg, DM_SETDEFID, IDOK, 0); ppd->idDefaultFallback = IDOK; }
// Undo PSM_CANCELTOCLOSE for the same reasons.
if (ppd->fFlags & PD_CANCELTOCLOSE) { ppd->fFlags &= ~PD_CANCELTOCLOSE; LocalizedLoadString(IDS_OK, szOK, ARRAYSIZE(szOK)); SetDlgItemText(ppd->hDlg, IDOK, szOK); EnableWindow(GetDlgItem(ppd->hDlg, IDCANCEL), TRUE); } }
/* Re-"select" the current item and get the whole list to
** repaint. */ if (lres != PSNRET_INVALID_NOCHANGEPAGE) PageChange(ppd, 1); }
ResetWaitCursor();
return(fSuccess); }
// Win3.1 USER didn't handle DM_SETDEFID very well-- it's very possible to get
// multiple buttons with the default button style look. This has been fixed
// for Win95, but the Setup wizard needs this hack when running from 3.1.
// it seems win95 doesn't handle it well either..
void ResetWizButtons(LPPROPDATA ppd) { int id;
if (IS_WIZARD(ppd)) {
for (id = 0; id < ARRAYSIZE(WizIDs); id++) SendDlgItemMessage(ppd->hDlg, WizIDs[id], BM_SETSTYLE, BS_PUSHBUTTON, TRUE); } }
void SetWizButtons(LPPROPDATA ppd, LPARAM lParam) { int idDef; int iShowID = IDD_NEXT; int iHideID = IDD_FINISH; BOOL bEnabled; BOOL bResetFocus; HWND hwndShow; HWND hwndFocus = GetFocus(); HWND hwndHide; HWND hwndBack; HWND hDlg = ppd->hDlg;
idDef = (int)LOWORD(SendMessage(hDlg, DM_GETDEFID, 0, 0));
// Enable/Disable the IDD_BACK button
hwndBack = GetDlgItem(hDlg, IDD_BACK); bEnabled = (lParam & PSWIZB_BACK) != 0; EnableWindow(hwndBack, bEnabled);
// Enable/Disable the IDD_NEXT button, and Next gets shown by default
// bEnabled remembers whether hwndShow should be enabled or not
hwndShow = GetDlgItem(hDlg, IDD_NEXT); bEnabled = (lParam & PSWIZB_NEXT) != 0; EnableWindow(hwndShow, bEnabled);
// Enable/Disable Show/Hide the IDD_FINISH button
if (lParam & (PSWIZB_FINISH | PSWIZB_DISABLEDFINISH)) { iShowID = IDD_FINISH; // If Finish is being shown
iHideID = IDD_NEXT; // then Next isn't
hwndShow = GetDlgItem(hDlg, IDD_FINISH); bEnabled = (lParam & PSWIZB_FINISH) != 0; EnableWindow(hwndShow, bEnabled); }
if (!(ppd->psh.dwFlags & PSH_WIZARDHASFINISH)) { hwndHide = GetDlgItem(hDlg, iHideID); ShowWindow(hwndHide, SW_HIDE); // Cannot disable the window; see Prsht_ButtonSubclassProc for explanation.
// WRONG - EnableWindow(hwndHide, FALSE);
hwndShow = GetDlgItem(hDlg, iShowID); // Cannot disable the window; see Prsht_ButtonSubclassProc for explanation.
// WRONG - EnableWindow(hwndShow, bEnabled);
ShowWindow(hwndShow, SW_SHOW); }
// bResetFocus keeps track of whether or not we need to set Focus to our button
bResetFocus = FALSE; if (hwndFocus) { // if the dude that has focus is a button, we want to steal focus away
// so users can just press enter all the way through a property sheet,
// getting the default as they go. this also catches the case
// of where focus is on one of our buttons which was turned off.
if (SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0L) & (DLGC_UNDEFPUSHBUTTON|DLGC_DEFPUSHBUTTON)) bResetFocus = TRUE; } if (!bResetFocus) { // if there is no focus or we're focused on an invisible/disabled
// item on the sheet, grab focus.
bResetFocus = !hwndFocus || !IsWindowVisible(hwndFocus) || !IsWindowEnabled(hwndFocus) ; }
// We used to do this code only if we nuked a button which had default
// or if bResetFocus. Unfortunately, some wizards turn off BACK+NEXT
// and then when they turn them back on, they want DEFID on NEXT.
// So now we always reset DEFID.
{ static const int ids[4] = { IDD_NEXT, IDD_FINISH, IDD_BACK, IDCANCEL }; int i; HWND hwndNewFocus = NULL;
for (i = 0; i < ARRAYSIZE(ids); i++) { hwndNewFocus = GetDlgItem(hDlg, ids[i]);
// can't do IsVisible because we may be doing this
// before the prop sheet as a whole is shown
if ((GetWindowLong(hwndNewFocus, GWL_STYLE) & WS_VISIBLE) && IsWindowEnabled(hwndNewFocus)) { hwndFocus = hwndNewFocus; break; } }
ppd->idDefaultFallback = ids[i]; if (bResetFocus) { if (!hwndNewFocus) hwndNewFocus = hDlg; #ifdef WM_NEXTDLGCTL_WORKS
SetDlgFocus(ppd, hwndNewFocus); #else
// 337614 - Since PSM_SETWIZBUTTONS is often a posted message,
// we may end up here when we don't even have focus at all
// (caller went on and called MessageBox or something before
// we got a chance to set the buttons). So do this only if
// focus belongs to our dialog box (or if it's nowhere).
hwndFocus = GetFocus(); if (!hwndFocus || (ppd->hDlg == hwndFocus || IsChild(ppd->hDlg, hwndFocus))) SetFocus(hwndNewFocus); #endif
} ResetWizButtons(ppd); SendMessage(hDlg, DM_SETDEFID, ids[i], 0);
} }
//
// lptie = NULL means "I don't care about the other goop, just give me
// the index."
//
int FindItem(HWND hwndTabs, HWND hwndPage, TC_ITEMEXTRA * lptie) { int i; TC_ITEMEXTRA tie;
if (!lptie) { tie.tci.mask = TCIF_PARAM; lptie = &tie; }
for (i = TabCtrl_GetItemCount(hwndTabs) - 1; i >= 0; --i) { TabCtrl_GetItem(hwndTabs, i, &lptie->tci);
if (lptie->hwndPage == hwndPage) { break; } }
//this will be -1 if the for loop falls out.
return i; }
// a page is telling us that something on it has changed and thus
// "Apply Now" should be enabled
void PageInfoChange(LPPROPDATA ppd, HWND hwndPage) { int i; TC_ITEMEXTRA tie;
tie.tci.mask = TCIF_PARAM; i = FindItem(ppd->hwndTabs, hwndPage, &tie);
if (i == -1) return;
if (!(tie.state & FLAG_CHANGED)) { // tie.tci.mask = TCIF_PARAM; // already set
tie.state |= FLAG_CHANGED; TabCtrl_SetItem(ppd->hwndTabs, i, &tie.tci); }
if (ppd->fAllowApply) EnableWindow(GetDlgItem(ppd->hDlg, IDD_APPLYNOW), TRUE); }
// a page is telling us that everything has reverted to its last
// saved state.
void PageInfoUnChange(LPPROPDATA ppd, HWND hwndPage) { int i; TC_ITEMEXTRA tie;
tie.tci.mask = TCIF_PARAM; i = FindItem(ppd->hwndTabs, hwndPage, &tie);
if (i == -1) return;
if (tie.state & FLAG_CHANGED) { tie.state &= ~FLAG_CHANGED; TabCtrl_SetItem(ppd->hwndTabs, i, &tie.tci); }
// check all the pages, if none are FLAG_CHANGED, disable IDD_APLYNOW
for (i = ppd->psh.nPages-1 ; i >= 0 ; i--) { if (!TabCtrl_GetItem(ppd->hwndTabs, i, &tie.tci)) break; if (tie.state & FLAG_CHANGED) break; } if (i<0) EnableWindow(GetDlgItem(ppd->hDlg, IDD_APPLYNOW), FALSE); }
HDWP Prsht_RepositionControl(LPPROPDATA ppd, HWND hwnd, HDWP hdwp, int dxMove, int dyMove, int dxSize, int dySize) { if (hwnd) { RECT rc; GetWindowRect(hwnd, &rc); MapWindowRect(HWND_DESKTOP, ppd->hDlg, &rc); hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left + dxMove, rc.top + dyMove, RECTWIDTH(rc) + dxSize, RECTHEIGHT(rc) + dySize, SWP_NOZORDER | SWP_NOACTIVATE); } return hdwp; }
//
// dxSize/(dySize+dyMove) is the amount by which to resize the tab control.
// dxSize/dySize controls how much the dialog should be grown.
// Buttons move by (dxSize, dySize+dyMove).
//
BOOL Prsht_ResizeDialog(LPPROPDATA ppd, int dxSize, int dySize, int dyMove) { BOOL fChanged = dxSize || dySize || dyMove; if (fChanged) { int dxMove = 0; // To make the code more symmetric in x and y
int dxAll = dxSize + dxMove; int dyAll = dySize + dyMove; RECT rc; UINT i; const int *rgid; UINT cid; HDWP hdwp; HWND hwnd;
// Use DeferWindowPos to avoid flickering. We expect to move
// the tab control, up to five buttons, two possible dividers,
// plus the current page. (And a partridge in a pear tree.)
//
hdwp = BeginDeferWindowPos(1 + 5 + 2 + 1);
// The tab control just sizes.
hdwp = Prsht_RepositionControl(ppd, ppd->hwndTabs, hdwp, 0, 0, dxAll, dyAll);
//
// Move and size the current page. We can't trust its location
// or size, since PageChange shoves it around without updating
// ppd->ySubDlg.
//
if (ppd->hwndCurPage) { hdwp = DeferWindowPos(hdwp, ppd->hwndCurPage, NULL, ppd->xSubDlg, ppd->ySubDlg, ppd->cxSubDlg, ppd->cySubDlg, SWP_NOZORDER | SWP_NOACTIVATE); }
//
// And our buttons just move by both the size and move (since they
// lie below both the tabs and the pages).
//
if (IS_WIZARD(ppd)) { //
// Ooh, wait, reposition the separator lines, too.
// Moves vertically but resizes horizontally.
//
hwnd = GetDlgItem(ppd->hDlg, IDD_DIVIDER); hdwp = Prsht_RepositionControl(ppd, hwnd, hdwp, 0, dyAll, dxAll, 0);
//
// The top divider does not move vertically since it lies
// above the area that is changing.
//
hwnd = GetDlgItem(ppd->hDlg, IDD_TOPDIVIDER); hdwp = Prsht_RepositionControl(ppd, hwnd, hdwp, 0, 0, dxAll, 0);
rgid = WizIDs; cid = ARRAYSIZE(WizIDs); } else { rgid = IDs; cid = ARRAYSIZE(IDs); }
for (i = 0 ; i < cid; i++) { hwnd = GetDlgItem(ppd->hDlg, rgid[i]); hdwp = Prsht_RepositionControl(ppd, hwnd, hdwp, dxAll, dyAll, 0, 0); }
// All finished sizing and moving. Let 'er rip!
if (hdwp) EndDeferWindowPos(hdwp);
// Grow ourselves as well
GetWindowRect(ppd->hDlg, &rc); SetWindowPos(ppd->hDlg, NULL, 0, 0, RECTWIDTH(rc) + dxAll, RECTHEIGHT(rc) + dyAll, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } return fChanged; }
BOOL Prsht_RecalcPageSizes(LPPROPDATA ppd) { SIZE siz; int dxSize = 0, dySize = 0, dyMove = 0;
// After inserting or removing a page, the tab control may have
// changed height. If so, then we need to resize ourselves to
// accomodate the growth or shrinkage, so that all the tabs remain
// visible.
//
// APP COMPAT! We cannot do this by default because Jamba 1.1
// **FAULTS** if the property sheet changes size after creation.
// Grrrrrr...
// Wizards don't have a visible tab control,
// so do this only for non-wizards
if (!IS_WIZARD(ppd)) { RECT rc;
// Get the client rect of the tab control in dialog coords
GetClientRect(ppd->hwndTabs, &rc); MapWindowRect(ppd->hwndTabs, ppd->hDlg, &rc);
// See how many rows there are now
TabCtrl_AdjustRect(ppd->hwndTabs, FALSE, &rc);
// rc.top is the new ySubDlg. Compute the amount we have to move.
dyMove = rc.top - ppd->ySubDlg; ppd->ySubDlg = rc.top; }
Prsht_GetIdealPageSize(ppd, &siz, GIPS_SKIPEXTERIOR97HEIGHT); dxSize = siz.cx - ppd->cxSubDlg; dySize = siz.cy - ppd->cySubDlg; ppd->cxSubDlg = siz.cx; ppd->cySubDlg = siz.cy; return Prsht_ResizeDialog(ppd, dxSize, dySize, dyMove); }
//
// InsertPropPage
//
// hpage is the page being inserted.
//
// hpageInsertAfter described where it should be inserted.
//
// hpageInsertAfter can be...
//
// MAKEINTRESOURCE(index) to insert at a specific index.
//
// NULL to insert at the beginning
//
// an HPROPSHEETPAGE to insert *after* that page
//
BOOL InsertPropPage(LPPROPDATA ppd, PSP * hpageInsertAfter, PSP * hpage) { TC_ITEMEXTRA tie; int nPage; HIMAGELIST himl; PAGEINFOEX pi; PISP pisp; int idx;
hpage = _Hijaak95Hack(ppd, hpage);
if (!hpage) return FALSE;
if (ppd->psh.nPages >= MAXPROPPAGES) return FALSE; // we're full
if (IS_INTRESOURCE(hpageInsertAfter)) { // Inserting by index
idx = (int) PtrToLong(hpageInsertAfter);
// Attempting to insert past the end is the same as appending.
if (idx > (int)ppd->psh.nPages) idx = (int)ppd->psh.nPages; } else { // Inserting by hpageInsertAfter.
for (idx = 0; idx < (int)(ppd->psh.nPages); idx++) { if (hpageInsertAfter == GETHPAGE(ppd, idx)) break; }
if (idx >= (int)(ppd->psh.nPages)) return FALSE; // hpageInsertAfter not found
idx++; // idx Points to the insertion location (to the right of hpageInsertAfter)
ASSERT(hpageInsertAfter == GETHPAGE(ppd, idx-1)); }
ASSERT(idx <= (int)(ppd->psh.nPages+1));
// Shift all pages adjacent to the insertion point to the right
for (nPage=ppd->psh.nPages - 1; nPage >= idx; nPage--) SETPISP(ppd, nPage+1, GETPISP(ppd, nPage));
// Insert the new page
pisp = InternalizeHPROPSHEETPAGE(hpage); SETPISP(ppd, idx, pisp);
ppd->psh.nPages++;
himl = TabCtrl_GetImageList(ppd->hwndTabs);
if (!GetPageInfoEx(ppd, pisp, &pi, GetMUILanguage(), GPI_ICON | GPI_BRTL | GPI_CAPTION | GPI_FONT | GPI_DIALOGEX)) { DebugMsg(DM_ERROR, TEXT("InsertPropPage: GetPageInfo failed")); goto bogus; }
Prsht_ComputeIdealPageSize(ppd, pisp, &pi);
tie.tci.mask = TCIF_TEXT | TCIF_PARAM | TCIF_IMAGE | (pi.bRTL ? TCIF_RTLREADING : 0); tie.hwndPage = NULL; tie.tci.pszText = pi.szCaption; tie.state = 0;
if (pi.hIcon) { if (himl) tie.tci.iImage = ImageList_AddIcon(himl, pi.hIcon); DestroyIcon(pi.hIcon); } else { tie.tci.iImage = -1; }
// Insert the page into the tab list
TabCtrl_InsertItem(ppd->hwndTabs, idx, &tie.tci);
// If this page wants premature initialization then init it
// do this last so pages can rely on "being there" at init time
if (pisp->_psp.dwFlags & PSP_PREMATURE) { if ((tie.hwndPage = _CreatePage(ppd, pisp, ppd->hDlg, GetMUILanguage())) == NULL) { TabCtrl_DeleteItem(ppd->hwndTabs, idx); // don't free the pisp here let the caller do it
// REARCHITECT raymondc - but caller doesn't know if hIcon has been destroyed
goto bogus; }
tie.tci.mask = TCIF_PARAM; TabCtrl_SetItem(ppd->hwndTabs, idx, &tie.tci); }
// Adjust the internally track current item if it is to the right of our insertion point
if (ppd->nCurItem >= idx) ppd->nCurItem++;
return TRUE;
bogus: // Shift everything back
for (nPage=idx; nPage < (int)(ppd->psh.nPages-1); nPage++) SETPISP(ppd, nPage, GETPISP(ppd, nPage+1));
ppd->psh.nPages--; return FALSE; }
#define AddPropPage(ppd, hpage) InsertPropPage(ppd, (LPVOID)MAKEINTRESOURCE(-1), hpage)
// removes property sheet hpage (index if NULL)
void RemovePropPage(LPPROPDATA ppd, int index, HPROPSHEETPAGE hpage) { int i = -1; BOOL fReturn = TRUE; TC_ITEMEXTRA tie;
//
// Notice that we explicitly do not do a InternalizeHPROPSHEETPAGE,
// because the app might be passing us garbage. We just want to
// say "Nope, can't find garbage here, sorry."
//
tie.tci.mask = TCIF_PARAM; if (hpage) { i = FindPageIndexByHpage(ppd, hpage); } if (i == -1) { i = index;
// this catches i < 0 && i >= (int)(ppd->psh.nPages)
if ((UINT)i >= ppd->psh.nPages) { DebugMsg(DM_ERROR, TEXT("RemovePropPage: invalid page")); return; } }
index = TabCtrl_GetCurSel(ppd->hwndTabs); if (i == index) { // if we're removing the current page, select another (don't worry
// about this page having invalid information on it -- we're nuking it)
PageChanging(ppd);
if (index == 0) index++; else index--;
if (SendMessage(ppd->hwndTabs, TCM_SETCURSEL, index, 0L) == -1) { // if we couldn't select (find) the new one, punt to 0th
SendMessage(ppd->hwndTabs, TCM_SETCURSEL, 0, 0L); } PageChange(ppd, 1); }
// REARCHITECT if removing a page below ppd->nCurItem, need to update
// nCurItem to prevent it from getting out of sync with hwndCurPage?
tie.tci.mask = TCIF_PARAM; TabCtrl_GetItem(ppd->hwndTabs, i, &tie.tci); if (tie.hwndPage) { if (ppd->hwndCurPage == tie.hwndPage) ppd->hwndCurPage = NULL; DestroyWindow(tie.hwndPage); }
RemovePropPageData(ppd, i); }
void RemovePropPageData(LPPROPDATA ppd, int nPage) { ULONG_PTR dw = PropPageActivateContext(ppd, GETPISP(ppd, nPage)); TabCtrl_DeleteItem(ppd->hwndTabs, nPage); DestroyPropertySheetPage(GETHPAGE(ppd, nPage)); PropPageDeactivateContext(dw);
//
// Delete the HPROPSHEETPAGE from our table and slide everybody down.
//
ppd->psh.nPages--; hmemcpy(&ppd->psh.H_phpage[nPage], &ppd->psh.H_phpage[nPage + 1], sizeof(ppd->psh.H_phpage[0]) * (ppd->psh.nPages - nPage)); }
// returns TRUE iff the page was successfully set to index/hpage
// Note: The iAutoAdj should be set to 1 or -1. This value is used
// by PageChange if a page refuses a SETACTIVE to either increment
// or decrement the page index.
BOOL PageSetSelection(LPPROPDATA ppd, int index, HPROPSHEETPAGE hpage, int iAutoAdj) { int i = -1; BOOL fReturn = FALSE; TC_ITEMEXTRA tie;
tie.tci.mask = TCIF_PARAM; if (hpage) { for (i = ppd->psh.nPages - 1; i >= 0; i--) { if (hpage == GETHPAGE(ppd, i)) break; } } if (i == -1) { if (index == -1) return FALSE;
i = index; } if (i >= MAXPROPPAGES) { // don't go off the end of our HPROPSHEETPAGE array
return FALSE; }
fReturn = !PageChanging(ppd); if (fReturn) { index = TabCtrl_GetCurSel(ppd->hwndTabs); if (SendMessage(ppd->hwndTabs, TCM_SETCURSEL, i, 0L) == -1) { // if we couldn't select (find) the new one, fail out
// and restore the old one
SendMessage(ppd->hwndTabs, TCM_SETCURSEL, index, 0L); fReturn = FALSE; } PageChange(ppd, iAutoAdj); } return fReturn; }
LRESULT QuerySiblings(LPPROPDATA ppd, WPARAM wParam, LPARAM lParam) { UINT i; for (i = 0 ; i < ppd->psh.nPages ; i++) { HWND hwndSibling = _Ppd_GetPage(ppd, i); if (hwndSibling) { LRESULT lres = SendMessage(hwndSibling, PSM_QUERYSIBLINGS, wParam, lParam); if (lres) return lres; } } return FALSE; }
// REVIEW HACK This gets round the problem of having a hotkey control
// up and trying to enter the hotkey that is already in use by a window.
BOOL HandleHotkey(LPARAM lparam) { WORD wHotkey; TCHAR szClass[32]; HWND hwnd;
// What hotkey did the user type hit?
wHotkey = (WORD)SendMessage((HWND)lparam, WM_GETHOTKEY, 0, 0); // Were they typing in a hotkey window?
hwnd = GetFocus(); GetClassName(hwnd, szClass, ARRAYSIZE(szClass)); if (lstrcmp(szClass, HOTKEY_CLASS) == 0) { // Yes.
SendMessage(hwnd, HKM_SETHOTKEY, wHotkey, 0); return TRUE; } return FALSE; }
//
// Function handles Next and Back functions for wizards. The code will
// be either PSN_WIZNEXT or PSN_WIZBACK
//
BOOL WizNextBack(LPPROPDATA ppd, int code) { LRESULT dwFind; int iPageIndex; int iAutoAdj = (code == PSN_WIZNEXT) ? 1 : -1;
dwFind = _Ppd_SendNotify(ppd, ppd->nCurItem, code, 0);
if (dwFind == -1) { return(FALSE); }
iPageIndex = FindPageIndex(ppd, ppd->nCurItem, dwFind, iAutoAdj);
if (iPageIndex == -1) { return(FALSE); }
return(PageSetSelection(ppd, iPageIndex, NULL, iAutoAdj)); }
BOOL Prsht_OnCommand(LPPROPDATA ppd, int id, HWND hwndCtrl, UINT codeNotify) {
//
// There's a bug in USER that when the user highlights a defpushbutton
// and presses ENTER, the WM_COMMAND is sent to the top-level dialog
// (i.e., the property sheet) instead of to the parent of the button.
// So if a property sheet page has a control whose ID coincidentally
// matches any of our own, we will think it's ours instead of theirs.
if (hwndCtrl && GetParent(hwndCtrl) != ppd->hDlg) goto Forward;
if (!hwndCtrl) hwndCtrl = GetDlgItem(ppd->hDlg, id);
switch (id) {
case IDCLOSE: case IDCANCEL: if (_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_QUERYCANCEL, 0) == 0) { ButtonPushed(ppd, id); } break;
case IDD_APPLYNOW: case IDOK: if (!IS_WIZARD(ppd)) {
//ButtonPushed returns true if and only if all pages have processed PSN_LASTCHANCEAPPLY
if (ButtonPushed(ppd, id)) {
//Everyone has processed the PSN_APPLY Message. Now send PSN_LASTCHANCEAPPLY message.
//
// HACKHACK (reinerF)
//
// We send out a private PSN_LASTCHANCEAPPLY message telling all the pages
// that everyone is done w/ the apply. This is needed for pages who have to do
// something after every other page has applied. Currently, the "General" tab
// of the file properties needs a last-chance to rename files as well as new print
// dialog in comdlg32.dll.
SendLastChanceApply(ppd); } } break;
case IDHELP: if (IsWindowEnabled(hwndCtrl)) { _Ppd_SendNotify(ppd, ppd->nCurItem, PSN_HELP, 0); } break;
case IDD_FINISH: { HWND hwndNewFocus; EnableWindow(ppd->hDlg, FALSE); hwndNewFocus = (HWND)_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_WIZFINISH, 0); // b#11346 - dont let multiple clicks on FINISH.
if (!hwndNewFocus) { ppd->hwndCurPage = NULL; ppd->nReturn = 1; } else { EnableWindow(ppd->hDlg, TRUE); if (IsWindow(hwndNewFocus) && IsChild(ppd->hDlg, hwndNewFocus)) #ifdef WM_NEXTDLGCTL_WORKS
SetDlgFocus(ppd, hwndNewFocus); #else
SetFocus(hwndNewFocus); #endif
} } break;
case IDD_NEXT: case IDD_BACK: ppd->idDefaultFallback = id; WizNextBack(ppd, id == IDD_NEXT ? PSN_WIZNEXT : PSN_WIZBACK); break;
default: Forward: FORWARD_WM_COMMAND(_Ppd_GetPage(ppd, ppd->nCurItem), id, hwndCtrl, codeNotify, SendMessage); }
return TRUE; }
BOOL Prop_IsDialogMessage(LPPROPDATA ppd, LPMSG32 pmsg32) { // Don't fault if we don't have a pointer...
if (!pmsg32) return FALSE;
if ((pmsg32->message == WM_KEYDOWN) && (GetKeyState(VK_CONTROL) < 0)) { BOOL bBack = FALSE;
switch (pmsg32->wParam) { case VK_TAB: bBack = GetKeyState(VK_SHIFT) < 0; break;
case VK_PRIOR: // VK_PAGE_UP
case VK_NEXT: // VK_PAGE_DOWN
bBack = (pmsg32->wParam == VK_PRIOR); break;
default: goto NoKeys; }
//notify of navigation key usage
SendMessage(ppd->hDlg, WM_CHANGEUISTATE, MAKELONG(UIS_CLEAR, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
if (IS_WIZARD(ppd)) { int idWiz; int idDlg; HWND hwnd;
if (bBack) { idWiz = PSN_WIZBACK; idDlg = IDD_BACK; } else { idWiz = PSN_WIZNEXT; idDlg = IDD_NEXT; }
hwnd = GetDlgItem(ppd->hDlg, idDlg); if (IsWindowVisible(hwnd) && IsWindowEnabled(hwnd)) WizNextBack(ppd, idWiz); } else { int iStart = TabCtrl_GetCurSel(ppd->hwndTabs); int iCur;
//
// Skip over hidden tabs, but don't go into an infinite loop.
//
iCur = iStart; do { // tab in reverse if shift is down
if (bBack) iCur += (ppd->psh.nPages - 1); else iCur++;
iCur %= ppd->psh.nPages; } while (_Ppd_IsPageHidden(ppd, iCur) && iCur != iStart); PageSetSelection(ppd, iCur, NULL, 1); } return TRUE; } NoKeys:
//
// Since we now send out a PSN_TRANSLATEACCELERATOR, add a
// short-circuit so we don't do all this work for things
// that can't possibly be accelerators.
//
if (pmsg32->message >= WM_KEYFIRST && pmsg32->message <= WM_KEYLAST &&
// And there had better be a target window...
pmsg32->hwnd &&
// and the target window must live either outside the propsheet
// altogether or completely inside the propsheet page.
// (This is so that the propsheet can display its own popup dialog,
// but can't futz with the tab control or OK/Cancel buttons.)
(!IsChild(ppd->hDlg, pmsg32->hwnd) || IsChild(ppd->hwndCurPage, pmsg32->hwnd)) &&
// Then ask the propsheet if he wants to eat it.
_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_TRANSLATEACCELERATOR, (LPARAM)pmsg32) == PSNRET_MESSAGEHANDLED) return TRUE;
if (IsDialogMessage32(ppd->hDlg, pmsg32, TRUE)) return TRUE;
return FALSE; }
HRESULT Prsht_GetObject (LPPROPDATA ppd, HWND hDlg, int iItem, const IID *piid, void **pObject) { TC_ITEMEXTRA tie; NMOBJECTNOTIFY non; PISP pisp = GETPISP(ppd, iItem); *pObject = NULL;
tie.tci.mask = TCIF_PARAM; TabCtrl_GetItem(ppd->hwndTabs, iItem, &tie.tci); if (!tie.hwndPage && ((tie.hwndPage = _CreatePage(ppd, pisp, hDlg, GetMUILanguage())) == NULL)) { RemovePropPageData(ppd, iItem); return E_UNEXPECTED; } TabCtrl_SetItem(ppd->hwndTabs, iItem, &tie.tci);
non.iItem = -1; non.piid = piid; non.pObject = NULL; non.hResult = E_NOINTERFACE; non.dwFlags = 0;
SendNotifyEx (tie.hwndPage, ppd->hwndTabs, PSN_GETOBJECT, &non.hdr, TRUE ); if (SUCCEEDED (non.hResult)) { *pObject = non.pObject; if (pObject == NULL) non.hResult = E_UNEXPECTED; } else if (non.pObject) { ((LPDROPTARGET) non.pObject)->lpVtbl->Release ((LPDROPTARGET) non.pObject); non.pObject = NULL; } return non.hResult; }
//
// We would not normally need IDD_PAGELIST except that DefWindowProc() and
// WinHelp() do hit-testing differently. DefWindowProc() will do cool
// things like checking against the SetWindowRgn and skipping over windows
// that return HTTRANSPARENT. WinHelp() on the other hand
// ignores window regions and transparency. So what happens is if you
// click on the transparent part of a tab control, DefWindowProc() says
// (correctly) "He clicked on the dialog background". We then say, "Okay,
// WinHelp(), go display context help for the dialog background", and it
// says, "Hey, I found a tab control. I'm going to display help for the
// tab control now." To keep a bogus context menu from appearing, we
// explicitly tell WinHelp that "If you found a tab control (IDD_PAGELIST),
// then ignore it (NO_HELP)."
//
const static DWORD aPropHelpIDs[] = { // Context Help IDs
IDD_APPLYNOW, IDH_COMM_APPLYNOW, IDD_PAGELIST, NO_HELP, 0, 0 };
void HandlePaletteChange(LPPROPDATA ppd, UINT uMessage, HWND hDlg) { HDC hdc; hdc = GetDC(hDlg); if (hdc) { BOOL fRepaint; SelectPalette(hdc,ppd->hplWatermark,(uMessage == WM_PALETTECHANGED)); fRepaint = RealizePalette(hdc); if (fRepaint) InvalidateRect(hDlg,NULL,TRUE); } ReleaseDC(hDlg,hdc); }
//
// Paint a rectangle with the specified brush and palette.
//
void PaintWithPaletteBrush(HDC hdc, LPRECT lprc, HPALETTE hplPaint, HBRUSH hbrPaint) { HBRUSH hbrPrev = SelectBrush(hdc, hbrPaint); UnrealizeObject(hbrPaint); if (hplPaint) { SelectPalette(hdc, hplPaint, FALSE); RealizePalette(hdc); } FillRect(hdc, lprc, hbrPaint); SelectBrush(hdc, hbrPrev); }
//
// lprc is the target rectangle.
// Use as much of the bitmap as will fit into the target rectangle.
// If the bitmap is smaller than the target rectangle, then fill the rest with
// the pixel in the upper left corner of the hbmpPaint.
//
void PaintWithPaletteBitmap(HDC hdc, LPRECT lprc, HPALETTE hplPaint, HBITMAP hbmpPaint) { HDC hdcBmp; BITMAP bm; int cxRect, cyRect, cxBmp, cyBmp;
GetObject(hbmpPaint, sizeof(BITMAP), &bm); hdcBmp = CreateCompatibleDC(hdc); SelectObject(hdcBmp, hbmpPaint);
if (hplPaint) { SelectPalette(hdc, hplPaint, FALSE); RealizePalette(hdc); }
cxRect = RECTWIDTH(*lprc); cyRect = RECTHEIGHT(*lprc);
// Never use more pixels from the bmp as we have room in the rect.
cxBmp = min(bm.bmWidth, cxRect); cyBmp = min(bm.bmHeight, cyRect);
BitBlt(hdc, lprc->left, lprc->top, cxBmp, cyBmp, hdcBmp, 0, 0, SRCCOPY);
// If bitmap is too narrow, then StretchBlt to fill the width.
if (cxBmp < cxRect) StretchBlt(hdc, lprc->left + cxBmp, lprc->top, cxRect - cxBmp, cyBmp, hdcBmp, 0, 0, 1, 1, SRCCOPY);
// If bitmap is to short, then StretchBlt to fill the height.
if (cyBmp < cyRect) StretchBlt(hdc, lprc->left, cyBmp, cxRect, cyRect - cyBmp, hdcBmp, 0, 0, 1, 1, SRCCOPY);
DeleteDC(hdcBmp); }
void _SetHeaderTitles(HWND hDlg, LPPROPDATA ppd, UINT uPage, LPCTSTR pszNewTitle, BOOL bTitle) { PISP pisp = NULL;
// Must be for wizard97
if (ppd->psh.dwFlags & PSH_WIZARD97) { // Page number must be within range
if (uPage < ppd->psh.nPages) { // Get the page structure
pisp = GETPISP(ppd, uPage);
// We should have this page if it's within range
ASSERT(pisp);
// Do this only if this page has header.
if (!(pisp->_psp.dwFlags & PSP_HIDEHEADER)) { LPCTSTR pszOldTitle = bTitle ? pisp->_psp.pszHeaderTitle : pisp->_psp.pszHeaderSubTitle;
if (!IS_INTRESOURCE(pszOldTitle)) LocalFree((LPVOID)pszOldTitle);
// Set the new title
if (bTitle) pisp->_psp.pszHeaderTitle = pszNewTitle; else pisp->_psp.pszHeaderSubTitle = pszNewTitle;
// set pszNewTitle to NULL here so that we don't free it later
pszNewTitle = NULL; // set the correct flags
pisp->_psp.dwFlags |= bTitle ? PSP_USEHEADERTITLE : PSP_USEHEADERSUBTITLE;
// force redrawing of the titles
if (uPage == (UINT)ppd->nCurItem) { RECT rcHeader; GetClientRect(hDlg, &rcHeader); rcHeader.bottom = ppd->cyHeaderHeight;
InvalidateRect(hDlg, &rcHeader, FALSE); } } } }
if (pszNewTitle) LocalFree((LPVOID)pszNewTitle); }
void PropSheetPaintHeader(LPPROPDATA ppd, PISP pisp, HWND hDlg, HDC hdc) { RECT rcHeader;
GetClientRect(hDlg, &rcHeader); if ((rcHeader.right > 0) && (rcHeader.bottom > 0)) { RECT rcHeaderBitmap;
rcHeader.bottom = ppd->cyHeaderHeight;
// do we need to paint the header?
if (ppd->psh.dwFlags & PSH_WIZARD97IE4) { // Do it the WIZARD97IE4 way
// Bug-for-bug compatibility: WIZARD97IE4 tested the wrong flag here
if ((ppd->psh.dwFlags & PSH_WATERMARK) && (ppd->hbrWatermark)) PaintWithPaletteBrush(hdc, &rcHeader, ppd->hplWatermark, ppd->hbrHeader); SetBkMode(hdc, TRANSPARENT); } else { // Do it the WIZARD97IE5 way
if ((ppd->psh.dwFlags & PSH_HEADER) && (ppd->hbmHeader)) { // compute the rectangle for the bitmap depending on the size of the header
int bx = RECTWIDTH(rcHeader) - HEADERBITMAP_CXBACK; ASSERT(bx > 0); FillRect(hdc, &rcHeader, g_hbrWindow); SetRect(&rcHeaderBitmap, bx, HEADERBITMAP_Y, bx + HEADERBITMAP_WIDTH, HEADERBITMAP_Y + HEADERBITMAP_HEIGHT); PaintWithPaletteBitmap(hdc, &rcHeaderBitmap, ppd->hplWatermark, ppd->hbmHeader); SetBkColor(hdc, g_clrWindow); SetTextColor(hdc, g_clrWindowText); } else { SendMessage(hDlg, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hDlg); } }
//
// WIZARD97IE5 subtracts out the space used by the header bitmap.
// WIZARD97IE4 uses the full width since the header bitmap
// in IE4 is a watermark and occupies no space.
//
if (!(ppd->psh.dwFlags & PSH_WIZARD97IE4)) { rcHeader.right -= HEADERBITMAP_CXBACK + HEADERSUBTITLE_WRAPOFFSET; }
ASSERT(rcHeader.right);
if (HASHEADERTITLE(pisp)) { _WriteHeaderTitle(ppd, hdc, &rcHeader, pisp->_psp.pszHeaderTitle, TRUE, DRAWTEXT_WIZARD97FLAGS); }
if (HASHEADERSUBTITLE(pisp)) { _WriteHeaderTitle(ppd, hdc, &rcHeader, pisp->_psp.pszHeaderSubTitle, FALSE, DRAWTEXT_WIZARD97FLAGS); } } }
// Free the title if we need to
void Prsht_FreeTitle(LPPROPDATA ppd) { if (ppd->fFlags & PD_FREETITLE) { ppd->fFlags &= ~PD_FREETITLE; if (!IS_INTRESOURCE(ppd->psh.pszCaption)) { LocalFree((LPVOID)ppd->psh.pszCaption); } } }
//
// pfnStrDup is the function that converts lParam into a native character
// set string. (Either StrDup or StrDup_AtoW).
//
void Prsht_OnSetTitle(LPPROPDATA ppd, WPARAM wParam, LPARAM lParam, STRDUPPROC pfnStrDup) { LPTSTR pszTitle;
//
// The ppd->psh.pszCaption is not normally LocalAlloc()d; it's
// just a pointer copy. But if the app does a PSM_SETTITLE,
// then all of a sudden it got LocalAlloc()d and needs to be
// freed. PD_FREETITLE is the flag that tell us that this has
// happened.
//
if (IS_INTRESOURCE(lParam)) { pszTitle = (LPTSTR)lParam; } else { pszTitle = pfnStrDup((LPTSTR)lParam); }
if (pszTitle) { Prsht_FreeTitle(ppd); // Free old title if necessary
ppd->psh.pszCaption = pszTitle; ppd->fFlags |= PD_FREETITLE; // Need to free this
ppd->psh.dwFlags = ((((DWORD)wParam) & PSH_PROPTITLE) | (ppd->psh.dwFlags & ~PSH_PROPTITLE)); _SetTitle(ppd->hDlg, ppd); } }
BOOL_PTR CALLBACK PropSheetDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam) { HWND hwndT; LPPROPDATA ppd = (LPPROPDATA)GetWindowLongPtr(hDlg, DWLP_USER); LRESULT lres;
if (!ppd && (uMessage != WM_INITDIALOG)) return FALSE;
switch (uMessage) { case WM_INITDIALOG: InitPropSheetDlg(hDlg, (LPPROPDATA)lParam); return FALSE;
case WM_SYSCOMMAND: if (wParam == SC_HOTKEY) return HandleHotkey(lParam); else if (wParam == SC_CLOSE) { UINT id = IDCLOSE;
if (IS_WIZARD(ppd)) id = IDCANCEL; else if (ppd->fFlags & PD_CANCELTOCLOSE) id = IDOK;
// system menu close should be IDCANCEL, but if we're in the
// PSM_CANCELTOCLOSE state, treat it as an IDOK (ie, "Close").
return Prsht_OnCommand(ppd, id, NULL, 0); }
return FALSE; // Let default process happen
case WM_NCDESTROY: { int iPage;
ASSERT(GetDlgItem(hDlg, IDD_PAGELIST) == NULL);
ppd->hwndTabs = NULL;
// NOTE: all of the hwnds for the pages must be destroyed by now!
// Release all page objects in REVERSE ORDER so we can have
// pages that are dependant on eachother based on the initial
// order of those pages
//
for (iPage = ppd->psh.nPages - 1; iPage >= 0; iPage--) { ULONG_PTR dw = PropPageActivateContext(ppd, GETPISP(ppd, iPage)); DestroyPropertySheetPage(GETHPAGE(ppd, iPage)); PropPageDeactivateContext(dw); } // hwndCurPage is no longer valid from here on
ppd->hwndCurPage = NULL;
if (ppd->hActCtxInit) ReleaseActCtx(ppd->hActCtxInit);
// If we are modeless, we need to free our ppd. If we are modal,
// we let _RealPropertySheet free it since one of our pages may
// set the restart flag during DestroyPropertySheetPage above.
if (ppd->psh.dwFlags & PSH_MODELESS) { LocalFree(ppd); } } //
// NOTES:
// Must return FALSE to avoid DS leak!!!
//
return FALSE;
case WM_DESTROY: { int i; HIMAGELIST himl; int c = TabCtrl_GetItemCount(ppd->hwndTabs);
for (i = 0; i < c; i++) { EnableThemeDialogTexture(_Ppd_GetPage(ppd, i), ETDT_DISABLE); }
// Destroy the image list we created during our init call.
himl = TabCtrl_GetImageList(ppd->hwndTabs); if (himl) ImageList_Destroy(himl);
if (ppd->psh.dwFlags & PSH_WIZARD97) {
// Even if the PSH_USEHBMxxxxxx flag is set, we might
// need to delete the bitmap if we had to create a
// stretched copy.
if (ppd->psh.dwFlags & PSH_WATERMARK) { if ((!(ppd->psh.dwFlags & PSH_USEHBMWATERMARK) || ppd->hbmWatermark != ppd->psh.H_hbmWatermark) && ppd->hbmWatermark) DeleteObject(ppd->hbmWatermark);
if (!(ppd->psh.dwFlags & PSH_USEHPLWATERMARK) && ppd->hplWatermark) DeleteObject(ppd->hplWatermark);
if (ppd->hbrWatermark) DeleteObject(ppd->hbrWatermark); }
if ((ppd->psh.dwFlags & PSH_HEADER) && ppd->psh.H_hbmHeader) { if ((!(ppd->psh.dwFlags & PSH_USEHBMHEADER) || ppd->hbmHeader != ppd->psh.H_hbmHeader) && ppd->hbmHeader) { ASSERT(ppd->hbmHeader != ppd->hbmWatermark); DeleteObject(ppd->hbmHeader); }
if (ppd->hbrHeader) { ASSERT(ppd->hbrHeader != ppd->hbrWatermark); DeleteObject(ppd->hbrHeader); } }
if (ppd->hFontBold) DeleteObject(ppd->hFontBold); }
if ((ppd->psh.dwFlags & PSH_USEICONID) && ppd->psh.H_hIcon) DestroyIcon(ppd->psh.H_hIcon);
Prsht_FreeTitle(ppd); }
break;
case WM_ERASEBKGND: return ppd->fFlags & PD_NOERASE; break;
case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; PISP pisp;
hdc = BeginPaint(hDlg, &ps);
// only paint if the invalid rect has a non-zero widht or height
if (((ps.rcPaint.bottom - ps.rcPaint.top) > 0) && ((ps.rcPaint.right - ps.rcPaint.left) > 0)) { // (dli) paint the header
if ((ppd->psh.dwFlags & PSH_WIZARD97) && (!((pisp = GETPISP(ppd, ppd->nCurItem))->_psp.dwFlags & PSP_HIDEHEADER))) { PropSheetPaintHeader(ppd, pisp, hDlg, hdc); }
if (ps.fErase) { SendMessage (hDlg, WM_ERASEBKGND, (WPARAM) hdc, 0); } }
EndPaint(hDlg, &ps); } break;
case WM_COMMAND: // Cannot use HANDLE_WM_COMMAND, because we want to pass a result!
return Prsht_OnCommand(ppd, GET_WM_COMMAND_ID(wParam, lParam), GET_WM_COMMAND_HWND(wParam, lParam), GET_WM_COMMAND_CMD(wParam, lParam));
case WM_NOTIFY: switch (((NMHDR *)lParam)->code) { case TCN_SELCHANGE: PageChange(ppd, 1); break;
case TCN_SELCHANGING: { lres = PageChanging(ppd); if (!lres) { SetWindowPos(ppd->hwndCurPage, HWND_BOTTOM, 0,0,0,0, SWP_NOACTIVATE | SWP_NOSIZE |SWP_NOMOVE); } goto ReturnLres; } break;
case TCN_GETOBJECT: { LPNMOBJECTNOTIFY lpnmon = (LPNMOBJECTNOTIFY)lParam;
lpnmon->hResult = Prsht_GetObject(ppd, hDlg, lpnmon->iItem, lpnmon->piid, &lpnmon->pObject); } break;
default: return FALSE; } return TRUE;
case PSM_SETWIZBUTTONS: SetWizButtons(ppd, lParam); break;
case PSM_SETFINISHTEXTA: case PSM_SETFINISHTEXT: { HWND hFinish = GetDlgItem(hDlg, IDD_FINISH); HWND hwndFocus = GetFocus(); HWND hwnd; BOOL fSetFocus = FALSE;
if (!(ppd->psh.dwFlags & PSH_WIZARDHASFINISH)) { hwnd = GetDlgItem(hDlg, IDD_NEXT); if (hwnd == hwndFocus) fSetFocus = TRUE; ShowWindow(hwnd, SW_HIDE); }
hwnd = GetDlgItem(hDlg, IDD_BACK); if (hwnd == hwndFocus) fSetFocus = TRUE; ShowWindow(hwnd, SW_HIDE);
if (lParam) { if (uMessage == PSM_SETFINISHTEXTA) { SetWindowTextA(hFinish, (LPSTR)lParam); } else Button_SetText(hFinish, (LPTSTR)lParam); } ShowWindow(hFinish, SW_SHOW); Button_Enable(hFinish, TRUE); ResetWizButtons(ppd); SendMessage(hDlg, DM_SETDEFID, IDD_FINISH, 0); ppd->idDefaultFallback = IDD_FINISH; if (fSetFocus) #ifdef WM_NEXTDLGCTL_WORKS
SetDlgFocus(ppd, hFinish); #else
SetFocus(hFinish); #endif
} break;
case PSM_SETTITLEA: Prsht_OnSetTitle(ppd, wParam, lParam, StrDup_AtoW); break;
case PSM_SETTITLE: Prsht_OnSetTitle(ppd, wParam, lParam, StrDup); break;
case PSM_SETHEADERTITLEA: { LPWSTR lpHeaderTitle = (lParam && HIWORD(lParam)) ? ProduceWFromA(CP_ACP, (LPCSTR)lParam) : StrDupW((LPWSTR)lParam); if (lpHeaderTitle) _SetHeaderTitles(hDlg, ppd, (UINT)wParam, lpHeaderTitle, TRUE); } break; case PSM_SETHEADERTITLE: { LPTSTR lpHeaderTitle = StrDup((LPCTSTR)lParam); if (lpHeaderTitle) _SetHeaderTitles(hDlg, ppd, (UINT)wParam, lpHeaderTitle, TRUE); } break; case PSM_SETHEADERSUBTITLEA: { LPWSTR lpHeaderSubTitle = (lParam && HIWORD(lParam)) ? ProduceWFromA(CP_ACP, (LPCSTR)lParam) : StrDupW((LPWSTR)lParam); if (lpHeaderSubTitle) _SetHeaderTitles(hDlg, ppd, (UINT)wParam, lpHeaderSubTitle, FALSE); } break;
case PSM_SETHEADERSUBTITLE: { LPTSTR lpHeaderSubTitle = StrDup((LPCTSTR)lParam); if (lpHeaderSubTitle) _SetHeaderTitles(hDlg, ppd, (UINT)wParam, lpHeaderSubTitle, FALSE); } break; case PSM_CHANGED: PageInfoChange(ppd, (HWND)wParam); break;
case PSM_RESTARTWINDOWS: ppd->nRestart |= ID_PSRESTARTWINDOWS; break;
case PSM_REBOOTSYSTEM: ppd->nRestart |= ID_PSREBOOTSYSTEM; break;
case PSM_DISABLEAPPLY: // the page is asking us to gray the "Apply" button and not let
// anyone else re-enable it
if (ppd->fAllowApply) { ppd->fAllowApply = FALSE; EnableWindow(GetDlgItem(ppd->hDlg, IDD_APPLYNOW), FALSE); } break;
case PSM_ENABLEAPPLY: // the page is asking us to allow the the "Apply" button to be
// once again enabled
if (!ppd->fAllowApply) ppd->fAllowApply = TRUE; // REARCHITECT - raymondc - shouldn't we call EnableWindow?
break;
case PSM_CANCELTOCLOSE: if (!(ppd->fFlags & PD_CANCELTOCLOSE)) { TCHAR szClose[20]; ppd->fFlags |= PD_CANCELTOCLOSE; LocalizedLoadString(IDS_CLOSE, szClose, ARRAYSIZE(szClose)); SetDlgItemText(hDlg, IDOK, szClose); EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE); } break;
case PSM_SETCURSEL: lres = PageSetSelection(ppd, (int)wParam, (HPROPSHEETPAGE)lParam, 1); goto ReturnLres;
case PSM_SETCURSELID: { int iPageIndex;
iPageIndex = FindPageIndex(ppd, ppd->nCurItem, (DWORD)lParam, 1);
if (iPageIndex == -1) lres = 0; else lres = PageSetSelection(ppd, iPageIndex, NULL, 1); goto ReturnLres; } break;
case PSM_REMOVEPAGE: RemovePropPage(ppd, (int)wParam, (HPROPSHEETPAGE)lParam); break;
case PSM_ADDPAGE: lres = AddPropPage(ppd,(HPROPSHEETPAGE)lParam); goto ReturnLres;
case PSM_INSERTPAGE: lres = InsertPropPage(ppd, (HPROPSHEETPAGE)wParam, (HPROPSHEETPAGE)lParam); goto ReturnLres;
case PSM_QUERYSIBLINGS: lres = QuerySiblings(ppd, wParam, lParam); goto ReturnLres;
case PSM_UNCHANGED: PageInfoUnChange(ppd, (HWND)wParam); break;
case PSM_APPLY: // a page is asking us to simulate an "Apply Now".
// let the page know if we're successful
lres = ButtonPushed(ppd, IDD_APPLYNOW); goto ReturnLres;
case PSM_GETTABCONTROL: lres = (LRESULT)ppd->hwndTabs; goto ReturnLres;
case PSM_GETCURRENTPAGEHWND: lres = (LRESULT)ppd->hwndCurPage; goto ReturnLres;
case PSM_PRESSBUTTON: if (wParam <= PSBTN_MAX) { const static int IndexToID[] = {IDD_BACK, IDD_NEXT, IDD_FINISH, IDOK, IDD_APPLYNOW, IDCANCEL, IDHELP}; Prsht_OnCommand(ppd, IndexToID[wParam], NULL, 0); } break;
case PSM_ISDIALOGMESSAGE: // returning TRUE means we handled it, do a continue
// FALSE do standard translate/dispatch
lres = Prop_IsDialogMessage(ppd, (LPMSG32)lParam); goto ReturnLres;
case PSM_HWNDTOINDEX: lres = FindItem(ppd->hwndTabs, (HWND)wParam, NULL); goto ReturnLres;
case PSM_INDEXTOHWND: if ((UINT)wParam < ppd->psh.nPages) lres = (LRESULT)_Ppd_GetPage(ppd, (int)wParam); else lres = 0; goto ReturnLres;
case PSM_PAGETOINDEX: lres = FindPageIndexByHpage(ppd, (HPROPSHEETPAGE)lParam); goto ReturnLres;
case PSM_INDEXTOPAGE: if ((UINT)wParam < ppd->psh.nPages) lres = (LRESULT)GETHPAGE(ppd, wParam); else lres = 0; goto ReturnLres;
case PSM_INDEXTOID: if ((UINT)wParam < ppd->psh.nPages) { lres = (LRESULT)GETPPSP(ppd, wParam)->P_pszTemplate;
// Need to be careful -- return a value only if pszTemplate
// is an ID. Don't return out our internal pointers!
if (!IS_INTRESOURCE(lres)) lres = 0; } else lres = 0; goto ReturnLres;
case PSM_IDTOINDEX: lres = FindPageIndex(ppd, ppd->nCurItem, (DWORD)lParam, 0); goto ReturnLres;
case PSM_GETRESULT: // This is valid only after the property sheet is gone
if (ppd->hwndCurPage) { lres = -1; // you shouldn't be calling me yet
} else { lres = ppd->nReturn; if (lres > 0 && ppd->nRestart) lres = ppd->nRestart; } goto ReturnLres; break;
case PSM_RECALCPAGESIZES: lres = Prsht_RecalcPageSizes(ppd); goto ReturnLres;
// these should be relayed to all created dialogs
case WM_WININICHANGE: case WM_SYSCOLORCHANGE: case WM_DISPLAYCHANGE: { int nItem, nItems = TabCtrl_GetItemCount(ppd->hwndTabs); for (nItem = 0; nItem < nItems; ++nItem) {
hwndT = _Ppd_GetPage(ppd, nItem); if (hwndT) SendMessage(hwndT, uMessage, wParam, lParam); } SendMessage(ppd->hwndTabs, uMessage, wParam, lParam); } break;
//
// send toplevel messages to the current page and tab control
//
case WM_PALETTECHANGED: //
// If this is our window we need to avoid selecting and realizing
// because doing so would cause an infinite loop between WM_QUERYNEWPALETTE
// and WM_PALETTECHANGED.
//
if((HWND)wParam == hDlg) { return(FALSE); } //
// FALL THROUGH
//
case WM_QUERYNEWPALETTE: // This is needed when another window which has different palette clips
// us
if ((ppd->psh.dwFlags & PSH_WIZARD97) && (ppd->psh.dwFlags & PSH_WATERMARK) && (ppd->psh.hplWatermark)) HandlePaletteChange(ppd, uMessage, hDlg);
//
// FALL THROUGH
//
case WM_ENABLE: case WM_DEVICECHANGE: case WM_QUERYENDSESSION: case WM_ENDSESSION: if (ppd->hwndTabs) SendMessage(ppd->hwndTabs, uMessage, wParam, lParam); //
// FALL THROUGH
//
case WM_ACTIVATEAPP: case WM_ACTIVATE: { hwndT = _Ppd_GetPage(ppd, ppd->nCurItem); if (hwndT && IsWindow(hwndT)) { //
// By doing this, we are "handling" the message. Therefore
// we must set the dialog return value to whatever the child
// wanted.
//
lres = SendMessage(hwndT, uMessage, wParam, lParam); goto ReturnLres; } }
if ((uMessage == WM_PALETTECHANGED) || (uMessage == WM_QUERYNEWPALETTE)) return TRUE; else return FALSE;
case WM_CONTEXTMENU: // ppd->hwndTabs is handled by aPropHelpIDs to work around a USER bug.
// See aPropHelpIDs for gory details.
if ((ppd->hwndCurPage != (HWND)wParam) && (!IS_WIZARD(ppd))) WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(LPVOID) aPropHelpIDs); break;
case WM_HELP: hwndT = (HWND)((LPHELPINFO)lParam)->hItemHandle; if ((GetParent(hwndT) == hDlg) && (hwndT != ppd->hwndTabs)) WinHelp(hwndT, NULL, HELP_WM_HELP, (ULONG_PTR)(LPVOID) aPropHelpIDs); break;
default: return FALSE; } return TRUE;
ReturnLres: SetWindowLongPtr(hDlg, DWLP_MSGRESULT, lres); return TRUE;
}
//
// Draw the background for wizard pages.
//
BOOL Prsht_EraseWizBkgnd(LPPROPDATA ppd, HDC hdc) { RECT rc; BOOL fPainted = FALSE; GetClientRect(ppd->hDlg, &rc);
if (ppd->psh.dwFlags & PSH_WIZARD97IE4) { if (ppd->hbrWatermark) { PaintWithPaletteBrush(hdc, &rc, ppd->hplWatermark, ppd->hbrWatermark); fPainted = TRUE; } } else // PSH_WIZARD97IE5
{ if (ppd->hbmWatermark) { // Right-hand side gets g_hbrWindow.
rc.left = BITMAP_WIDTH; FillRect(hdc, &rc, g_hbrWindow);
// Left-hand side gets watermark in top portion with autofill...
rc.right = rc.left; rc.left = 0; PaintWithPaletteBitmap(hdc, &rc, ppd->hplWatermark, ppd->hbmWatermark); fPainted = TRUE; } } return fPainted; }
LRESULT CALLBACK WizardWndProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData) { LPPROPDATA ppd = (LPPROPDATA)dwRefData; switch (uMessage) { case WM_ERASEBKGND: if (Prsht_EraseWizBkgnd(ppd, (HDC)wParam)) return TRUE; break;
// Only PSH_WIZARD97IE4 cares about these messages
case WM_CTLCOLOREDIT: case WM_CTLCOLORDLG: if (!(ppd->psh.dwFlags & PSH_WIZARD97IE4)) break; // fall through
case WM_CTLCOLOR: case WM_CTLCOLORMSGBOX: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORBTN: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: if (ppd->psh.dwFlags & PSH_WIZARD97IE4) { if (ppd->hbrWatermark) { POINT pt; // Bug-for-bug compatibility: TRANSPARENT messes up edit
// controls when they scroll, but that's what IE4 did.
SetBkMode((HDC)wParam, TRANSPARENT);
if (ppd->hplWatermark) { SelectPalette((HDC)wParam, ppd->hplWatermark, FALSE); RealizePalette((HDC)wParam); } UnrealizeObject(ppd->hbrWatermark); GetDCOrgEx((HDC)wParam, &pt); // Bug-for-bug compatibility: We shouldn't use GetParent
// because the notification might be forwarded up from an
// embedded dialog child, but that's what IE4 did.
ScreenToClient(GetParent((HWND)lParam), &pt); SetBrushOrgEx((HDC)wParam, -pt.x, -pt.y, NULL); return (LRESULT)(HBRUSH)ppd->hbrWatermark; } } else // PSH_WIZARD97IE5
{ if (ppd->hbmWatermark) { LRESULT lRet = DefWindowProc(hDlg, uMessage, wParam, lParam); if (lRet == DefSubclassProc(hDlg, uMessage, wParam, lParam)) { SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT)); SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); return (LRESULT)g_hbrWindow; } else return lRet; } } break;
case WM_PALETTECHANGED: if((HWND)wParam == hDlg) return(FALSE);
case WM_QUERYNEWPALETTE: HandlePaletteChange(ppd, uMessage, hDlg); return TRUE;
case WM_DESTROY: // Clean up subclass
RemoveWindowSubclass(hDlg, WizardWndProc, 0); break;
default: break; }
return DefSubclassProc(hDlg, uMessage, wParam, lParam); }
//
// EnumResLangProc
//
// purpose: a callback function for EnumResourceLanguages().
// look into the type passed in and if it is RT_DIALOG
// copy the lang of the first resource to our buffer
// this also counts # of lang if more than one of them
// are passed in
//
//
typedef struct { WORD wLang; BOOL fFoundLang; LPCTSTR lpszType; } ENUMLANGDATA;
BOOL CALLBACK EnumResLangProc(HINSTANCE hinst, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIdLang, LPARAM lparam) { ENUMLANGDATA *pel = (ENUMLANGDATA *)lparam; BOOL fContinue = TRUE;
ASSERT(pel);
if (lpszType == pel->lpszType) { // When comctl's been initialized with a particular MUI language,
// we pass in the langid to GetPageLanguage(), then it's given to this proc.
// we want to look for a template that matches to the langid,
// and if it's not found, we have to use the first instance of templates.
//
if (pel->wLang == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) || (pel->wLang == wIdLang)) { pel->wLang = wIdLang; pel->fFoundLang = TRUE; fContinue = FALSE; } } return fContinue; // continue until we get langs...
}
// GetPageLanguage
//
// purpose: tries to retrieve language information out of
// given page's dialog template. We get the first language
// in which the template is localized in.
// currently doesn't support PSP_DLGINDIRECT case
//
// we luck out with browselc since there's only one lang per resid,
// we should cache the langid we loaded up front and pull it out here.
//
HRESULT GetPageLanguage(PISP pisp, WORD *pwLang) { if (pisp && pwLang) { if (pisp->_psp.dwFlags & PSP_DLGINDIRECT) { // try something other than dialog
return E_FAIL; // not supported yet.
} else { ENUMLANGDATA el; // the caller passes-in the langid with which we're initialized
//
el.wLang = *pwLang; el.fFoundLang = FALSE; el.lpszType = RT_DIALOG; // check with the dialog template specified
EnumResourceLanguages(pisp->_psp.hInstance, RT_DIALOG, pisp->_psp.P_pszTemplate, EnumResLangProc, (LPARAM)&el); if (!el.fFoundLang) { // we couldn't find a matching lang in the given page's resource
// so we'll take the first one
el.wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); // it doesn't matter if this fails, because we'll then end up with
// the neutral langid, which is the best guess here after failing
// to get any page lang.
//
EnumResourceLanguages(pisp->_psp.hInstance, RT_DIALOG, pisp->_psp.P_pszTemplate, EnumResLangProc, (LPARAM)&el); } *pwLang = el.wLang; } return S_OK; } return E_FAIL; }
//
// FindResourceExRetry
//
// Just like FindResourceEx, except that if we can't find the resource,
// we try again with MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL).
//
HRSRC FindResourceExRetry(HMODULE hmod, LPCTSTR lpType, LPCTSTR lpName, WORD wLang) { HRSRC hrsrc = FindResourceEx(hmod, lpType, lpName, wLang);
// if failed because we couldn't find the resouce in requested lang
// and requested lang wasn't neutral, then try neutral.
if (!hrsrc && wLang != MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)) { wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); hrsrc = FindResourceEx(hmod, lpType, lpName, wLang); }
return hrsrc; }
WORD GetShellResourceLangID(void);
// NT5_GetUserDefaultUILanguage
//
// NT5 has a new function GetUserDefaultUILanguage which returns the
// language the user as selected for UI.
//
// If the function is not available (e.g., NT4), then use the
// shell resource language ID.
//
typedef LANGID (CALLBACK* GETUSERDEFAULTUILANGUAGE)(void);
GETUSERDEFAULTUILANGUAGE _GetUserDefaultUILanguage;
LANGID NT5_GetUserDefaultUILanguage(void) { if (_GetUserDefaultUILanguage == NULL) { HMODULE hmod = GetModuleHandle(TEXT("KERNEL32"));
//
// Must keep in a local to avoid thread races.
//
GETUSERDEFAULTUILANGUAGE pfn = NULL;
if (hmod) pfn = (GETUSERDEFAULTUILANGUAGE) GetProcAddress(hmod, "GetUserDefaultUILanguage");
//
// If function is not available, then use our fallback
//
if (pfn == NULL) pfn = GetShellResourceLangID;
ASSERT(pfn != NULL); _GetUserDefaultUILanguage = pfn; }
return _GetUserDefaultUILanguage(); }
LCID CCGetSystemDefaultThreadLocale(LCID iLcidThreadOrig) { UINT uLangThread, uLangThreadOrig;
uLangThreadOrig = LANGIDFROMLCID(iLcidThreadOrig);
// uLangThread is the language we think we want to use
uLangThread = uLangThreadOrig;
if (staticIsOS(OS_NT4ORGREATER) && !staticIsOS(OS_WIN2000ORGREATER)) { int iLcidUserDefault = GetUserDefaultLCID(); UINT uLangUD = LANGIDFROMLCID(iLcidUserDefault);
//
// If we are running on Enabled Arabic NT4, we should always
// display the US English resources (since the UI is English), however NT4
// Resource Loader will look for the current Thread Locale (which is Arabic).
// This is no problem in NT5 since the Resource Loader will check for
// the UI Language (newly introduced) when loading such resources. To
// fix this, we will change the thread locale to US English
// and restore it back to Arabic/Hebrew if we are running on an Enabled Arabic/Hebrew NT4.
// The check is done to make sure we are running within a Araic/Hebrew user locale
// and the thread locale is still Arabic/Hebrew (i.e. nobody tried to SetThreadLocale).
// [samera]
//
if( ((PRIMARYLANGID(uLangUD ) == LANG_ARABIC) && (PRIMARYLANGID(uLangThread) == LANG_ARABIC)) || ((PRIMARYLANGID(uLangUD ) == LANG_HEBREW) && (PRIMARYLANGID(uLangThread) == LANG_HEBREW))) { uLangThread = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); } }
//
// Make locale match UI locale if not otherwise overridden.
//
if (uLangThread == uLangThreadOrig) { uLangThread = NT5_GetUserDefaultUILanguage(); }
//
// Now see if we actually changed the thread language.
//
if (uLangThread == uLangThreadOrig) { // No change, return the original locale, including sort stuff
return iLcidThreadOrig; } else { // It changed, return a generic sort order, since we don't use
// this information for sorting.
return MAKELCID(uLangThread, SORT_DEFAULT); } }
//
// GetAltFontLangId
//
// used to detect "MS UI Gothic" on Jpn localized non NT5 platforms
// the font is shipped with IE5 for the language but comctl can't
// always assume the font so we have a fake sublang id assigned to
// the secondary resource file for the language
//
int CALLBACK FontEnumProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, int FontType, LPARAM lParam ) { if (lParam) { *(BOOL *)lParam = TRUE; } return 0; // stop at the first callback
} UINT GetDefaultCharsetFromLang(LANGID wLang) { TCHAR szData[6+1]; // 6 chars are max allowed for this lctype
UINT uiRet = DEFAULT_CHARSET;
// JPN hack here: GetLocaleInfo() DOES return > 0 for Jpn altfont langid,
// but doesn't get us any useful info. So for JPN, we ripout the SUBLANG
// portion of id. we can't do this for other langs since sublang can affect
// charset (ex. chinese)
//
if(PRIMARYLANGID(wLang) == LANG_JAPANESE) wLang = MAKELANGID(PRIMARYLANGID(wLang), SUBLANG_NEUTRAL); if (GetLocaleInfo(MAKELCID(wLang, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, szData, ARRAYSIZE(szData)) > 0) {
UINT uiCp = StrToInt(szData); CHARSETINFO csinfo;
if (TranslateCharsetInfo(IntToPtr_(DWORD *, uiCp), &csinfo, TCI_SRCCODEPAGE)) uiRet = csinfo.ciCharset; }
return uiRet; } BOOL IsFontInstalled(LANGID wLang, LPCTSTR szFace) { BOOL fInstalled = FALSE; HDC hdc; LOGFONT lf = {0};
StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), szFace); // retrieve charset from given language
lf.lfCharSet = (BYTE)GetDefaultCharsetFromLang(wLang); // then see if we can enumrate the font
hdc = GetDC(NULL); if (hdc) { EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROC)FontEnumProc, (LPARAM)&fInstalled, 0); ReleaseDC(NULL, hdc); }
return fInstalled; }
LANGID GetAltFontLangId(LANGID wLang) { LPCTSTR pszTypeFace = NULL; USHORT usAltSubLang = SUBLANG_NEUTRAL; const static TCHAR s_szUIGothic[] = TEXT("MS UI Gothic"); static int iPrimaryFontInstalled = -1;
// most of the case we return the lang just as is
switch(PRIMARYLANGID(wLang)) { case LANG_JAPANESE: pszTypeFace = s_szUIGothic; usAltSubLang = SUBLANG_JAPANESE_ALTFONT; break; // add code here to handle any other cases like Jpn
default: return wLang; }
// check existence of the font if we haven't
if (iPrimaryFontInstalled < 0 && pszTypeFace) { iPrimaryFontInstalled = IsFontInstalled(wLang, pszTypeFace); }
// return secondary lang id if our alternative font *is* installed
if (iPrimaryFontInstalled == 1) wLang = MAKELANGID(PRIMARYLANGID(wLang), usAltSubLang);
return wLang; } // GetShellResourceLangID
//
// On NT4, we want to match our ML resource to the one that OS is localized.
// this is to prevent general UI (buttons) from changing along with regional
// setting change.
// Win95 won't change system default locale, NT5 will load from matching satelite
// resource dll automatically so this won't be needed on these platforms.
// This function finds shell32.dll and gets the language in which the dll is
// localized, then cache the lcid so we won't have to detect it again.
//
WORD GetShellResourceLangID(void) { static WORD langRes = 0L;
// we do this only once
if (langRes == 0L) { HINSTANCE hinstShell; ENUMLANGDATA el = {MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), FALSE, RT_DIALOG};
hinstShell = LoadLibrary(TEXT("shell32.dll")); if (hinstShell) { EnumResourceLanguages(hinstShell, RT_DIALOG, MAKEINTRESOURCE(DLG_EXITWINDOWS), EnumResLangProc, (LPARAM)&el);
FreeLibrary(hinstShell); }
if (PRIMARYLANGID(el.wLang) == LANG_CHINESE || PRIMARYLANGID(el.wLang) == LANG_PORTUGUESE ) { // these two languages need special handling
langRes = el.wLang; } else { // otherwise we use only primary langid.
langRes = MAKELANGID(PRIMARYLANGID(el.wLang), SUBLANG_NEUTRAL); } } return langRes; }
//
// CCGetProperThreadLocale
//
// This function computes its brains out and tries to decide
// which thread locale we should use for our UI components.
//
// Returns the desired locale.
//
// Adjustment - For Arabic / Hebrew - NT4 Only
//
// Converts the thread locale to US, so that neutral resources
// loaded by the thread will be the US-English one, if available.
// This is used when the locale is Arabic/Hebrew and the system is
// NT4 enabled ( There was no localized NT4), as a result we need
// always to see the English resources on NT4 Arabic/Hebrew.
// [samera]
//
// Adjustment - For all languages - NT4 Only
//
// Convert the thread locale to the shell locale if not otherwise
// altered by previous adjustments.
//
// Adjustment - For all languages - NT5 Only
//
// Always use the default UI language. If that fails, then use the
// shell locale.
//
// The last two adjustments are handled in a common function, because
// the NT5 fallback turns out to be equal to the NT4 algorithm.
//
LCID CCGetProperThreadLocale(OPTIONAL LCID *plcidPrev) { LANGID uLangAlt, uLangMUI; LCID lcidRet, iLcidThreadOrig;
iLcidThreadOrig = GetThreadLocale(); if (plcidPrev) *plcidPrev = iLcidThreadOrig;
uLangMUI = GetMUILanguage(); if ( uLangMUI == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)) { // return adjusted system default locale if MUI isn't initialized
//
lcidRet = CCGetSystemDefaultThreadLocale(iLcidThreadOrig); } else { // our host has initialized us with prefered MUI language
//
lcidRet = MAKELCID(uLangMUI, SORT_DEFAULT); }
uLangAlt = GetAltFontLangId(LANGIDFROMLCID(lcidRet)); if (uLangAlt != LANGIDFROMLCID(lcidRet)) { // use secondary resource for the language
// if the platform *does* have the alternative font
lcidRet = MAKELCID(uLangAlt, SORTIDFROMLCID(lcidRet)); } return lcidRet; }
//
// CCLoadStringEx
//
// Just like LoadString, except you can specify the language, too.
//
// This is harder than you think, because NT5 changed the way strings
// are loaded. Quote:
//
// We changed the resource loader in NT5, to only load resources
// in the language of the thread locale, if the thread locale is
// different to the user locale. The reasoning behind this was
// the "random" loading of the language of the user locale in
// the UI. This breaks if you do a SetThreadLocale to the User
// Locale, because then the whole step is ignored and the
// InstallLanguage of the system is loaded.
//
// Therefore, we have to use FindResourceEx.
//
//
int CCLoadStringEx(UINT uID, LPWSTR lpBuffer, int nBufferMax, WORD wLang) { return CCLoadStringExInternal(HINST_THISDLL, uID, lpBuffer, nBufferMax, wLang); }
int CCLoadStringExInternal(HINSTANCE hInst, UINT uID, LPWSTR lpBuffer, int nBufferMax, WORD wLang) { PWCHAR pwch; HRSRC hrsrc; int cwch = 0;
if (nBufferMax <= 0) return 0; // sanity check
/*
* String tables are broken up into "bundles" of 16 strings each. */
hrsrc = FindResourceExRetry(hInst, RT_STRING, (LPCTSTR)(LONG_PTR)(1 + (USHORT)uID / 16), wLang); if (hrsrc) { pwch = (PWCHAR)LoadResource(hInst, hrsrc); if (pwch) { /*
* Now skip over the strings in the resource until we * hit the one we want. Each entry is a counted string, * just like Pascal. */ for (uID %= 16; uID; uID--) { pwch += *pwch + 1; } cwch = min(*pwch, nBufferMax - 1); memcpy(lpBuffer, pwch+1, cwch * sizeof(WCHAR)); /* Copy the goo */ } } lpBuffer[cwch] = L'\0'; /* Terminate the string */ return cwch; }
//
// LocalizedLoadString
//
// Loads a string from our resources, using the correct language.
//
int LocalizedLoadString(UINT uID, LPWSTR lpBuffer, int nBufferMax) { return CCLoadStringEx(uID, lpBuffer, nBufferMax, LANGIDFROMLCID(CCGetProperThreadLocale(NULL))); }
//
// Determine if the prop sheet frame should use the new
// "MS Shell Dlg 2" font. To do this, we examine each page's dlg template.
// If all pages have SHELLFONT enabled, then
// we want to use the new font.
//
BOOL ShouldUseMSShellDlg2Font(LPPROPDATA ppd) { UINT iPage; PAGEINFOEX pi; LANGID langidMUI;
if (!staticIsOS(OS_WIN2000ORGREATER)) return FALSE;
langidMUI = GetMUILanguage(); for (iPage = 0; iPage < ppd->psh.nPages; iPage++) { if (GetPageInfoEx(ppd, GETPISP(ppd, iPage), &pi, langidMUI, GPI_DIALOGEX)) { if (!IsPageInfoSHELLFONT(&pi)) { return FALSE; } } } return TRUE; }
PSPT_OS Prsht_GetOS() { static PSPT_OS pspt_os = (PSPT_OS)-1; int iIsOSBiDiEnabled = 0; if (pspt_os != (PSPT_OS)-1) { return pspt_os; }
iIsOSBiDiEnabled = GetSystemMetrics(SM_MIDEASTENABLED); if (staticIsOS(OS_WIN2000ORGREATER)) { pspt_os = PSPT_OS_WINNT5; } else if (iIsOSBiDiEnabled && staticIsOS(OS_NT4ORGREATER) && (!staticIsOS(OS_WIN2000ORGREATER))) { pspt_os = PSPT_OS_WINNT4_ENA; } else if (iIsOSBiDiEnabled && staticIsOS(OS_WIN95ORGREATER) && (!staticIsOS(OS_WIN98))) { pspt_os = PSPT_OS_WIN95_BIDI; } else if (iIsOSBiDiEnabled && staticIsOS(OS_WIN98)) { pspt_os = PSPT_OS_WIN98_BIDI; } else { pspt_os = PSPT_OS_OTHER; }
return pspt_os; }
PSPT_OVERRIDE Prsht_GetOverrideState(LPPROPDATA ppd) { // if passed bad argument, assume no override
if(!ppd) return PSPT_OVERRIDE_NOOVERRIDE; if (ppd->psh.dwFlags & PSH_USEPAGELANG) return PSPT_OVERRIDE_USEPAGELANG;
return PSPT_OVERRIDE_NOOVERRIDE; }
PSPT_TYPE Prsht_GetType(LPPROPDATA ppd, WORD wLang) {
PISP pisp = NULL; // if passed bad argument, give it the english resources
if(!ppd) return PSPT_TYPE_ENGLISH;
pisp = GETPISP(ppd, 0); if(pisp) { PAGEINFOEX pi = {0};
if ((IS_PROCESS_RTL_MIRRORED()) || (GetPageInfoEx(ppd, pisp, &pi, wLang, GPI_BMIRROR) && pi.bMirrored)) return PSPT_TYPE_MIRRORED;
else { WORD wLang = LANGIDFROMLCID(CCGetProperThreadLocale(NULL)); GetPageLanguage(pisp,&wLang); if((PRIMARYLANGID(wLang) == LANG_ARABIC) || (PRIMARYLANGID(wLang) == LANG_HEBREW)) return PSPT_TYPE_ENABLED; } }
return PSPT_TYPE_ENGLISH; }
PSPT_ACTION Prsht_GetAction(PSPT_TYPE pspt_type, PSPT_OS pspt_os, PSPT_OVERRIDE pspt_override) { if ((pspt_type < 0) || (pspt_type >= PSPT_TYPE_MAX) || (pspt_os < 0) || (pspt_os >= PSPT_OS_MAX) || (pspt_override < 0) || (pspt_override >= PSPT_OVERRIDE_MAX)) return PSPT_ACTION_NOACTION;
return g_PSPT_Action[pspt_type][pspt_os][pspt_override];
}
void Prsht_PrepareTemplate(LPPROPDATA ppd, HINSTANCE hInst, HGLOBAL *phDlgTemplate, HRSRC *phResInfo, LPCSTR lpName, HWND hWndOwner, LPWORD lpwLangID) {
LPDLGTEMPLATE pDlgTemplate = NULL; PSPT_ACTION pspt_action;
if (pDlgTemplate = (LPDLGTEMPLATE)LockResource(*phDlgTemplate)) {
// We save BiDi templates as DIALOG (not DIALOGEX)
// If we got an extended template then it is not ours
if (((LPDLGTEMPLATEEX)pDlgTemplate)->wSignature == 0xFFFF) return;
// Cut it short to save time
//
if (!(pDlgTemplate->dwExtendedStyle & (RTL_MIRRORED_WINDOW | RTL_NOINHERITLAYOUT))) return; }
pspt_action = Prsht_GetAction(Prsht_GetType(ppd, *lpwLangID), Prsht_GetOS(), Prsht_GetOverrideState(ppd)); switch(pspt_action) { case PSPT_ACTION_NOACTION: return;
case PSPT_ACTION_NOMIRRORING: { if (pDlgTemplate) { EditBiDiDLGTemplate(pDlgTemplate, EBDT_NOMIRROR, NULL, 0); } } break;
case PSPT_ACTION_FLIP: { if (pDlgTemplate) { EditBiDiDLGTemplate(pDlgTemplate, EBDT_NOMIRROR, NULL, 0); EditBiDiDLGTemplate(pDlgTemplate, EBDT_FLIP, (PWORD)&wIgnoreIDs, ARRAYSIZE(wIgnoreIDs)); ppd->fFlipped = TRUE; } } break;
case PSPT_ACTION_LOADENGLISH: { HGLOBAL hDlgTemplateTemp = NULL; HRSRC hResInfoTemp;
//
//Try to load an English resource.
//
*lpwLangID = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
if ((hResInfoTemp = FindResourceExA( hInst, (LPCSTR)RT_DIALOG, lpName, *lpwLangID))) { hDlgTemplateTemp = LoadResource(hInst, hResInfoTemp); } if (hDlgTemplateTemp) { //
//And return it to the caller to use it.
// Since we loaeded a new template, we should copy it to a local memory
// in case there is a callback.
//
DWORD cbTemplate = SizeofResource(hInst, hResInfoTemp); LPVOID pTemplateMod;
pTemplateMod = (LPVOID)LocalAlloc(LPTR, cbTemplate * 2); if (pTemplateMod) { memmove(pTemplateMod, hDlgTemplateTemp, cbTemplate); LocalFree(*phDlgTemplate); *phResInfo = hResInfoTemp; *phDlgTemplate = pTemplateMod; } }
} break;
case PSPT_ACTION_WIN9XCOMPAT: { if (pDlgTemplate) { pDlgTemplate->style |= DS_BIDI_RTL; } } } }
INT_PTR _RealPropertySheet(LPPROPDATA ppd) { HWND hwndMain; MSG32 msg32; HWND hwndTopOwner; int nReturn = -1; HWND hwndOriginalFocus; WORD wLang, wUserLang; LCID iLcidThread=0L; HRSRC hrsrc = 0; LPVOID pTemplate, pTemplateMod; LPTSTR lpDlgId; if (ppd->psh.nPages == 0) { DebugMsg(DM_ERROR, TEXT("no pages for prop sheet")); goto FreePpdAndReturn; }
ppd->hwndCurPage = NULL; ppd->nReturn = -1; ppd->nRestart = 0;
hwndTopOwner = ppd->psh.hwndParent; hwndOriginalFocus = GetFocus();
#ifdef DEBUG
if (GetAsyncKeyState(VK_CONTROL) < 0) {
ppd->psh.dwFlags |= PSH_WIZARDHASFINISH; } #endif
if (!(ppd->psh.dwFlags & PSH_MODELESS)) { //
// Like dialog boxes, we only want to disable top level windows.
// NB The mail guys would like us to be more like a regular
// dialog box and disable the parent before putting up the sheet.
if (hwndTopOwner) { while (GetWindowLong(hwndTopOwner, GWL_STYLE) & WS_CHILD) hwndTopOwner = GetParent(hwndTopOwner);
ASSERT(hwndTopOwner); // Should never get this!
if ((hwndTopOwner == GetDesktopWindow()) || (EnableWindow(hwndTopOwner, FALSE))) { //
// If the window was the desktop window, then don't disable
// it now and don't reenable it later.
// Also, if the window was already disabled, then don't
// enable it later.
//
hwndTopOwner = NULL; } } }
//
// WARNING! WARNING! WARNING! WARNING!
//
// Before you mess with any language stuff, be aware that MFC loads
// resources directly out of comctl32.dll, so if you change the
// way we choose the proper resource, you may break MFC apps.
// See NT bug 302959.
//
// Support PSH_USEPAGELANG
//
// Presume we load our template based on thread lang id.
wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); wUserLang= MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
if (ppd->psh.dwFlags & PSH_USEPAGELANG) { // Get callers language version. We know we have at least one page
if (FAILED(GetPageLanguage(GETPISP(ppd, 0), &wLang))) { // failed to get langid out of caller's resource
// just pretend nothing happened.
wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); } wUserLang = wLang; } else wLang = LANGIDFROMLCID(CCGetProperThreadLocale(NULL));
//
// The only thing we need the thread locale for is to locate the
// correct dialog template. We don't want it to affect page
// initialization or anything else like that, so get the template
// and quickly set the locale back before anyone notices.
//
// If we can't get the requested language, retry with the neutral
// language.
//
// We have seperate dialog templates for Win95 BiDi localized
// The code used to check to see if we are running on Win98 BiDi localized
// and load this template.
// We have a special case when running Office2000 with Arabic/Hebrew SKU on
// BiDi win95 Enabled where we need to load this template as well
if(Prsht_GetOS() == PSPT_OS_WIN95_BIDI) { lpDlgId = MAKEINTRESOURCE(IS_WIZARD(ppd) ? DLG_WIZARD95 : DLG_PROPSHEET95); hrsrc = FindResourceEx( HINST_THISDLL, RT_DIALOG, lpDlgId, wLang ); // we only have DLG_WIZARD95 and DLG_PROPSHEET95 in Arabic & Hebrew language
// if we got any other language we will fail
// In this case, let's use the normal templates
if(hrsrc) { ppd->fFlipped = TRUE; } else { lpDlgId = MAKEINTRESOURCE(IS_WIZARD(ppd) ? DLG_WIZARD : DLG_PROPSHEET); hrsrc = FindResourceExRetry( HINST_THISDLL, RT_DIALOG, lpDlgId, wLang ); } } else { lpDlgId = MAKEINTRESOURCE(IS_WIZARD(ppd) ? DLG_WIZARD : DLG_PROPSHEET);
hrsrc = FindResourceExRetry( HINST_THISDLL, RT_DIALOG, lpDlgId, wLang ); } // Setup for failure
hwndMain = NULL;
if (hrsrc && (pTemplate = (LPVOID)LoadResource(HINST_THISDLL, hrsrc))) { DWORD cbTemplate;
cbTemplate = SizeofResource(HINST_THISDLL, hrsrc);
pTemplateMod = (LPVOID)LocalAlloc(LPTR, cbTemplate * 2); //double it to give some play leeway
if (pTemplateMod) { hmemcpy(pTemplateMod, pTemplate, cbTemplate); //Check the direction of this dialog and change it if it does not match the owner.
Prsht_PrepareTemplate(ppd, HINST_THISDLL, &pTemplateMod, (HRSRC *)&hrsrc, (LPSTR)lpDlgId,ppd->psh.hwndParent, &wUserLang); } else { pTemplateMod = pTemplate; // no modifications
}
//
// Template editing and callbacks happen only if we were able
// to create a copy for modifying.
//
if (pTemplateMod != pTemplate) { if (ppd->psh.dwFlags & PSH_NOCONTEXTHELP) { if (((LPDLGTEMPLATEEX)pTemplateMod)->wSignature == 0xFFFF){ ((LPDLGTEMPLATEEX)pTemplateMod)->dwStyle &= ~DS_CONTEXTHELP; } else { ((LPDLGTEMPLATE)pTemplateMod)->style &= ~DS_CONTEXTHELP; } }
if (IS_WIZARD(ppd) && (ppd->psh.dwFlags & PSH_WIZARDCONTEXTHELP)) {
if (((LPDLGTEMPLATEEX)pTemplateMod)->wSignature == 0xFFFF){ ((LPDLGTEMPLATEEX)pTemplateMod)->dwStyle |= DS_CONTEXTHELP; } else { ((LPDLGTEMPLATE)pTemplateMod)->style |= DS_CONTEXTHELP; } }
// extra check for PSH_USEPAGELANG case
if (ppd->psh.pfnCallback) { #ifdef WX86
if (ppd->fFlags & PD_WX86) Wx86Callback(ppd->psh.pfnCallback, NULL, PSCB_PRECREATE, (LPARAM)(LPVOID)pTemplateMod); else #endif
ppd->psh.pfnCallback(NULL, PSCB_PRECREATE, (LPARAM)(LPVOID)pTemplateMod); } }
if (pTemplateMod) { //
// For NT, we want to use MS Shell Dlg 2 font in the prop sheet if
// all of the pages in the sheet use MS Shell Dlg 2.
// To do this, we ensure the template is DIALOGEX and that the
// DS_SHELLFONT style bits (DS_SHELLFONT | DS_FIXEDSYS) are set.
//
if (ShouldUseMSShellDlg2Font(ppd)) { if (((LPDLGTEMPLATEEX)pTemplateMod)->wSignature != 0xFFFF) { //
// Convert DLGTEMPLATE to DLGTEMPLATEEX.
//
LPVOID pTemplateCvtEx; int iCharset = GetDefaultCharsetFromLang(wLang); if (SUCCEEDED(CvtDlgToDlgEx(pTemplateMod, (LPDLGTEMPLATEEX *)&pTemplateCvtEx, iCharset))) { LocalFree(pTemplateMod); pTemplateMod = pTemplateCvtEx; } else { // Unable to convert to ShellFont; oh well
goto NotShellFont; } } //
// Set DS_SHELLFONT style bits so we get "MS Shell Dlg2" font.
//
((LPDLGTEMPLATEEX)pTemplateMod)->dwStyle |= DS_SHELLFONT; ppd->fFlags |= PD_SHELLFONT; NotShellFont:; }
// pTemplateMod is always unicode, even for the A function - no need to thunk
hwndMain = CreateDialogIndirectParam(HINST_THISDLL, pTemplateMod, ppd->psh.hwndParent, PropSheetDlgProc, (LPARAM)(LPPROPDATA)ppd);
// WORK AROUND WOW/USER BUG: Even though InitPropSheetDlg sets
// ppd->hDlg, in the WOW scenario, the incoming hDlg is WRONG!
// The USER guys say "Tough. You have to work around it."
ppd->hDlg = hwndMain; } if (pTemplateMod != pTemplate) LocalFree(pTemplateMod); }
if (!hwndMain) { int iPage;
DebugMsg(DM_ERROR, TEXT("PropertySheet: unable to create main dialog"));
if (hwndTopOwner && !(ppd->psh.dwFlags & PSH_MODELESS)) EnableWindow(hwndTopOwner, TRUE);
// Release all page objects in REVERSE ORDER so we can have
// pages that are dependant on eachother based on the initial
// order of those pages
//
for (iPage = (int)ppd->psh.nPages - 1; iPage >= 0; iPage--) { ULONG_PTR dw = PropPageActivateContext(ppd, GETPISP(ppd, iPage)); DestroyPropertySheetPage(GETHPAGE(ppd, iPage)); PropPageDeactivateContext(dw); }
goto FreePpdAndReturn; }
if (ppd->psh.dwFlags & PSH_MODELESS) return (INT_PTR)hwndMain;
while( ppd->hwndCurPage && GetMessage32(&msg32, NULL, 0, 0, TRUE) ) { // if (PropSheet_IsDialogMessage(ppd->hDlg, (LPMSG)&msg32))
if (Prop_IsDialogMessage(ppd, &msg32)) continue;
TranslateMessage32(&msg32, TRUE); DispatchMessage32(&msg32, TRUE); }
if( ppd->hwndCurPage ) { // GetMessage returned FALSE (WM_QUIT)
DebugMsg( DM_TRACE, TEXT("PropertySheet: bailing in response to WM_QUIT (and reposting quit)") ); ButtonPushed( ppd, IDCANCEL ); // nuke ourselves
PostQuitMessage( (int) msg32.wParam ); // repost quit for next enclosing loop
}
// don't let this get mangled during destroy processing
nReturn = ppd->nReturn ;
if (ppd->psh.hwndParent && (GetActiveWindow() == hwndMain)) { DebugMsg(DM_TRACE, TEXT("Passing activation up")); SetActiveWindow(ppd->psh.hwndParent); }
if (hwndTopOwner) EnableWindow(hwndTopOwner, TRUE);
if (IsWindow(hwndOriginalFocus)) { SetFocus(hwndOriginalFocus); }
DestroyWindow(hwndMain);
// do pickup any PSM_REBOOTSYSTEM or PSM_RESTARTWINDOWS sent during destroy
if ((nReturn > 0) && ppd->nRestart) nReturn = ppd->nRestart;
FreePpdAndReturn: LocalFree((HLOCAL)ppd); return nReturn; }
HPROPSHEETPAGE WINAPI CreateProxyPage(HPROPSHEETPAGE hpage16, HINSTANCE hinst16) { SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING); return NULL; }
// DestroyPropsheetPageArray
//
// Helper function used during error handling. It destroys the
// incoming property sheet pages.
void DestroyPropsheetPageArray(LPCPROPSHEETHEADER ppsh) { int iPage;
if (!(ppsh->dwFlags & PSH_PROPSHEETPAGE)) { // Release all page objects in REVERSE ORDER so we can have
// pages that are dependant on eachother based on the initial
// order of those pages
for (iPage = (int)ppsh->nPages - 1; iPage >= 0; iPage--) { DestroyPropertySheetPage(ppsh->H_phpage[iPage]); } } }
// PropertySheet API
//
// This function displays the property sheet described by ppsh.
//
// Since I don't expect anyone to ever check the return value
// (we certainly don't), we need to make sure any provided phpage array
// is always freed with DestroyPropertySheetPage, even if an error occurs.
//
//
// The fNeedShadow parameter means "The incoming LPCPROPSHEETHEADER is in the
// opposite character set from what you implement natively".
//
// If we are compiling UNICODE, then fNeedShadow is TRUE if the incoming
// LPCPROPSHEETHEADER is really an ANSI property sheet page.
//
// If we are compiling ANSI-only, then fNeedShadow is always FALSE because
// we don't support UNICODE in the ANSI-only version.
//
INT_PTR WINAPI _PropertySheet(LPCPROPSHEETHEADER ppsh, BOOL fNeedShadow) { PROPDATA *ppd; int iPage;
//
// validate header
//
ASSERT(IsValidPROPSHEETHEADERSIZE(sizeof(PROPSHEETHEADER)));
if (!IsValidPROPSHEETHEADERSIZE(ppsh->dwSize)) { DebugMsg( DM_ERROR, TEXT("PropertySheet: dwSize is not correct") ); goto invalid_call; }
if (ppsh->dwFlags & ~PSH_ALL) { DebugMsg( DM_ERROR, TEXT("PropertySheet: invalid flags") ); goto invalid_call; }
if (ppsh->nPages >= MAXPROPPAGES) { DebugMsg( DM_ERROR, TEXT("PropertySheet: too many pages ( use MAXPROPPAGES )") ); goto invalid_call; }
ppd = (PROPDATA *)LocalAlloc(LPTR, sizeof(PROPDATA)); if (ppd == NULL) { DebugMsg(DM_ERROR, TEXT("failed to alloc property page data"));
invalid_call: DestroyPropsheetPageArray(ppsh); return -1; }
// Initialize the flags.
ppd->fFlags = FALSE;
#ifdef WX86
//
// If Wx86 is calling, set the flag that thunks the callbacks.
//
if ( Wx86IsCallThunked() ) { ppd->fFlags |= PD_WX86; } #endif
if (fNeedShadow) ppd->fFlags |= PD_NEEDSHADOW;
// make a copy of the header so we can party on it
hmemcpy(&ppd->psh, ppsh, ppsh->dwSize);
// so we don't have to check later...
if (!(ppd->psh.dwFlags & PSH_USECALLBACK)) ppd->psh.pfnCallback = NULL;
// fix up the page pointer to point to our copy of the page array
ppd->psh.H_phpage = ppd->rghpage;
GetCurrentActCtx(&ppd->hActCtxInit);
if (ppd->psh.dwFlags & PSH_PROPSHEETPAGE) { // for lazy clients convert PROPSHEETPAGE structures into page handles
LPCPROPSHEETPAGE ppsp = ppsh->H_ppsp;
for (iPage = 0; iPage < (int)ppd->psh.nPages; iPage++) { ppd->psh.H_phpage[iPage] = _CreatePropertySheetPage(ppsp, fNeedShadow, ppd->fFlags & PD_WX86); if (!ppd->psh.H_phpage[iPage]) { iPage--; ppd->psh.nPages--; }
ppsp = (LPCPROPSHEETPAGE)((LPBYTE)ppsp + ppsp->dwSize); // next PROPSHEETPAGE structure
} } else { // The UNICODE build needs to hack around Hijaak 95.
//
ppd->psh.nPages = 0; for (iPage = 0; iPage < (int)ppsh->nPages; iPage++) { ppd->psh.H_phpage[ppd->psh.nPages] = _Hijaak95Hack(ppd, ppsh->H_phpage[iPage]); if (ppd->psh.H_phpage[ppd->psh.nPages]) { ppd->psh.nPages++; } } }
//
// Everybody else assumes that the HPROPSHEETPAGEs have been
// internalized, so let's do that before anybody notices.
//
for (iPage = 0; iPage < (int)ppd->psh.nPages; iPage++) { SETPISP(ppd, iPage, InternalizeHPROPSHEETPAGE(ppd->psh.H_phpage[iPage])); }
//
// Walk all pages to see if any have help and if so, set the PSH_HASHELP
// flag in the header.
//
if (!(ppd->psh.dwFlags & PSH_HASHELP)) { for (iPage = 0; iPage < (int)ppd->psh.nPages; iPage++) { if (GETPPSP(ppd, iPage)->dwFlags & PSP_HASHELP) { ppd->psh.dwFlags |= PSH_HASHELP; break; } } }
return _RealPropertySheet(ppd); }
INT_PTR WINAPI PropertySheetW(LPCPROPSHEETHEADERW ppsh) { return _PropertySheet(ppsh, FALSE); }
INT_PTR WINAPI PropertySheetA(LPCPROPSHEETHEADERA ppsh) { PROPSHEETHEADERW pshW; INT_PTR iResult;
//
// Most validation is done by _PropertySheet, but we need
// to validate the header size, or we won't survive the thunk.
//
if (!IsValidPROPSHEETHEADERSIZE(ppsh->dwSize)) { DebugMsg( DM_ERROR, TEXT("PropertySheet: dwSize is not correct") ); goto Error; }
if (!ThunkPropSheetHeaderAtoW(ppsh, &pshW)) goto Error;
iResult = _PropertySheet(&pshW, TRUE);
FreePropSheetHeaderW(&pshW);
return iResult;
Error: DestroyPropsheetPageArray((LPCPROPSHEETHEADER)ppsh); return -1; }
//
// CopyPropertyPageStrings
//
// We have a PROPSHEETPAGE structure that contains pointers to strings.
// For each string, create a copy and smash the pointer-to-copy in the
// place where the original static pointer used to be.
//
// The method of copying varies depending on what kind of copy we want
// to make, so we use a callback procedure.
//
// UNICODE-to-UNICODE: StrDupW
// ANSI-to-UNICODE: StrDup_AtoW
// ANSI-to-ANSI: StrDupA
//
// On failure, all strings that did not get properly duplicated are set
// to NULL. You still have to call FreePropertyPageStrings to clear
// them out. Notice that when we fail to allocate, we merely make a note
// of the fact and continue onward. This ensures that all string fields
// are set to NULL if they could not be dup'd.
//
// ppsp - A pointer to either a PROPSHEETPAGEA or PROPSHEETPAGEW.
// The two structures are laid out identically, so it doesn't matter.
//
// pfnStrDup - function that will make the appropriate copy.
//
BOOL CopyPropertyPageStrings(LPPROPSHEETPAGE ppsp, STRDUPPROC pfnStrDup) { BOOL fSuccess = TRUE;
if (!(ppsp->dwFlags & PSP_DLGINDIRECT) && !IS_INTRESOURCE(ppsp->P_pszTemplate)) { ppsp->P_pszTemplate = pfnStrDup(ppsp->P_pszTemplate); if (!ppsp->P_pszTemplate) fSuccess = FALSE; }
if ((ppsp->dwFlags & PSP_USEICONID) && !IS_INTRESOURCE(ppsp->P_pszIcon)) { ppsp->P_pszIcon = pfnStrDup(ppsp->P_pszIcon); if (!ppsp->P_pszIcon) fSuccess = FALSE; }
if ((ppsp->dwFlags & PSP_USETITLE) && !IS_INTRESOURCE(ppsp->pszTitle)) { ppsp->pszTitle = pfnStrDup(ppsp->pszTitle); if (!ppsp->pszTitle) fSuccess = FALSE; }
if ((ppsp->dwFlags & PSP_USEHEADERTITLE) && !IS_INTRESOURCE(ppsp->pszHeaderTitle)) { ppsp->pszHeaderTitle = pfnStrDup(ppsp->pszHeaderTitle); if (!ppsp->pszHeaderTitle) fSuccess = FALSE; }
if ((ppsp->dwFlags & PSP_USEHEADERSUBTITLE) && !IS_INTRESOURCE(ppsp->pszHeaderSubTitle)) { ppsp->pszHeaderSubTitle = pfnStrDup(ppsp->pszHeaderSubTitle); if (!ppsp->pszHeaderSubTitle) fSuccess = FALSE; }
return fSuccess; }
//
// FreePropertyPageStrings
//
// Free the strings that live inside a property sheet page structure.
//
// ppsp - A pointer to either a PROPSHEETPAGEA or PROPSHEETPAGEW.
// The two structures are laid out identically, so it doesn't matter.
//
void FreePropertyPageStrings(LPCPROPSHEETPAGE ppsp) { if (!(ppsp->dwFlags & PSP_DLGINDIRECT) && !IS_INTRESOURCE(ppsp->P_pszTemplate)) LocalFree((LPVOID)ppsp->P_pszTemplate);
if ((ppsp->dwFlags & PSP_USEICONID) && !IS_INTRESOURCE(ppsp->P_pszIcon)) LocalFree((LPVOID)ppsp->P_pszIcon);
if ((ppsp->dwFlags & PSP_USETITLE) && !IS_INTRESOURCE(ppsp->pszTitle)) LocalFree((LPVOID)ppsp->pszTitle);
if ((ppsp->dwFlags & PSP_USEHEADERTITLE) && !IS_INTRESOURCE(ppsp->pszHeaderTitle)) LocalFree((LPVOID)ppsp->pszHeaderTitle);
if ((ppsp->dwFlags & PSP_USEHEADERSUBTITLE) && !IS_INTRESOURCE(ppsp->pszHeaderSubTitle)) LocalFree((LPVOID)ppsp->pszHeaderSubTitle); }
//*************************************************************
//
// ThunkPropSheetHeaderAtoW ()
//
// Purpose: Thunks the Ansi version of PROPSHEETHEADER to
// Unicode.
//
// Note that the H_phpage / H_ppsp field is not thunked.
// We'll deal with that separately.
//
//*************************************************************
BOOL ThunkPropSheetHeaderAtoW (LPCPROPSHEETHEADERA ppshA, LPPROPSHEETHEADERW ppsh) { //
// Deciding whether an item should be freed or not is tricky, so we
// keep a private array of all the pointers we've allocated, so we
// know what to free when we fail.
//
LPTSTR Alloced[5] = { 0 };
ASSERT(IsValidPROPSHEETHEADERSIZE(ppshA->dwSize));
hmemcpy(ppsh, ppshA, ppshA->dwSize);
ppsh->dwFlags |= PSH_THUNKED; if ((ppsh->dwFlags & PSH_USEICONID) && !IS_INTRESOURCE(ppsh->H_pszIcon)) { ppsh->H_pszIcon = Alloced[0] = StrDup_AtoW(ppsh->H_pszIcon); if (!ppsh->H_pszIcon) goto ExitIcon; }
if (!IS_WIZARDPSH(*ppsh) && !IS_INTRESOURCE(ppsh->pszCaption)) { ppsh->pszCaption = Alloced[1] = StrDup_AtoW(ppsh->pszCaption); if (!ppsh->pszCaption) goto ExitCaption; }
if ((ppsh->dwFlags & PSH_USEPSTARTPAGE) && !IS_INTRESOURCE(ppsh->H_pStartPage)) { ppsh->H_pStartPage = Alloced[2] = StrDup_AtoW(ppsh->H_pStartPage); if (!ppsh->H_pStartPage) goto ExitStartPage; }
if (ppsh->dwFlags & PSH_WIZARD97) { if ((ppsh->dwFlags & PSH_WATERMARK) && !(ppsh->dwFlags & PSH_USEHBMWATERMARK) && !IS_INTRESOURCE(ppsh->H_pszbmWatermark)) { ppsh->H_pszbmWatermark = Alloced[3] = StrDup_AtoW(ppsh->H_pszbmWatermark); if (!ppsh->H_pszbmWatermark) goto ExitWatermark; }
if ((ppsh->dwFlags & PSH_HEADER) && !(ppsh->dwFlags & PSH_USEHBMHEADER) && !IS_INTRESOURCE(ppsh->H_pszbmHeader)) { ppsh->H_pszbmHeader = Alloced[4] = StrDup_AtoW(ppsh->H_pszbmHeader); if (!ppsh->H_pszbmHeader) goto ExitHeader; } }
return TRUE;
ExitHeader: if (Alloced[3]) LocalFree(Alloced[3]); ExitWatermark: if (Alloced[2]) LocalFree(Alloced[2]); ExitStartPage: if (Alloced[1]) LocalFree(Alloced[1]); ExitCaption: if (Alloced[0]) LocalFree(Alloced[0]); ExitIcon: return FALSE; }
void FreePropSheetHeaderW(LPPROPSHEETHEADERW ppsh) { if ((ppsh->dwFlags & PSH_USEICONID) && !IS_INTRESOURCE(ppsh->H_pszIcon)) LocalFree((LPVOID)ppsh->H_pszIcon);
if (!IS_WIZARDPSH(*ppsh) && !IS_INTRESOURCE(ppsh->pszCaption)) LocalFree((LPVOID)ppsh->pszCaption);
if ((ppsh->dwFlags & PSH_USEPSTARTPAGE) && !IS_INTRESOURCE(ppsh->H_pStartPage)) LocalFree((LPVOID)ppsh->H_pStartPage);
if (ppsh->dwFlags & PSH_WIZARD97) { if ((ppsh->dwFlags & PSH_WATERMARK) && !(ppsh->dwFlags & PSH_USEHBMWATERMARK) && !IS_INTRESOURCE(ppsh->H_pszbmWatermark)) LocalFree((LPVOID)ppsh->H_pszbmWatermark);
if ((ppsh->dwFlags & PSH_HEADER) && !(ppsh->dwFlags & PSH_USEHBMHEADER) && !IS_INTRESOURCE(ppsh->H_pszbmHeader)) LocalFree((LPVOID)ppsh->H_pszbmHeader); } }
|