Windows2000/private/shell/win16/commctrl/prsht.c

2408 lines
66 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
#include "ctlspriv.h"
#include <prsht.h>
#ifndef IEWIN31
#include <commctrl.h> // For the hotkey hack.
#endif
#include <prshtp.h>
#include "..\inc\help.h" // Help IDs
#ifndef WIN31
#define g_cxSmIcon GetSystemMetrics(SM_CXSMICON)
#define g_cySmIcon GetSystemMetrics(SM_CYSMICON)
#endif
#define FLAG_CHANGED 0x0001
LPVOID WINAPI MapSLFix(HANDLE);
VOID WINAPI UnMapSLFixArray(int, HANDLE *);
// to avoid warnings....
#ifdef WIN32
#define HWNDTOLONG(hwnd) (LONG)(hwnd)
#else
#define HWNDTOLONG(hwnd) MAKELONG(hwnd,0)
#endif
#define RECTWIDTH(rc) (rc.right - rc.left)
#define RECTHEIGHT(rc) (rc.bottom - rc.top)
#ifdef WIN31
LRESULT CALLBACK Win31PropPageWndProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam);
LRESULT NEAR PASCAL Win31OnCtlColor(HWND hDlg, HDC hdcChild, HWND hwndChild, int nCtlType);
BOOL NEAR PASCAL Win31MakeDlgNonBold(HWND hDlg);
BOOL FAR PASCAL Win31IsKeyMessage(HWND hwndDlg, LPMSG lpmsg);
VOID NEAR PASCAL CenterDialog ( HWND hdlg );
void NEAR PASCAL Win31RepositionDlg( HWND hDlg );
void FAR PASCAL RemoveDefaultButton(HWND hwndRoot, HWND hwndStart);
const char g_szNonBoldFont[] = "DS_3DLOOK";
const char g_szLoSubclass[] = "MS_LO_SUBCLASS";
const char g_szHiSubclass[] = "MS_HI_SUBCLASS";
#endif
#if !defined(WIN32) && !defined(WIN31)
#ifdef FE_IME
typedef void *PVOID;
DWORD WINAPI GetCurrentThreadID(VOID);
BOOL WINAPI CreateImmTLink(DWORD);
#endif
#endif
void NEAR PASCAL ResetWizButtons(LPPROPDATA ppd);
typedef struct // tie
{
TC_ITEMHEADER tci;
HWND hwndPage;
UINT state;
} TC_ITEMEXTRA;
#define CB_ITEMEXTRA (sizeof(TC_ITEMEXTRA) - sizeof(TC_ITEMHEADER))
void NEAR PASCAL PageChange(LPPROPDATA ppd, int iAutoAdj);
HWND WINAPI CreatePage(PSP FAR *hpage, HWND hwndParent);
BOOL WINAPI GetPageInfo(PSP FAR *hpage, LPSTR pszCaption, int cbCaption, LPPOINT ppt, HICON FAR *hIcon);;
// 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.
#pragma data_seg(DATASEG_READONLY)
static int IDs[] = {IDD_APPLYNOW, IDOK, IDCANCEL, IDHELP};
static int WizIDs[] = {IDD_BACK, IDD_NEXT, IDD_FINISH, IDCANCEL, IDHELP};
#pragma data_seg()
void NEAR PASCAL _SetTitle(HWND hDlg, LPPROPDATA ppd)
{
char szFormat[50];
char szTitle[128];
char szTemp[128 + 50];
LPCSTR pCaption = ppd->psh.pszCaption;
if (HIWORD(pCaption) == 0) {
LoadString(ppd->psh.hInstance, (UINT)LOWORD(pCaption), szTitle, sizeof(szTitle));
pCaption = (LPCSTR)szTitle;
}
if (ppd->psh.dwFlags & PSH_PROPTITLE) {
LoadString(HINST_THISDLL, IDS_PROPERTIESFOR, szFormat, sizeof(szFormat));
if ((lstrlen(pCaption) + 1 + lstrlen(szFormat) + 1) < sizeof(szTemp)) {
wsprintf(szTemp, szFormat, pCaption);
pCaption = szTemp;
}
}
SetWindowText(hDlg, pCaption);
}
int NEAR PASCAL RemoveButton(HWND hDlg, int idRemove, int idPrev)
{
HWND hRemove = GetDlgItem(hDlg, idRemove);
HWND hPrev = GetDlgItem(hDlg, idPrev);
RECT rcRemove, rcPrev;
GetWindowRect(hRemove, &rcRemove);
GetWindowRect(hPrev, &rcPrev);
ShowWindow(hRemove, SW_HIDE);
return(rcRemove.right - rcPrev.right);
}
void NEAR PASCAL InitPropSheetDlg(HWND hDlg, LPPROPDATA ppd)
{
char szTemp[128 + 50];
int dxMax, dyMax, dxDlg, dyDlg, dyGrow, dxGrow;
RECT rcMinSize, rcDlg, rcPage, rcOrigCtrl;
int dxApplyButton = 0;
UINT uPages;
#ifdef WIN32
HIMAGELIST himl = NULL;
#endif
TC_ITEMEXTRA tie;
char szStartPage[128];
LPCSTR pStartPage;
UINT nStartPage;
#ifdef DEBUG
BOOL fStartPageFound = FALSE;
#endif
// set our instance data pointer
SetWindowLong(hDlg, DWL_USER, (LONG)ppd);
// Make sure this gets inited early on.
ppd->nCurItem = 0;
// do this here instead of using DS_SETFOREGROUND so we don't hose
// pages that do things that want to set the foreground window
#ifdef WIN31
if (GetWindowStyle(hDlg) & DS_3DLOOK)
Win31MakeDlgNonBold(hDlg);
#endif
if (!(ppd->psh.dwFlags & PSH_WIZARD)) {
_SetTitle(hDlg, ppd);
}
if (ppd->psh.dwFlags & PSH_USEICONID)
{
#ifndef WIN31
ppd->psh.hIcon = LoadImage(ppd->psh.hInstance, ppd->psh.pszIcon, IMAGE_ICON, g_cxSmIcon, g_cySmIcon, LR_DEFAULTCOLOR);
#else
ppd->psh.hIcon = NULL;
#endif
}
if ((ppd->psh.dwFlags & (PSH_USEICONID | PSH_USEHICON)) && ppd->psh.hIcon)
SendMessage(hDlg, WM_SETICON, FALSE, (LPARAM)(UINT)ppd->psh.hIcon);
ppd->hDlg = hDlg;
ppd->hwndTabs = GetDlgItem(hDlg, IDD_PAGELIST);
TabCtrl_SetItemExtra(ppd->hwndTabs, CB_ITEMEXTRA);
// nStartPage is either ppd->psh.nStartPage or the page pStartPage
nStartPage = ppd->psh.nStartPage;
if (ppd->psh.dwFlags & PSH_USEPSTARTPAGE)
{
pStartPage = ppd->psh.pStartPage;
nStartPage = 0; // default page if pStartPage not found
if (!HIWORD(pStartPage))
{
szTemp[0] = '\0';
LoadString(ppd->psh.hInstance, (UINT)LOWORD(pStartPage),
szStartPage, sizeof(szStartPage));
pStartPage = (LPCSTR)szTemp;
}
}
dxMax = dyMax = 0;
tie.tci.mask = TCIF_TEXT | TCIF_PARAM | TCIF_IMAGE;
tie.hwndPage = NULL;
tie.tci.pszText = szTemp;
tie.state = 0;
SendMessage(ppd->hwndTabs, WM_SETREDRAW, FALSE, 0L);
for (uPages = 0; uPages < ppd->psh.nPages; uPages++)
{
POINT pt;
HICON hIcon = NULL;
if (GetPageInfo(ppd->psh.phpage[uPages], szTemp, sizeof(szTemp), &pt, &hIcon))
{
// Add the page to the end of the tab list
tie.tci.iImage = -1;
if (hIcon) {
#ifdef WIN32
if (!himl) {
himl = ImageList_Create(g_cxSmIcon, g_cySmIcon, TRUE, 8, 4);
TabCtrl_SetImageList(ppd->hwndTabs, himl);
}
tie.tci.iImage = ImageList_AddIcon(himl, hIcon);
#endif
DestroyIcon(hIcon);
}
// BUGBUG? What if this fails? Do we want to destroy the page?
if (TabCtrl_InsertItem(ppd->hwndTabs, 1000, &tie.tci) >= 0)
{
if (dxMax < pt.x)
dxMax = pt.x;
if (dyMax < pt.y)
dyMax = pt.y;
}
// if the user is specifying the startpage via title, check it here
if (ppd->psh.dwFlags & PSH_USEPSTARTPAGE &&
!lstrcmpi(pStartPage, szTemp))
{
nStartPage = uPages;
#ifdef DEBUG
fStartPageFound = TRUE;
#endif
}
}
else
{
// Destroy this hpage and move all the rest down.
// Do this because if we don't, then things get confused (GPF).
// This is safe to do since we call DestroyPropSheetPage
// in PropertySheet. (Of course there we do it in inverse
// order, and here it just happens on error... Oh well...)
DebugMsg(DM_ERROR, "PropertySheet failed to GetPageInfo");
DestroyPropertySheetPage(ppd->psh.phpage[uPages]);
hmemcpy(&(ppd->psh.phpage[uPages]),
&(ppd->psh.phpage[uPages+1]),
sizeof(HPROPSHEETPAGE) * (ppd->psh.nPages - uPages - 1));
uPages--;
ppd->psh.nPages--;
}
}
SendMessage(ppd->hwndTabs, WM_SETREDRAW, TRUE, 0L);
if (ppd->psh.pfnCallback) {
ppd->psh.pfnCallback(hDlg, PSCB_INITIALIZED, 0);
}
// Now compute the size of the tab control.
// Compute rcPage = Size of page area in pixels
rcPage.left = rcPage.top = 0;
rcPage.right = dxMax;
rcPage.bottom = dyMax;
MapDialogRect(hDlg, &rcPage);
// Get the size of the pagelist control in pixels.
GetClientRect(ppd->hwndTabs, &rcOrigCtrl);
// Now compute the minimum size for the page region
rcMinSize = rcOrigCtrl;
if (rcMinSize.right < rcPage.right)
rcMinSize.right = rcPage.right;
if (rcMinSize.bottom < rcPage.bottom)
rcMinSize.bottom = rcPage.bottom;
// 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 (ppd->psh.dwFlags & PSH_WIZARD) {
// 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).
// Resize the dialog to make room for the control's new size. This can
// only grow the size.
GetWindowRect(hDlg, &rcDlg);
dxGrow = rcMinSize.right - rcOrigCtrl.right;
dxDlg = rcDlg.right - rcDlg.left + dxGrow;
dyGrow = rcMinSize.bottom - rcOrigCtrl.bottom;
dyDlg = rcDlg.bottom - rcDlg.top + dyGrow;
// Cascade property sheet windows (only for comctl32 and commctrl)
#ifndef WIN31
// 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.
{
#pragma data_seg(DATASEG_READONLY)
const char c_szStatic[] = "Static";
#pragma data_seg()
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)) {
rcDlg.top += g_cySmIcon;
rcDlg.left += g_cxSmIcon;
}
swp = SWP_NOZORDER | SWP_NOACTIVATE;
}
SetWindowPos(hDlg, NULL, rcDlg.left, rcDlg.top, dxDlg, dyDlg, swp);
}
#else
SetWindowPos(hDlg, NULL, 0, 0, dxDlg, dyDlg, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
#endif
// Now we'll figure out where the page needs to start relative
// to the bottom of the tabs.
MapWindowPoints(ppd->hwndTabs, hDlg, (LPPOINT)&rcPage, 2);
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;
int i, numids;
int iStartID = 0;
if (ppd->psh.dwFlags & PSH_WIZARD) {
numids = ARRAYSIZE(WizIDs);
hCtrl = GetDlgItem(hDlg, IDD_DIVIDER);
GetWindowRect(hCtrl, &rcCtrl);
MapWindowRect(NULL, hDlg, &rcCtrl);
SetWindowPos(hCtrl, NULL, rcCtrl.left, rcCtrl.top + dyGrow,
RECTWIDTH(rcCtrl) + dxGrow, RECTHEIGHT(rcCtrl),
SWP_NOZORDER | SWP_NOACTIVATE);
EnableWindow(GetDlgItem(hDlg, IDD_BACK), TRUE);
ShowWindow(GetDlgItem(hDlg, IDD_FINISH), SW_HIDE);
ppd->idDefaultFallback = IDD_NEXT;
} else {
numids = ARRAYSIZE(IDs);
ppd->idDefaultFallback = IDOK;
}
// If there's no help, then remove the help button.
if (!(ppd->psh.dwFlags & PSH_HASHELP)) {
numids--;
dxGrow += RemoveButton(hDlg, IDHELP,
(ppd->psh.dwFlags & PSH_WIZARD) ? IDCANCEL : IDD_APPLYNOW);
}
// If we are not a wizard, and we should NOT show apply now
if ((ppd->psh.dwFlags & PSH_NOAPPLYNOW) &&
!(ppd->psh.dwFlags & PSH_WIZARD))
{
dxApplyButton = RemoveButton(hDlg, IDD_APPLYNOW, IDCANCEL);
iStartID = 1;
}
for (i = iStartID; i < numids; ++i)
{
int iCtrl = (ppd->psh.dwFlags & PSH_WIZARD) ? WizIDs[i] : IDs[i];
hCtrl = GetDlgItem(hDlg, iCtrl);
GetWindowRect(hCtrl, &rcCtrl);
ScreenToClient(hDlg, (LPPOINT)&rcCtrl);
// Relies on the fact that IDHELP is last in the array. This is to
// properly adjust for sheets that have help, but no apply button.
if (iCtrl == IDHELP) {
dxApplyButton = 0;
}
SetWindowPos(hCtrl, NULL, rcCtrl.left + dxGrow + dxApplyButton,
rcCtrl.top + dyGrow, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
}
// force the dialog to reposition itself based on its new size
#ifdef WIN31
if (ppd->psh.dwFlags & PSH_CENTER)
CenterDialog(hDlg);
else
Win31RepositionDlg( hDlg );
#else
SendMessage(hDlg, DM_REPOSITION, 0, 0L);
SetForegroundWindow(hDlg);
#endif // WIN31
// Now attempt to select the starting page.
TabCtrl_SetCurSel(ppd->hwndTabs, nStartPage);
PageChange(ppd, 1);
#ifdef DEBUG
if (ppd->psh.dwFlags & PSH_USEPSTARTPAGE && !fStartPageFound)
DebugMsg(DM_WARNING, "sh WN - Property start page '%s' not found.", pStartPage);
#endif
// We set this to 1 if the user saves any changes.
ppd->nReturn = 0;
}
HWND NEAR PASCAL _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;
}
LRESULT NEAR PASCAL _Ppd_SendNotify(LPPROPDATA ppd, int nItem, int code, LPARAM lParam)
{
PSHNOTIFY pshn;
pshn.lParam = lParam;
return SendNotify(_Ppd_GetPage(ppd,nItem), ppd->hDlg, code, (LPNMHDR)&pshn);
}
int FindPageIndex(LPPROPDATA ppd, int nCurItem, DWORD dwFind, int iAutoAdj)
{
int nActivate;
if (dwFind == 0) {
nActivate = nCurItem + iAutoAdj;
if (((UINT)nActivate) <= ppd->psh.nPages) {
return(nActivate);
}
} else {
for (nActivate = 0; (UINT)nActivate < ppd->psh.nPages; nActivate++) {
if ((DWORD)(ppd->psh.phpage[nActivate]->psp.pszTemplate) ==
dwFind) {
return(nActivate);
}
}
}
return nCurItem + iAutoAdj;
}
void NEAR PASCAL SetNewDefID(LPPROPDATA ppd)
{
HWND hDlg = ppd->hDlg;
#ifdef WIN31
if( ppd->psh.dwFlags & PSH_WIZARD )
{
SetFocus(ppd->hwndCurPage);
if( GetFocus()==NULL )
SetFocus( GetDlgItem( hDlg, IDD_NEXT ) );
}
#else
HWND hwndFocus;
hwndFocus = GetNextDlgTabItem(ppd->hwndCurPage, NULL, FALSE);
Assert(hwndFocus);
if (hwndFocus) {
int id;
if (((DWORD)SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0L)) & DLGC_HASSETSEL)
{
// select the text
Edit_SetSel(hwndFocus, 0, -1);
}
id = GetDlgCtrlID(hwndFocus);
// HACKHACK....
// if there is no tab stop, GetDlgCtrlID will return the first item
// (don't ask me why it doesn't return NULL
// so we need to check it and set the focus to the tabs if it's not a tabstop
if ((GetWindowLong(hwndFocus, GWL_STYLE) & (WS_VISIBLE | WS_DISABLED | WS_TABSTOP)) == (WS_VISIBLE | WS_TABSTOP))
SetFocus(hwndFocus);
else {
// in prop sheet mode, focus on tabs,
// in wizard mode, tabs aren't visible, go to idDefFallback
if (ppd->psh.dwFlags & PSH_WIZARD)
SetFocus(GetDlgItem(hDlg, ppd->idDefaultFallback));
else
SetFocus(ppd->hwndTabs);
}
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 NEAR PASCAL PageChanging(LPPROPDATA ppd)
{
BOOL bRet = FALSE;
if (ppd && ppd->hwndCurPage)
{
bRet = (BOOL)_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_KILLACTIVE, 0);
}
return bRet;
}
void NEAR PASCAL PageChange(LPPROPDATA ppd, int iAutoAdj)
{
HWND hwndCurPage;
HWND hwndCurFocus;
int nItem;
HWND hDlg, hwndTabs;
TC_ITEMEXTRA tie;
int FlailCount = 0;
LRESULT lres;
#ifdef WIN31
LRESULT (WINAPI *_DefDlgProc)(HWND, UINT, WPARAM, LPARAM);
#endif
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 > 10)
{
DebugMsg(DM_TRACE, "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)
{
PSP FAR *hpage = ppd->psh.phpage[nItem];
hwndCurPage = CreatePage(hpage, hDlg);
if (hwndCurPage)
{
// tie.tci.mask = TCIF_PARAM;
tie.hwndPage = hwndCurPage;
TabCtrl_SetItem(hwndTabs, nItem, &tie.tci);
#ifdef WIN31
// Subclass for proper color handling
// SubclassWindow(hwndCurPage, Win31PropPageWndProc);
_DefDlgProc = SubclassWindow(hwndCurPage, Win31PropPageWndProc);
SetProp(hwndCurPage,g_szLoSubclass,(HANDLE)LOWORD(_DefDlgProc));
SetProp(hwndCurPage,g_szHiSubclass,(HANDLE)HIWORD(_DefDlgProc));
// Make fonts non-bold
if (GetWindowStyle(hwndCurPage) & DS_3DLOOK)
Win31MakeDlgNonBold(hwndCurPage);
// Remove the default button - it is in the main dialog
RemoveDefaultButton(hwndCurPage,NULL);
#endif
}
else
{
/* Should we put up some sort of error message here?
*/
TabCtrl_DeleteItem(hwndTabs, nItem);
TabCtrl_SetCurSel(hwndTabs, 0);
goto TryAgain;
}
}
// 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.
*/
SetWindowPos(hwndCurPage, HWND_TOP, ppd->xSubDlg, ppd->ySubDlg, ppd->cxSubDlg, ppd->cySubDlg, 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) {
TabCtrl_SetCurSel(hwndTabs, FindPageIndex(ppd, nItem,
(lres == -1) ? 0 : lres, iAutoAdj));
ShowWindow(hwndCurPage, SW_HIDE);
goto TryAgain;
}
if (ppd->psh.dwFlags & PSH_HASHELP) {
Button_Enable(GetDlgItem(hDlg, IDHELP),
(BOOL)(ppd->psh.phpage[nItem]->psp.dwFlags & PSP_HASHELP));
}
// If this is a wizard then we'll set the dialog's title to the tab
// title.
if (ppd->psh.dwFlags & PSH_WIZARD) {
TC_ITEMEXTRA tie;
char szTemp[128 + 50];
tie.tci.mask = TCIF_TEXT;
tie.tci.pszText = szTemp;
tie.tci.cchTextMax = sizeof(szTemp);
// BUGBUG -- Check for error. Does this return false if fails??
TabCtrl_GetItem(hwndTabs, nItem, &tie.tci);
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);
}
}
#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)
// return TRUE iff all sheets successfully handle the notification
BOOL NEAR PASCAL 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;
LPARAM lParam = FALSE;
switch (wParam) {
case IDOK:
lParam = TRUE;
bExit = TRUE;
// Fall through...
case IDD_APPLYNOW:
// First allow the current dialog to validate itself.
if ((BOOL)_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_KILLACTIVE, 0))
return FALSE;
nReturnNew = 1;
nNotify = PSN_APPLY;
break;
case IDCLOSE:
lParam = TRUE;
case IDCANCEL:
bExit = TRUE;
nNotify = PSN_RESET;
break;
default:
return FALSE;
}
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, "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)
{
char szOK[30];
HWND hwndApply;
if (!(ppd->psh.dwFlags & PSH_WIZARD)) {
// 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;
LoadString(HINST_THISDLL, IDS_OK, szOK, sizeof(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 NEAR PASCAL ResetWizButtons(LPPROPDATA ppd)
{
int id;
if (ppd->psh.dwFlags & PSH_WIZARD) {
for (id = 0; id < ARRAYSIZE(WizIDs); id++)
SendDlgItemMessage(ppd->hDlg, WizIDs[id], BM_SETSTYLE, BS_PUSHBUTTON, TRUE);
}
}
void NEAR PASCAL SetWizButtons(LPPROPDATA ppd, LPARAM lParam)
{
int iShowID = IDD_NEXT;
int iHideID = IDD_FINISH;
int idDef;
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));
bEnabled = (lParam & PSWIZB_BACK) != 0;
hwndBack = GetDlgItem(hDlg, IDD_BACK);
EnableWindow(hwndBack, bEnabled);
bEnabled = (lParam & PSWIZB_NEXT) != 0;
if (lParam & (PSWIZB_FINISH | PSWIZB_DISABLEDFINISH)) {
iShowID = IDD_FINISH;
iHideID = IDD_NEXT;
bEnabled = (lParam & PSWIZB_FINISH) != 0;
}
hwndHide = GetDlgItem(hDlg, iHideID);
ShowWindow(hwndHide, SW_HIDE);
hwndShow = GetDlgItem(hDlg, iShowID);
ShowWindow(hwndShow, SW_SHOW);
EnableWindow(hwndShow, bEnabled);
// 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) ;
Assert(!bResetFocus); // how can this be??
}
// 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.
{
int ids[3] = { iShowID, IDD_BACK, IDCANCEL };
int i;
HWND hwndNewFocus = NULL;
for (i = 0; i < ARRAYSIZE(ids); i++) {
hwndNewFocus = GetDlgItem(hDlg, ids[i]);
if (IsWindowVisible(hwndNewFocus) && IsWindowEnabled(hwndNewFocus)) {
hwndFocus = hwndNewFocus;
break;
}
}
ppd->idDefaultFallback = ids[i];
if (bResetFocus)
SetFocus(hwndNewFocus);
ResetWizButtons(ppd);
SendMessage(hDlg, DM_SETDEFID, ids[i], 0);
}
}
int NEAR PASCAL FindItem(HWND hwndTabs, HWND hwndPage, TC_ITEMEXTRA FAR * lptie)
{
int i;
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 NEAR PASCAL 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);
}
EnableWindow(GetDlgItem(ppd->hDlg, IDD_APPLYNOW), TRUE);
}
// a page is telling us that everything has reverted to its last
// saved state.
void NEAR PASCAL 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--)
{
// BUGBUG? Does TabCtrl_GetItem return its information properly?!?
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);
}
BOOL NEAR PASCAL AddPropPage(LPPROPDATA ppd, PSP FAR * hpage)
{
POINT pt;
HICON hIcon = NULL;
char szTemp[128];
TC_ITEMEXTRA tie;
#ifdef WIN32
HIMAGELIST himl;
#endif
if (ppd->psh.nPages >= MAXPROPPAGES)
return FALSE; // we're full
ppd->psh.phpage[ppd->psh.nPages] = hpage;
ppd->psh.nPages++;
tie.tci.mask = TCIF_TEXT | TCIF_PARAM | TCIF_IMAGE;
tie.hwndPage = NULL;
tie.tci.pszText = szTemp;
tie.state = 0;
#ifdef WIN32
himl = TabCtrl_GetImageList(ppd->hwndTabs);
#endif
if (GetPageInfo(hpage, szTemp, sizeof(szTemp), &pt, &hIcon))
{
// Add the page to the end of the tab list
if (hIcon) {
#ifdef WIN32
if (himl)
tie.tci.iImage = ImageList_AddIcon(himl, hIcon);
#else
tie.tci.iImage = -1;
#endif
DestroyIcon(hIcon);
} else {
tie.tci.iImage = -1;
}
TabCtrl_InsertItem(ppd->hwndTabs, 1000, &tie.tci);
return TRUE;
}
else
{
DebugMsg(DM_ERROR, "AddPropPage: GetPageInfo failed");
ppd->psh.nPages--;
return FALSE;
}
}
// removes property sheet hpage (index if NULL)
void NEAR PASCAL RemovePropPage(LPPROPDATA ppd, int index, PSP FAR * hpage)
{
int i = -1;
BOOL fReturn = TRUE;
TC_ITEMEXTRA tie;
tie.tci.mask = TCIF_PARAM;
if (hpage) {
for (i = ppd->psh.nPages - 1; i >= 0; i--) {
if (hpage == ppd->psh.phpage[i])
break;
}
}
if (i == -1) {
i = index;
// this catches i < 0 && i >= (int)(ppd->psh.nPages)
if ((UINT)i >= ppd->psh.nPages)
{
DebugMsg(DM_ERROR, "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);
}
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);
}
DestroyPropertySheetPage(ppd->psh.phpage[i]);
ppd->psh.nPages--;
hmemcpy(&ppd->psh.phpage[i], &ppd->psh.phpage[i+1],
sizeof(ppd->psh.phpage[0]) * (ppd->psh.nPages - i));
TabCtrl_DeleteItem(ppd->hwndTabs, i);
}
// 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 NEAR PASCAL PageSetSelection(LPPROPDATA ppd, int index, PSP FAR * 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 == ppd->psh.phpage[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 NEAR PASCAL 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 NEAR PASCAL HandleHotkey(LPARAM lparam)
{
WORD wHotkey;
char 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, sizeof(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 NEAR PASCAL WizNextBack(LPPROPDATA ppd, int code)
{
DWORD dwFind;
int iAutoAdj = (code == PSN_WIZNEXT) ? 1 : -1;
dwFind = _Ppd_SendNotify(ppd, ppd->nCurItem, code, 0);
if (dwFind == -1) {
return(FALSE);
}
return(PageSetSelection(ppd, FindPageIndex(ppd, ppd->nCurItem, dwFind, iAutoAdj), NULL, iAutoAdj));
}
BOOL NEAR PASCAL Prsht_OnCommand(LPPROPDATA ppd, int id, HWND hwndCtrl, UINT codeNotify)
{
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 (!(ppd->psh.dwFlags & PSH_WIZARD)) {
ButtonPushed(ppd, id);
} else {
Assert(0);
}
break;
case IDHELP:
if (IsWindowEnabled(hwndCtrl))
{
_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_HELP, 0);
}
break;
case IDD_FINISH:
// b#11346 - dont let multiple clicks on FINISH.
EnableWindow(ppd->hDlg, FALSE);
if (!_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_WIZFINISH, 0))
{
ppd->hwndCurPage = NULL;
ppd->nReturn = 1;
}
else
{
EnableWindow(ppd->hDlg, TRUE);
}
break;
case IDD_NEXT:
case IDD_BACK:
ppd->idDefaultFallback = id;
WizNextBack(ppd, id == IDD_NEXT ? PSN_WIZNEXT : PSN_WIZBACK);
break;
default:
FORWARD_WM_COMMAND(_Ppd_GetPage(ppd, ppd->nCurItem), id, hwndCtrl, codeNotify, SendMessage);
}
return TRUE;
}
BOOL NEAR PASCAL Prop_IsDialogMessage(LPPROPDATA ppd, LPMSG32 pmsg32)
{
if ((pmsg32->message == WM_KEYDOWN) && (GetAsyncKeyState(VK_CONTROL) < 0))
{
BOOL bBack;
switch (pmsg32->wParam) {
case VK_TAB:
bBack = GetAsyncKeyState(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;
}
if (ppd->psh.dwFlags & PSH_WIZARD)
{
WizNextBack(ppd, bBack ? PSN_WIZBACK : PSN_WIZNEXT);
}
else
{
int iCur = TabCtrl_GetCurSel(ppd->hwndTabs);
// tab in reverse if shift is down
if (bBack)
iCur += (ppd->psh.nPages - 1);
else
iCur++;
iCur %= ppd->psh.nPages;
PageSetSelection(ppd, iCur, NULL, 1);
}
return TRUE;
}
NoKeys:
#ifdef WIN31
if (Win31IsKeyMessage(ppd->hDlg, pmsg32))
return TRUE;
#ifdef IEWIN31_25
// BUGBUG:
// If focus is on the property sheet, then the accelerator for the
// apply button is ignored. So we check for this accelerator ourselves.
// This is only necessary for real win31 systems. The code below
// works fine on Win95's 16-bit system without this hack. (stevepro)
if (pmsg32->message == WM_SYSCHAR)
{
// Find the apply button to get its accelerator
HWND hwndApply = GetDlgItem(ppd->hDlg, IDD_APPLYNOW);
if (hwndApply && (IsWindowVisible(hwndApply)))
{
// Get the accelerator for the apply button
char buf[40];
char FAR * pszAccel;
GetWindowText(hwndApply, buf, sizeof(buf)-1);
pszAccel = _fstrstr(buf, "&");
if (pszAccel && (0 == StrCmpNI(&(char)pmsg32->wParam, ++pszAccel, 1)))
{
// We have a match, so process the accelerator
// if (IsDialogMessage32(ppd->hDlg, pmsg32, TRUE))
SendMessage(hwndApply, BM_SETSTATE, TRUE, 0);
SendMessage(hwndApply, BM_SETSTATE, FALSE, 0);
SendMessage(GetParent(hwndApply), WM_COMMAND, IDD_APPLYNOW, MAKELPARAM(hwndApply, BN_CLICKED));
return TRUE;
}
}
}
#endif // IEWIN31_25
if (IsDialogMessage32(ppd->hwndCurPage, pmsg32, TRUE))
return TRUE;
// BUGBUG:
// User 3.1 doesn't handle accelerator keys properly between
// the main and sub dialog. If a control in the main dialog has
// focus and an accelerator for a control in the subdialog is
// pressed, only the main dialog gets the message so the focus
// doesn't change. Likewise if a control in the subdialog has focus.
// This will only be used during setup so we won't freak about it
// (for now).
if ((pmsg32->message == WM_SYSCHAR) && (pmsg32->hwnd != ppd->hwndCurPage))
{
SetFocus(ppd->hwndCurPage);
}
#endif
if (IsDialogMessage32(ppd->hDlg, pmsg32, TRUE))
return TRUE;
return FALSE;
}
#pragma data_seg(DATASEG_READONLY)
const static DWORD aPropHelpIDs[] = { // Context Help IDs
IDD_APPLYNOW, IDH_COMM_APPLYNOW,
0, 0
};
#pragma data_seg()
BOOL CALLBACK PropSheetDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
HWND hwndT;
LPPROPDATA ppd = (LPPROPDATA)GetWindowLong(hDlg, DWL_USER);
switch (uMessage) {
case WM_INITDIALOG:
InitPropSheetDlg(hDlg, (LPPROPDATA)lParam);
return FALSE;
// REVIEW for dealing with hotkeys.
// BUGBUG: This code might not work with 32-bit WM_SYSCOMMAND msgs.
case WM_SYSCOMMAND:
if (wParam == SC_HOTKEY)
return HandleHotkey(lParam);
else if (wParam == SC_CLOSE)
{
// 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, (ppd->fFlags & PD_CANCELTOCLOSE) ? IDOK : IDCLOSE, NULL, 0);
}
return FALSE; // Let default process happen
case WM_NCDESTROY:
if (ppd)
{
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--)
{
DestroyPropertySheetPage(ppd->psh.phpage[iPage]);
}
// 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)
{
#ifdef WIN32
LocalFree((HLOCAL)ppd);
#else
LocalFree((HLOCAL)LOWORD(ppd));
#endif
}
}
// NOTES:
// Must return FALSE to avoid DS leak!!!
return FALSE;
case WM_DESTROY:
if (ppd)
{
#ifdef WIN32
// Destroy the image list we created during our init call.
HIMAGELIST himl = TabCtrl_GetImageList(ppd->hwndTabs);
if (himl)
ImageList_Destroy(himl);
#endif
if ((ppd->psh.dwFlags & PSH_USEICONID) && ppd->psh.hIcon)
DestroyIcon(ppd->psh.hIcon);
}
#ifdef WIN31
{
// Clean up the non-bold font if we created one
HFONT hFont = GetProp(hDlg, g_szNonBoldFont);
if (hFont)
{
DeleteObject(hFont);
}
RemoveProp(hDlg, g_szNonBoldFont);
}
#endif
break;
case WM_ERASEBKGND:
return ppd->fFlags & PD_NOERASE;
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));
#ifdef WIN31
case WM_CTLCOLOR:
return LOWORD(HANDLE_WM_CTLCOLOR(hDlg, wParam, lParam, Win31OnCtlColor));
case WM_GETDLGCODE:
return DLGC_RECURSE;
#endif
case WM_NOTIFY:
switch (((NMHDR FAR *)lParam)->code)
{
case TCN_SELCHANGE:
PageChange(ppd, 1);
break;
case TCN_SELCHANGING:
SetWindowLong(hDlg, DWL_MSGRESULT, PageChanging(ppd));
break;
default:
return FALSE;
}
return TRUE;
case PSM_SETWIZBUTTONS:
SetWizButtons(ppd, lParam);
break;
case PSM_SETFINISHTEXT:
{
HWND hFinish = GetDlgItem(hDlg, IDD_FINISH);
HWND hwndFocus = GetFocus();
HWND hwnd;
BOOL fSetFocus = FALSE;
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) {
Button_SetText(hFinish, (LPSTR)lParam);
}
ShowWindow(hFinish, SW_SHOW);
Button_Enable(hFinish, TRUE);
ResetWizButtons(ppd);
SendMessage(hDlg, DM_SETDEFID, IDD_FINISH, 0);
ppd->idDefaultFallback = IDD_FINISH;
if (fSetFocus)
SetFocus(hFinish);
}
break;
case PSM_SETTITLE:
ppd->psh.pszCaption = (LPCSTR)lParam;
ppd->psh.dwFlags = ((((DWORD)wParam) & PSH_PROPTITLE) | (ppd->psh.dwFlags & ~PSH_PROPTITLE));
_SetTitle(hDlg, ppd);
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_CANCELTOCLOSE:
if (!(ppd->fFlags & PD_CANCELTOCLOSE))
{
char szClose[20];
ppd->fFlags |= PD_CANCELTOCLOSE;
LoadString(HINST_THISDLL, IDS_CLOSE, szClose, sizeof(szClose));
SetDlgItemText(hDlg, IDOK, szClose);
EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
}
break;
case PSM_SETCURSEL:
SetWindowLong(hDlg, DWL_MSGRESULT,
PageSetSelection(ppd, (int)wParam,(PSP FAR *)lParam, 1));
break;
case PSM_SETCURSELID:
SetWindowLong(hDlg, DWL_MSGRESULT,
PageSetSelection(ppd, FindPageIndex(ppd, ppd->nCurItem, (DWORD)lParam, 1),
NULL, 1));
break;
case PSM_REMOVEPAGE:
RemovePropPage(ppd, (int)wParam,(PSP FAR *)lParam);
break;
case PSM_ADDPAGE:
SetDlgMsgResult(hDlg, uMessage, AddPropPage(ppd,(PSP FAR *)lParam));
break;
case PSM_QUERYSIBLINGS:
SetWindowLong(hDlg, DWL_MSGRESULT, QuerySiblings(ppd, wParam, lParam));
break;
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
SetWindowLong(hDlg, DWL_MSGRESULT, ButtonPushed(ppd, IDD_APPLYNOW));
break;
case PSM_GETTABCONTROL:
SetWindowLong(hDlg, DWL_MSGRESULT, (LONG)(UINT)ppd->hwndTabs);
break;
case PSM_GETCURRENTPAGEHWND:
SetWindowLong(hDlg, DWL_MSGRESULT, (LONG)(UINT)ppd->hwndCurPage);
break;
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
SetWindowLong(hDlg, DWL_MSGRESULT, Prop_IsDialogMessage(ppd, (LPMSG32)lParam));
break;
// these should be relayed to all created dialogs
case WM_WININICHANGE:
case WM_SYSCOLORCHANGE:
if (ppd)
{
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_ENABLE:
case WM_QUERYNEWPALETTE:
case WM_PALETTECHANGED:
case WM_DEVICECHANGE:
if (!ppd)
return FALSE;
if (ppd->hwndTabs)
SendMessage(ppd->hwndTabs, uMessage, wParam, lParam);
case WM_ACTIVATEAPP:
case WM_ACTIVATE:
if (ppd)
{
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.
SetWindowLong(hDlg, DWL_MSGRESULT,
SendMessage(hwndT, uMessage, wParam, lParam));
break;
}
}
return FALSE;
case WM_CONTEXTMENU:
if ((ppd->hwndTabs != (HWND)wParam) && (ppd->hwndCurPage != (HWND)wParam) &&
(!(ppd->psh.dwFlags & PSH_WIZARD)))
WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (DWORD)(LPVOID)aPropHelpIDs);
break;
#ifndef IEWIN31_25
case WM_HELP:
hwndT = (HWND)((LPHELPINFO)lParam)->hItemHandle;
if ((GetParent(hwndT) == hDlg) && (hwndT != ppd->hwndTabs))
WinHelp(hwndT, NULL, HELP_WM_HELP, (DWORD)(LPSTR) aPropHelpIDs);
break;
#endif
default:
return FALSE;
}
return TRUE;
}
#ifdef WIN31
LRESULT CALLBACK Win31PropPageWndProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
LRESULT result;
HFONT hFont;
LRESULT (WINAPI *_DefDlgProc)(HWND, UINT, WPARAM, LPARAM)=NULL;
(FARPROC)_DefDlgProc = (FARPROC)(MAKELONG((UINT) GetProp(hDlg, g_szLoSubclass),
(UINT)GetProp(hDlg, g_szHiSubclass)));
switch (uMessage)
{
case WM_CTLCOLOR:
result = HANDLE_WM_CTLCOLOR(hDlg,wParam,lParam,Win31OnCtlColor);
if (result)
return result;
break;
case WM_GETDLGCODE:
return DLGC_RECURSE;
case WM_DESTROY:
// Clean up the non-bold font if we created one
hFont = GetProp(hDlg, g_szNonBoldFont);
if (hFont)
{
DeleteObject(hFont);
RemoveProp(hDlg, g_szNonBoldFont);
}
// Clean up subclass
if( _DefDlgProc )
{
RemoveProp(hDlg, g_szLoSubclass);
RemoveProp(hDlg, g_szHiSubclass);
// Now put the subclass back.
SetWindowLong(hDlg, GWL_WNDPROC, (LPARAM)(WNDPROC)(_DefDlgProc));
}
break;
default:
break;
}
// return DefDlgProc(hDlg,uMessage,wParam,lParam);
if( _DefDlgProc )
return _DefDlgProc(hDlg,uMessage,wParam,lParam);
else
return DefDlgProc(hDlg,uMessage,wParam,lParam);
}
// Centers a dialog relative to the desktop
VOID NEAR PASCAL CenterDialog ( HWND hdlg )
{
HWND hwndPar = GetDesktopWindow();
RECT rectDlg, rectPar;
int x, y;
int dyPar, dyDlg, dyOff;
POINT pt;
GetWindowRect(hdlg, &rectDlg);
GetClientRect(hwndPar, &rectPar);
if ((x = (rectPar.right - rectPar.left) / 2 -
(rectDlg.right - rectDlg.left) / 2) < 0)
{
x = 0;
}
dyPar = rectPar.bottom - rectPar.top;
dyDlg = rectDlg.bottom - rectDlg.top;
if ((y = dyPar / 2 - dyDlg / 2) < 0)
{
y = 0;
}
if (y > 0)
{
/* Offset by 1/2 width of title bar and border.
*/
pt.x = pt.y = 0;
ClientToScreen(hwndPar, &pt);
GetWindowRect(hwndPar, &rectPar);
dyOff = (pt.y - rectPar.top) / 2;
if (y + dyOff + dyDlg < dyPar)
y += dyOff;
else
y = dyPar - dyDlg;
}
SetWindowPos(hdlg, NULL, x, y, 0, 0, (SWP_NOSIZE | SWP_NOZORDER));
}
// Win31RepositionDlg( HWND hDlg )
// Ensures that a dialog lies entirely on the screen, used as a substitute for
// the DM_REPOSITION message in non Win4.0 environments
void NEAR PASCAL Win31RepositionDlg( HWND hDlg )
{
RECT rcWindow;
int dX, dY;
// set rcWindow to dimensions of property page
GetWindowRect( hDlg, &rcWindow );
// set dX if dlg needs to be moved to the right
dX = max( -rcWindow.left, 0 );
if( dX == 0 ){
// didn't need to be moved right, maybe we need to move left
dX = -max( rcWindow.right - (GetSystemMetrics( SM_CXSCREEN ) - 1 ), 0 );
}
// set dY if dlg needs to be moved down
dY = max( -rcWindow.top, 0 );
if( dY == 0 ){
// didn't need to be moved down, maybe we need to move up
dY = -max( rcWindow.bottom - (GetSystemMetrics( SM_CYSCREEN ) - 1 ), 0 );
}
if( dX != 0 || dY != 0 ) {
// we need to move the dialog
OffsetRect( &rcWindow, dX, dY );
SetWindowPos( hDlg, NULL, rcWindow.left, rcWindow.top, 0, 0,
SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER );
}
}
LRESULT NEAR PASCAL Win31OnCtlColor(HWND hDlg, HDC hdcChild, HWND hwndChild, int nCtlType)
{
COLORREF clrFore;
COLORREF clrBack;
HBRUSH hbrBack;
// Set up the supplied DC with the foreground and background
// colors we want to use in the control, and return a brush
// to use for filling.
switch (nCtlType)
{
case CTLCOLOR_STATIC:
case CTLCOLOR_DLG:
case CTLCOLOR_MSGBOX:
clrFore = g_clrWindowText;
clrBack = g_clrBtnFace;
hbrBack = g_hbrBtnFace;
break;
case CTLCOLOR_BTN:
clrFore = g_clrBtnText;
clrBack = g_clrBtnFace;
hbrBack = g_hbrBtnFace;
break;
case CTLCOLOR_EDIT:
if (GetWindowStyle(hwndChild) & ES_READONLY)
{
clrFore = g_clrWindowText;
clrBack = g_clrBtnFace;
hbrBack = g_hbrBtnFace;
break;
}
// else fall thru to default
default:
// cause defaults to be used
return NULL;
}
SetTextColor(hdcChild, clrFore);
SetBkColor(hdcChild, clrBack);
return((LRESULT)(DWORD)(WORD)hbrBack);
}
BOOL NEAR PASCAL Win31MakeDlgNonBold(HWND hDlg)
{
HFONT hFont;
LOGFONT LogFont;
HWND hwndCtl;
GetObject(GetWindowFont(hDlg), sizeof(LogFont), &LogFont);
// Check if already non-bold
if (LogFont.lfWeight <= FW_NORMAL)
return TRUE;
// Create the non-bold font
LogFont.lfWeight = FW_NORMAL;
if ((hFont = CreateFontIndirect(&LogFont)) == NULL)
return FALSE;
// Save the font as a window prop so we can delete it later
SetProp(hDlg, g_szNonBoldFont, hFont);
// Set all controls non-bold
for (hwndCtl = GetWindow(hDlg,GW_CHILD);
hwndCtl != NULL;
hwndCtl = GetWindow(hwndCtl,GW_HWNDNEXT))
{
SetWindowFont(hwndCtl,hFont,FALSE);
}
return TRUE;
}
#endif // WIN31
int NEAR PASCAL _RealPropertySheet(LPPROPDATA ppd)
{
HWND hwndMain;
MSG32 msg32;
HWND hwndTopOwner;
int nReturn = -1;
HWND hwndOriginalFocus;
if (ppd->psh.nPages == 0)
{
DebugMsg(DM_ERROR, "no pages for prop sheet");
goto FreePpdAndReturn;
}
ppd->hwndCurPage = NULL;
ppd->nReturn = -1;
ppd->nRestart = 0;
ppd->fFlags = FALSE;
hwndTopOwner = ppd->psh.hwndParent;
hwndOriginalFocus = GetFocus();
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 (EnableWindow(hwndTopOwner, FALSE))
{
// Oh, the window was already disabled. Don't enable it later.
hwndTopOwner = NULL;
}
}
}
#if !defined(WIN32) && !defined(WIN31)
#ifdef FE_IME
// Win95d-B#754
// When PCMCIA gets detected, NETDI calls DiCallClassInstaller().
// The class installer of setupx calls PropertySheet() for msgsrv32.
// We usually don't prepare thread link info in imm for that process as
// it won't use IME normaly but we need to treat this case as special.
CreateImmTLink(GetCurrentThreadID());
#endif
#endif
if (ppd->psh.pfnCallback)
{
HRSRC hrsrc;
HGLOBAL hglbl;
LPVOID pTemplate;
LPVOID pTemplateMod;
DWORD cbTemplate;
// Setup for failure
hwndMain = NULL;
// We need to load the resource and put it into a writeable memory block
if ((hrsrc = FindResource(HINST_THISDLL,
MAKEINTRESOURCE((ppd->psh.dwFlags & PSH_WIZARD) ? DLG_WIZARD : DLG_PROPSHEET),
RT_DIALOG)) &&
(hglbl = LoadResource(HINST_THISDLL, hrsrc)))
{
pTemplate = LockResource(hglbl);
cbTemplate = SizeofResource(HINST_THISDLL, hrsrc);
pTemplateMod = (LPVOID)GlobalAlloc(GPTR, cbTemplate * 2); //double it to give some play leeway
if (pTemplateMod)
{
hmemcpy(pTemplateMod, pTemplate, cbTemplate);
ppd->psh.pfnCallback(NULL, PSCB_PRECREATE, (LPARAM)(LPVOID)pTemplateMod);
hwndMain = CreateDialogIndirectParam(HINST_THISDLL, pTemplateMod,
ppd->psh.hwndParent, PropSheetDlgProc, (LPARAM)(LPPROPDATA)ppd);
GlobalFreePtr(pTemplateMod);
}
UnlockResource(hglbl);
}
}
else
hwndMain = CreateDialogParam(HINST_THISDLL,
MAKEINTRESOURCE((ppd->psh.dwFlags & PSH_WIZARD) ? DLG_WIZARD : DLG_PROPSHEET),
ppd->psh.hwndParent, PropSheetDlgProc, (LPARAM)(LPPROPDATA)ppd);
if (!hwndMain)
{
int iPage;
DebugMsg(DM_ERROR, "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--)
DestroyPropertySheetPage(ppd->psh.phpage[iPage]);
goto FreePpdAndReturn;
}
if (ppd->psh.dwFlags & PSH_MODELESS)
return (int)hwndMain;
ShowWindow(hwndMain, SW_SHOW);
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, "PropertySheet: bailing in response to WM_QUIT (and reposting quit)" );
ButtonPushed(ppd, IDCANCEL); // nuke ourselves
PostQuitMessage(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, "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:
#ifdef WIN32
LocalFree((HLOCAL)ppd);
#else
LocalFree((HLOCAL)LOWORD(ppd));
#endif
return nReturn;
}
#ifdef WIN32
// Description:
// This function creates a 32-bit proxy page object for 16-bit page object.
// The PSP_IS16 flag in psp.dwFlags indicates that this is a proxy object.
// Arguments:
// hpage16 -- Specifies the handle to 16-bit property sheet page object.
// hinst16 -- Specifies a handle to FreeLibrary16() when page is deleted.
HPROPSHEETPAGE WINAPI CreateProxyPage(HPROPSHEETPAGE hpage16, HINSTANCE hinst16)
{
HPROPSHEETPAGE hpage = Alloc(sizeof(PSP));
PROPSHEETPAGE * ppsp = MapSLFix(hpage16);
Assert(hpage16 != NULL);
if (hpage)
{
hpage->psp.dwSize = sizeof(hpage->psp);
if (ppsp)
{
// copy the dwFlags so we can reference PSP_HASHELP from the 32 bit side.
hpage->psp.dwFlags = ppsp->dwFlags | PSP_IS16;
}
else
{
hpage->psp.dwFlags = PSP_IS16;
}
hpage->psp.lParam = (LPARAM)hpage16;
hpage->psp.hInstance = hinst16;
}
if (ppsp)
{
UnMapSLFixArray(1, &hpage16);
}
return hpage;
}
#endif
// 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.
int WINAPI PropertySheet(LPCPROPSHEETHEADER ppsh)
{
PROPDATA NEAR *ppd;
// validate header
if (ppsh->dwSize > sizeof(PROPSHEETHEADER))
{
DebugMsg( DM_ERROR, "PropertySheet: dwSize > sizeof( PROPSHEETHEADER )" );
goto invalid_call;
}
if (ppsh->dwSize < (sizeof(PROPSHEETHEADER) - sizeof(PFNPROPSHEETCALLBACK)))
{
DebugMsg( DM_ERROR, "PropertySheet: dwSize < sizeof( old PROPSHEETHEADER )" );
goto invalid_call;
}
if (ppsh->dwFlags & ~PSH_ALL)
{
DebugMsg( DM_ERROR, "PropertySheet: invalid flags" );
goto invalid_call;
}
// BUGBUG: is this >= for a reason?
if (ppsh->nPages >= MAXPROPPAGES)
{
DebugMsg( DM_ERROR, "PropertySheet: too many pages ( use MAXPROPPAGES )" );
goto invalid_call;
}
ppd = (PROPDATA NEAR *)LocalAlloc(LPTR, sizeof(PROPDATA) + MAXPROPPAGES * sizeof(HPROPSHEETPAGE));
if (ppd == NULL)
{
DebugMsg(DM_ERROR, "failed to alloc property page data");
invalid_call:
if (!(ppsh->dwFlags & PSH_PROPSHEETPAGE))
{
int iPage;
// 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->phpage[iPage]);
}
}
return -1;
}
// 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.phpage = (HPROPSHEETPAGE FAR *)((LPBYTE)ppd + sizeof(PROPDATA));
if (ppd->psh.dwFlags & PSH_PROPSHEETPAGE)
{
// for lazy clients convert PROPSHEETPAGE structures into page handles
int iPage;
LPCPROPSHEETPAGE ppsp = ppsh->ppsp;
for (iPage = 0; iPage < (int)ppd->psh.nPages; iPage++)
{
ppd->psh.phpage[iPage] = CreatePropertySheetPage(ppsp);
if (!ppd->psh.phpage[iPage])
{
iPage--;
ppd->psh.nPages--;
}
ppsp = (LPCPROPSHEETPAGE)((LPBYTE)ppsp + ppsp->dwSize); // next PROPSHEETPAGE structure
}
}
else
{
// make a copy of the pages passed in, since we will party here
hmemcpy(ppd->psh.phpage, ppsh->phpage, sizeof(HPROPSHEETPAGE) * ppsh->nPages);
}
// 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))
{
int iPage;
for (iPage = 0; iPage < (int)ppd->psh.nPages; iPage++)
{
if (ppd->psh.phpage[iPage]->psp.dwFlags & PSP_HASHELP)
{
ppd->psh.dwFlags |= PSH_HASHELP;
break;
}
}
}
return _RealPropertySheet(ppd);
}
#ifndef WIN32
#ifndef WIN31
// crap to make this run on M7 user for setup
LPVOID GetUserProc(UINT ord)
{
return (LPVOID)GetProcAddress(GetModuleHandle("USER"), MAKEINTRESOURCE(ord));
}
// GetMessage32 = USER.820
BOOL WINAPI GetMessage32(LPMSG32 pmsg, HWND hwnd, UINT min, UINT max, BOOL f32)
{
static BOOL (WINAPI *pfn)(LPMSG32, HWND, UINT, UINT, BOOL) = (LPVOID)-1;
if (pfn == (LPVOID)-1)
pfn = GetUserProc(820);
if (pfn)
return pfn(pmsg, hwnd, min, max, f32);
else
return GetMessage((LPMSG)pmsg, hwnd, min, max);
}
// PeekMessage32 = USER.819
BOOL WINAPI PeekMessage32(LPMSG32 pmsg, HWND hwnd, UINT min, UINT max, UINT flags, BOOL f32)
{
static BOOL (WINAPI *pfn)(LPMSG32, HWND, UINT, UINT, UINT, BOOL) = (LPVOID)-1;
if (pfn == (LPVOID)-1)
pfn = GetUserProc(819);
if (pfn)
return pfn(pmsg, hwnd, min, max, flags, f32);
else
return PeekMessage((LPMSG)pmsg, hwnd, min, max, flags);
}
// TranslateMessage32 = USER.821
BOOL WINAPI TranslateMessage32(const MSG32 FAR* pmsg, BOOL f32)
{
static BOOL (WINAPI *pfn)(const MSG32 FAR*, BOOL) = (LPVOID)-1;
if (pfn == (LPVOID)-1)
pfn = GetUserProc(821);
if (pfn)
return pfn(pmsg, f32);
else
return TranslateMessage((LPMSG)pmsg);
}
// DispatchMessage32 = USER.822
LONG WINAPI DispatchMessage32(const MSG32 FAR* pmsg, BOOL f32)
{
static BOOL (WINAPI *pfn)(const MSG32 FAR*, BOOL) = (LPVOID)-1;
if (pfn == (LPVOID)-1)
pfn = GetUserProc(822);
if (pfn)
return pfn(pmsg, f32);
else
return DispatchMessage((LPMSG)pmsg);
}
// CallMsgFilter32 = USER.823
BOOL WINAPI CallMsgFilter32(LPMSG32 pmsg, int x, BOOL f32)
{
static BOOL (WINAPI *pfn)(LPMSG32, int, BOOL) = (LPVOID)-1;
if (pfn == (LPVOID)-1)
pfn = GetUserProc(823);
if (pfn)
return pfn(pmsg, x, f32);
else
return CallMsgFilter((LPMSG)pmsg, x);
}
// IsDialogMessage32 = USER.824
BOOL WINAPI IsDialogMessage32(HWND hwnd, LPMSG32 pmsg, BOOL f32)
{
static BOOL (WINAPI *pfn)(HWND, LPMSG32, BOOL) = (LPVOID)-1;
if (pfn == (LPVOID)-1)
pfn = GetUserProc(824);
if (pfn)
return pfn(hwnd, pmsg, f32);
else
return IsDialogMessage(hwnd, (LPMSG)pmsg);
}
#ifdef FE_IME
// To get internal APIs within USER
LPVOID GetIMMProc(UINT ord)
{
return (LPVOID)GetProcAddress(GetModuleHandle("USER"), MAKEINTRESOURCE(ord));
}
BOOL WINAPI CreateImmTLink(DWORD idThread)
{
static BOOL (WINAPI *pfn)(DWORD) = (LPVOID)-1;
if (pfn == (LPVOID)-1)
pfn = GetIMMProc(952);
if(pfn) return pfn(idThread);
else return FALSE;
}
#endif
#endif
#endif