Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5456 lines
179 KiB

  1. #include "ctlspriv.h"
  2. #include "help.h" // Help IDs
  3. #include "prshti.h"
  4. #include "dlgcvt.h"
  5. #ifdef WX86
  6. #include <wx86ofl.h>
  7. #endif
  8. #define FLAG_CHANGED 0x0001
  9. #define DEFAULTHEADERHEIGHT 58 // in pixels
  10. #define DEFAULTTEXTDIVIDERGAP 5
  11. #define DEFAULTCTRLWIDTH 501 // page list window in new wizard style
  12. #define DEFAULTCTRLHEIGHT 253 // page list window in new wizard style
  13. #define TITLEX 22
  14. #define TITLEY 10
  15. #define SUBTITLEX 44
  16. #define SUBTITLEY 25
  17. // fixed sizes for the bitmap painted in the header section
  18. #define HEADERBITMAP_Y 5
  19. #define HEADERBITMAP_WIDTH 49
  20. #define HEADERBITMAP_CXBACK (5 + HEADERBITMAP_WIDTH)
  21. #define HEADERBITMAP_HEIGHT 49
  22. #define HEADERSUBTITLE_WRAPOFFSET 10
  23. // Fixed sizes for the watermark bitmap (Wizard97IE5 style)
  24. #define BITMAP_WIDTH 164
  25. #define BITMAP_HEIGHT 312
  26. #define DRAWTEXT_WIZARD97FLAGS (DT_LEFT | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL)
  27. LPVOID WINAPI MapSLFix(HANDLE);
  28. VOID WINAPI UnMapSLFixArray(int, HANDLE *);
  29. LRESULT CALLBACK WizardWndProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData);
  30. void ResetWizButtons(LPPROPDATA ppd);
  31. typedef struct // tie
  32. {
  33. TC_ITEMHEADER tci;
  34. HWND hwndPage;
  35. UINT state;
  36. } TC_ITEMEXTRA;
  37. #define CB_ITEMEXTRA (sizeof(TC_ITEMEXTRA) - sizeof(TC_ITEMHEADER))
  38. #define IS_WIZARDPSH(psh) ((psh).dwFlags & (PSH_WIZARD | PSH_WIZARD97 | PSH_WIZARD_LITE))
  39. #define IS_WIZARD(ppd) IS_WIZARDPSH(ppd->psh)
  40. void PageChange(LPPROPDATA ppd, int iAutoAdj);
  41. void RemovePropPageData(LPPROPDATA ppd, int nPage);
  42. HRESULT GetPageLanguage(PISP pisp, WORD *pwLang);
  43. UINT GetDefaultCharsetFromLang(LANGID wLang);
  44. LANGID NT5_GetUserDefaultUILanguage(void);
  45. //
  46. // IMPORTANT: The IDHELP ID should always be LAST since we just subtract
  47. // 1 from the number of IDs if no help in the page.
  48. // IDD_APPLYNOW should always be the FIRST ID for standard IDs since it
  49. // is sometimes not displayed and we'll start with index 1.
  50. //
  51. const static int IDs[] = {IDOK, IDCANCEL, IDD_APPLYNOW, IDHELP};
  52. const static int WizIDs[] = {IDD_BACK, IDD_NEXT, IDD_FINISH, IDCANCEL, IDHELP};
  53. const static WORD wIgnoreIDs[] = {IDD_PAGELIST, IDD_DIVIDER, IDD_TOPDIVIDER};
  54. // Prsht_PrepareTemplate action matrix. Please do not change without contacting [msadek]...
  55. const PSPT_ACTION g_PSPT_Action [PSPT_TYPE_MAX][PSPT_OS_MAX][PSPT_OVERRIDE_MAX]={
  56. PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_NOOVERRIDE
  57. PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_USEPAGELANG
  58. PSPT_ACTION_WIN9XCOMPAT, // PSPT_TYPE_MIRRORED, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_NOOVERRIDE
  59. PSPT_ACTION_WIN9XCOMPAT, // PSPT_TYPE_MIRRORED, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_USEPAGELANG
  60. PSPT_ACTION_FLIP, // PSPT_TYPE_MIRRORED, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_NOOVERRIDE
  61. PSPT_ACTION_FLIP, // PSPT_TYPE_MIRRORED, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_USEPAGELANG
  62. PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_WINNT5, PSPT_OVERRIDE_NOOVERRIDE
  63. PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_WINNT5, PSPT_OVERRIDE_USEPAGELANG
  64. PSPT_ACTION_NOACTION, // PSPT_TYPE_MIRRORED, PSPT_OS_OTHER, PSPT_OVERRIDE_NOOVERRIDE
  65. PSPT_ACTION_NOMIRRORING, // PSPT_TYPE_MIRRORED, PSPT_OS_OTHER, PSPT_OVERRIDE_USEPAGELANG
  66. PSPT_ACTION_NOACTION, // PSPT_TYPE_ENABLED, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_NOOVERRIDE
  67. PSPT_ACTION_NOACTION, // PSPT_TYPE_ENABLED, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_USEPAGELANG
  68. PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_NOOVERRIDE
  69. PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_USEPAGELANG
  70. PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_NOOVERRIDE
  71. PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_USEPAGELANG
  72. PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WINNT5, PSPT_OVERRIDE_NOOVERRIDE
  73. PSPT_ACTION_FLIP, // PSPT_TYPE_ENABLED, PSPT_OS_WINNT5, PSPT_OVERRIDE_USEPAGELANG
  74. PSPT_ACTION_NOACTION, // PSPT_TYPE_ENABLED, PSPT_OS_OTHER, PSPT_OVERRIDE_NOOVERRIDE
  75. PSPT_ACTION_NOMIRRORING, // PSPT_TYPE_ENABLED, PSPT_OS_OTHER, PSPT_OVERRIDE_USEPAGELANG
  76. PSPT_ACTION_NOACTION, // PSPT_TYPE_ENGLISH, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_NOOVERRIDE
  77. PSPT_ACTION_NOACTION, // PSPT_TYPE_ENGLISH, PSPT_OS_WIN95_BIDI, PSPT_OVERRIDE_USEPAGELANG
  78. PSPT_ACTION_LOADENGLISH, // PSPT_TYPE_ENGLISH, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_NOOVERRIDE
  79. PSPT_ACTION_FLIP, // PSPT_TYPE_ENGLISH, PSPT_OS_WIN98_BIDI, PSPT_OVERRIDE_USEPAGELANG
  80. PSPT_ACTION_FLIP, // PSPT_TYPE_ENGLISH, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_NOOVERRIDE
  81. PSPT_ACTION_FLIP, // PSPT_TYPE_ENGLISH, PSPT_OS_WINNT4_ENA, PSPT_OVERRIDE_USEPAGELANG
  82. PSPT_ACTION_LOADENGLISH, // PSPT_TYPE_ENGLISH, PSPT_OS_WINNT5, PSPT_OVERRIDE_NOOVERRIDE
  83. PSPT_ACTION_FLIP, // PSPT_TYPE_ENGLISH, PSPT_OS_WINNT5, PSPT_OVERRIDE_USEPAGELANG
  84. PSPT_ACTION_NOACTION, // PSPT_TYPE_ENGLISH, PSPT_OS_OTHER, PSPT_OVERRIDE_NOOVERRIDE
  85. PSPT_ACTION_NOMIRRORING, // PSPT_TYPE_ENGLISH, PSPT_OS_OTHER, PSPT_OVERRIDE_USEPAGELANG
  86. };
  87. // HACK FOR HIJAAK 95!
  88. //
  89. // Instead of creating
  90. // property sheet pages with CreatePropertySheetPage, they merely
  91. // take a pointer to a PROPSHEETPAGE structure and cast it to
  92. // HPROPSHEETPAGE. They got away with this on Win95 because Win95's
  93. // HPROPSHEETPAGE actually was 95% identical to a PROPSHEETPAGE.
  94. // (The missing 5% causes RIPs at property sheet destruction, which
  95. // Hijaak no doubt ignored.)
  96. //
  97. // On NT and IE5, this coincidence is not true.
  98. //
  99. // So validate that what we have is really a property sheet
  100. // structure by checking if it's on the heap at the
  101. // right place. If not, then make one.
  102. //
  103. HPROPSHEETPAGE WINAPI _Hijaak95Hack(LPPROPDATA ppd, HPROPSHEETPAGE hpage)
  104. {
  105. if (hpage && !LocalSize(PropSheetBase(hpage))) {
  106. // SLACKERS! Have to call CreatePropertySheetPage for them
  107. RIPMSG(0, "App passed HPROPSHEETPAGE not created by us; trying to cope");
  108. hpage = _CreatePropertySheetPage((LPCPROPSHEETPAGE)hpage,
  109. ppd->fFlags & PD_NEEDSHADOW,
  110. ppd->fFlags & PD_WX86);
  111. }
  112. return hpage;
  113. }
  114. void _SetTitle(HWND hDlg, LPPROPDATA ppd)
  115. {
  116. TCHAR szFormat[50];
  117. TCHAR szTitle[128];
  118. TCHAR szTemp[128 + 50];
  119. LPCTSTR pCaption = ppd->psh.pszCaption;
  120. if (IS_INTRESOURCE(pCaption)) {
  121. LoadString(ppd->psh.hInstance, (UINT)LOWORD(pCaption), szTitle, ARRAYSIZE(szTitle));
  122. pCaption = (LPCTSTR)szTitle;
  123. }
  124. if (ppd->psh.dwFlags & PSH_PROPTITLE)
  125. {
  126. if (*pCaption == 0)
  127. {
  128. // Hey, no title, we need a different resource for localization
  129. LocalizedLoadString(IDS_PROPERTIES, szTemp, ARRAYSIZE(szTemp));
  130. pCaption = szTemp;
  131. }
  132. else
  133. {
  134. LocalizedLoadString(IDS_PROPERTIESFOR, szFormat, ARRAYSIZE(szFormat));
  135. if ((lstrlen(pCaption) + 1 + lstrlen(szFormat) + 1) < ARRAYSIZE(szTemp))
  136. {
  137. StringCchPrintf(szTemp, ARRAYSIZE(szTemp), szFormat, pCaption);
  138. pCaption = szTemp;
  139. }
  140. }
  141. }
  142. if(ppd->psh.dwFlags & PSH_RTLREADING)
  143. {
  144. SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) | WS_EX_RTLREADING);
  145. }
  146. SetWindowText(hDlg, pCaption);
  147. }
  148. BOOL _SetHeaderFonts(HWND hDlg, LPPROPDATA ppd)
  149. {
  150. HFONT hFont;
  151. LOGFONT LogFont;
  152. GetObject(GetWindowFont(hDlg), sizeof(LogFont), &LogFont);
  153. CCAdjustForBold(&LogFont);
  154. if ((hFont = CreateFontIndirect(&LogFont)) == NULL)
  155. {
  156. ppd->hFontBold = NULL;
  157. return FALSE;
  158. }
  159. ppd->hFontBold = hFont;
  160. // Save the font as a window prop so we can delete it later
  161. return TRUE;
  162. }
  163. int _WriteHeaderTitle(LPPROPDATA ppd, HDC hdc, LPRECT prc, LPCTSTR pszTitle, BOOL bTitle, DWORD dwDrawFlags)
  164. {
  165. LPCTSTR pszOut;
  166. int cch;
  167. int cx, cy;
  168. TCHAR szTitle[MAX_PATH*4];
  169. HFONT hFontOld = NULL;
  170. HFONT hFont;
  171. int yDrawHeight = 0;
  172. if (IS_INTRESOURCE(pszTitle))
  173. {
  174. LoadString(GETPPSP(ppd, ppd->nCurItem)->hInstance, (UINT)LOWORD(pszTitle), szTitle, ARRAYSIZE(szTitle));
  175. pszOut = szTitle;
  176. }
  177. else
  178. pszOut = pszTitle;
  179. cch = lstrlen(pszOut);
  180. if (bTitle && ppd->hFontBold)
  181. hFont = ppd->hFontBold;
  182. else
  183. hFont = GetWindowFont(ppd->hDlg);
  184. hFontOld = SelectObject(hdc, hFont);
  185. if (bTitle)
  186. {
  187. cx = TITLEX;
  188. cy = TITLEY;
  189. ExtTextOut(hdc, cx, cy, 0, prc, pszOut, cch, NULL);
  190. }
  191. else
  192. {
  193. RECT rcWrap;
  194. CopyRect(&rcWrap, prc);
  195. rcWrap.left = SUBTITLEX;
  196. rcWrap.top = ppd->ySubTitle;
  197. yDrawHeight = DrawText(hdc, pszOut, cch, &rcWrap, dwDrawFlags);
  198. }
  199. if (hFontOld)
  200. SelectObject(hdc, hFontOld);
  201. return yDrawHeight;
  202. }
  203. // In Wizard97 only:
  204. // The subtitles user passed in could be larger than the two line spaces we give
  205. // them, especially in localization cases. So here we go through all subtitles and
  206. // compute the max space they need and set the header height so that no text is clipped
  207. int _ComputeHeaderHeight(LPPROPDATA ppd, int dxMax)
  208. {
  209. int dyHeaderHeight;
  210. int dyTextDividerGap;
  211. HDC hdc;
  212. dyHeaderHeight = DEFAULTHEADERHEIGHT;
  213. hdc = GetDC(ppd->hDlg);
  214. // First, let's get the correct text height and spacing, this can be used
  215. // as the title height and the between-lastline-and-divider spacing.
  216. {
  217. HFONT hFont, hFontOld;
  218. TEXTMETRIC tm;
  219. if (ppd->hFontBold)
  220. hFont = ppd->hFontBold;
  221. else
  222. hFont = GetWindowFont(ppd->hDlg);
  223. hFontOld = SelectObject(hdc, hFont);
  224. if (GetTextMetrics(hdc, &tm))
  225. {
  226. dyTextDividerGap = tm.tmExternalLeading;
  227. ppd->ySubTitle = max ((tm.tmHeight + tm.tmExternalLeading + TITLEY), SUBTITLEY);
  228. }
  229. else
  230. {
  231. dyTextDividerGap = DEFAULTTEXTDIVIDERGAP;
  232. ppd->ySubTitle = SUBTITLEY;
  233. }
  234. if (hFontOld)
  235. SelectObject(hdc, hFontOld);
  236. }
  237. // Second, get the subtitle text block height
  238. // should make into a function if shared
  239. {
  240. RECT rcWrap;
  241. UINT uPages;
  242. //
  243. // WIZARD97IE5 subtracts out the space used by the header bitmap.
  244. // WIZARD97IE4 uses the full width since the header bitmap
  245. // in IE4 is a watermark and occupies no space.
  246. //
  247. if (ppd->psh.dwFlags & PSH_WIZARD97IE4)
  248. rcWrap.right = dxMax;
  249. else
  250. rcWrap.right = dxMax - HEADERBITMAP_CXBACK - HEADERSUBTITLE_WRAPOFFSET;
  251. for (uPages = 0; uPages < ppd->psh.nPages; uPages++)
  252. {
  253. PROPSHEETPAGE *ppsp = GETPPSP(ppd, uPages);
  254. if (!(ppsp->dwFlags & PSP_HIDEHEADER) &&
  255. (ppsp->dwFlags & PSP_USEHEADERSUBTITLE))
  256. {
  257. int iSubHeaderHeight = _WriteHeaderTitle(ppd, hdc, &rcWrap, ppsp->pszHeaderSubTitle,
  258. FALSE, DT_CALCRECT | DRAWTEXT_WIZARD97FLAGS);
  259. if ((iSubHeaderHeight + ppd->ySubTitle) > dyHeaderHeight)
  260. dyHeaderHeight = iSubHeaderHeight + ppd->ySubTitle;
  261. }
  262. }
  263. }
  264. // If the header height has been recomputed, set the correct gap between
  265. // the text and the divider.
  266. if (dyHeaderHeight != DEFAULTHEADERHEIGHT)
  267. {
  268. ASSERT(dyHeaderHeight > DEFAULTHEADERHEIGHT);
  269. dyHeaderHeight += dyTextDividerGap;
  270. }
  271. ReleaseDC(ppd->hDlg, hdc);
  272. return dyHeaderHeight;
  273. }
  274. void MoveAllButtons(HWND hDlg, const int *pids, int idLast, int dx, int dy)
  275. {
  276. do {
  277. HWND hCtrl;
  278. RECT rcCtrl;
  279. int iCtrl = *pids;
  280. hCtrl = GetDlgItem(hDlg, iCtrl);
  281. GetWindowRect(hCtrl, &rcCtrl);
  282. //
  283. // If the dialog wizard window is mirrored, then rcl.right
  284. // in terms of screen coord is the near edge (lead). [samera]
  285. //
  286. if (IS_WINDOW_RTL_MIRRORED(hDlg))
  287. rcCtrl.left = rcCtrl.right;
  288. ScreenToClient(hDlg, (LPPOINT)&rcCtrl);
  289. SetWindowPos(hCtrl, NULL, rcCtrl.left + dx,
  290. rcCtrl.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  291. } while(*(pids++) != idLast);
  292. }
  293. void RemoveButton(HWND hDlg, int idRemove, const int *pids)
  294. {
  295. int idPrev = 0;
  296. HWND hRemove = NULL;
  297. HWND hPrev;
  298. RECT rcRemove, rcPrev;
  299. int iWidth = 0;
  300. const int *pidRemove;
  301. // get the previous id
  302. for (pidRemove = pids; *pidRemove != idRemove; pidRemove++)
  303. idPrev = *pidRemove;
  304. if (idPrev) {
  305. hRemove = GetDlgItem(hDlg, idRemove);
  306. hPrev = GetDlgItem(hDlg, idPrev);
  307. GetWindowRect(hRemove, &rcRemove);
  308. GetWindowRect(hPrev, &rcPrev);
  309. //
  310. // If the dialog window is mirrored, then the prev button
  311. // will be ahead (to the right) of the button-to-be-removed.
  312. // As a result, the subtraction will be definitely negative,
  313. // so let's convert it to be positive. [samera]
  314. //
  315. if (IS_WINDOW_RTL_MIRRORED(hDlg))
  316. iWidth = rcPrev.right - rcRemove.right;
  317. else
  318. iWidth = rcRemove.right - rcPrev.right;
  319. }
  320. MoveAllButtons(hDlg, pids, idRemove, iWidth, 0);
  321. if (hRemove)
  322. ShowWindow(hRemove, SW_HIDE);
  323. // Cannot disable the window; see Prsht_ButtonSubclassProc for explanation.
  324. // WRONG - EnableWindow(hRemove, FALSE);
  325. }
  326. typedef struct LOGPALETTE256 {
  327. WORD palVersion;
  328. WORD palNumEntries;
  329. union {
  330. PALETTEENTRY rgpal[256];
  331. RGBQUAD rgq[256];
  332. } u;
  333. } LOGPALETTE256;
  334. HPALETTE PaletteFromBmp(HBITMAP hbm)
  335. {
  336. LOGPALETTE256 pal;
  337. int i,n;
  338. HDC hdc;
  339. HPALETTE hpl;
  340. hdc = CreateCompatibleDC(NULL);
  341. SelectObject(hdc, hbm);
  342. n = GetDIBColorTable(hdc, 0, 256, pal.u.rgq);
  343. if (n) // DIB section with color table
  344. {
  345. // Palettes are such a hassle. GetDIBColorTable returns RGBQUADs, whereas
  346. // LOGPALETTE wants PALETTEENTRYss, and the two are reverse-endian
  347. // of each other.
  348. for (i= 0 ; i < n; i++)
  349. {
  350. PALETTEENTRY pe;
  351. pe.peRed = pal.u.rgq[i].rgbRed;
  352. pe.peGreen = pal.u.rgq[i].rgbGreen;
  353. pe.peBlue = pal.u.rgq[i].rgbBlue;
  354. pe.peFlags = 0;
  355. pal.u.rgpal[i] = pe;
  356. }
  357. pal.palVersion = 0x0300;
  358. pal.palNumEntries = (WORD)n;
  359. hpl = CreatePalette((LPLOGPALETTE)&pal);
  360. }
  361. else // Not a DIB section or no color table
  362. {
  363. hpl = CreateHalftonePalette(hdc);
  364. }
  365. DeleteDC(hdc);
  366. return hpl;
  367. }
  368. // -------------- stolen from user code -------------------------------------
  369. //
  370. // GetCharDimensions(hDC, psiz)
  371. //
  372. // This function loads the Textmetrics of the font currently selected into
  373. // the given hDC and saves the height and Average char width of the font
  374. // (NOTE: the
  375. // AveCharWidth value returned by the text metrics call is wrong for
  376. // proportional fonts -- so, we compute them).
  377. //
  378. // -------------- stolen from user code --------------------------------------
  379. TCHAR AveCharWidthData[52+1] = TEXT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
  380. void GetCharDimensions(HDC hDC, SIZE *psiz)
  381. {
  382. TEXTMETRIC tm;
  383. // Store the System Font metrics info.
  384. GetTextMetrics(hDC, &tm);
  385. if (!(tm.tmPitchAndFamily & TMPF_FIXED_PITCH)) // the name is opposite:)
  386. psiz->cx = tm.tmAveCharWidth;
  387. else
  388. {
  389. // Change from tmAveCharWidth. We will calculate a true average as
  390. // opposed to the one returned by tmAveCharWidth. This works better
  391. // when dealing with proportional spaced fonts. -- ROUND UP
  392. if (GetTextExtentPoint32(hDC, AveCharWidthData, 52, psiz) == TRUE)
  393. {
  394. psiz->cx = ((psiz->cx / 26) + 1) / 2;
  395. }
  396. else
  397. psiz->cx = tm.tmAveCharWidth;
  398. }
  399. psiz->cy = tm.tmHeight;
  400. }
  401. //
  402. // It is a feature that USER considers keyboard accelerators live even if
  403. // the control is hidden. This lets you put a hidden static in front of
  404. // a custom control to get an accelerator attached to the custom control.
  405. //
  406. // Unfortunately, it means that the &F accelerator for "Finish" activates
  407. // the Finish button even when the Finish button is hidden. The normal
  408. // workaround for this is to disable the control, but that doesn't work
  409. // because Microsoft PhotoDraw runs around and secretly hides and shows
  410. // buttons without going through PSM_SETWIZBUTTONS, so they end up showing
  411. // a disabled window and their wizard stops working.
  412. //
  413. // So instead, we subclass the buttons and customize their WM_GETDLGCODE
  414. // so that when the control is hidden, they disable their accelerators.
  415. //
  416. LRESULT CALLBACK Prsht_ButtonSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT_PTR uID, ULONG_PTR dwRefData)
  417. {
  418. LRESULT lres;
  419. switch (wm)
  420. {
  421. case WM_GETDLGCODE:
  422. lres = DefSubclassProc(hwnd, wm, wp, lp);
  423. if (!IsWindowVisible(hwnd))
  424. {
  425. // To remove yourself from the mnemonic search, you have to
  426. // return DLGC_WANTCHAR if you are give a NULL LPMSG pointer.
  427. // Normally, the dialog manager sends a real LPMSG containing
  428. // the message that just got received, but when it's poking
  429. // around looking for accelerators, it doesn't give you a
  430. // message at all. It is in that case that you want to
  431. // say, "Hey, I will process the (nonexistent) message".
  432. // This tricks USER into thinking you're an edit control, so
  433. // it won't scan your for mnemonics.
  434. if ((LPMSG)lp == NULL)
  435. lres |= DLGC_WANTCHARS;
  436. }
  437. break;
  438. case WM_NCDESTROY:
  439. // Clean up subclass
  440. RemoveWindowSubclass(hwnd, Prsht_ButtonSubclassProc, 0);
  441. lres = DefSubclassProc(hwnd, wm, wp, lp);
  442. break;
  443. default:
  444. lres = DefSubclassProc(hwnd, wm, wp, lp);
  445. break;
  446. }
  447. return lres;
  448. }
  449. void Prsht_SubclassButton(HWND hDlg, UINT idd)
  450. {
  451. SetWindowSubclass(GetDlgItem(hDlg, idd), Prsht_ButtonSubclassProc, 0, 0);
  452. }
  453. BOOL CompareFontFaceW(LPCWSTR lpwz1, LPCWSTR lpwz2, BOOL fBitCmp)
  454. {
  455. return lstrcmpiW(lpwz1, lpwz2);
  456. }
  457. //
  458. // GetPageFontMetrics
  459. //
  460. // synopsis:
  461. //
  462. // Get the real font metrics from PAGEFONTDATA. Used in InitPropSheetDlg() to
  463. // calculate the physical page size based on the font specified in page templates
  464. //
  465. // fML is set if we are in here because of an ML scenario, in which case the
  466. // font names need to be mapped.
  467. //
  468. BOOL GetPageFontMetrics(LPPROPDATA ppd, PPAGEFONTDATA ppfd, BOOL fML)
  469. {
  470. LOGFONT lf = {0};
  471. HFONT hFont;
  472. HRESULT fRc = FALSE;
  473. HDC hdc;
  474. if (ppfd && (ppfd->PointSize > 0) && ppfd->szFace[0])
  475. {
  476. // font name mapping
  477. // should be done only for the platform less than NT5
  478. // NT5 is supposed to work with native typeface on any system locale.
  479. //
  480. if (!staticIsOS(OS_WIN2000ORGREATER) && fML)
  481. {
  482. // replace native font face name to single byte name for non-native platform
  483. typedef struct tagFontFace
  484. {
  485. BOOL fBitCmp;
  486. LPCWSTR lpEnglish;
  487. LPCWSTR lpNative;
  488. } FONTFACE, *LPFONTFACE;
  489. const static FONTFACE s_FontTbl[] =
  490. {
  491. { FALSE, L"MS Gothic", L"MS UI Gothic" },
  492. { TRUE, L"MS Gothic", L"\xff2d\xff33 \xff30\x30b4\x30b7\x30c3\x30af" },
  493. { TRUE, L"GulimChe", L"\xad74\xb9bc" },
  494. { TRUE, L"MS Song", L"\x5b8b\x4f53" },
  495. { TRUE, L"MingLiU", L"\x65b0\x7d30\x660e\x9ad4" }
  496. };
  497. int i;
  498. for (i = 0; i < ARRAYSIZE(s_FontTbl); i++)
  499. {
  500. if (!CompareFontFaceW(ppfd->szFace, s_FontTbl[i].lpNative, s_FontTbl[i].fBitCmp))
  501. {
  502. StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), s_FontTbl[i].lpEnglish);
  503. break;
  504. }
  505. }
  506. if (i >= ARRAYSIZE(s_FontTbl))
  507. {
  508. StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), ppfd->szFace);
  509. }
  510. }
  511. else
  512. {
  513. StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), ppfd->szFace);
  514. }
  515. // Try to use the cache
  516. if (ppfd->iCharset == ppd->pfdCache.iCharset &&
  517. ppfd->bItalic == ppd->pfdCache.bItalic &&
  518. ppfd->PointSize == ppd->pfdCache.PointSize &&
  519. lstrcmpiW(ppfd->szFace, ppd->pfdCache.szFace) == 0) {
  520. fRc = TRUE;
  521. } else {
  522. if (hdc = GetDC(ppd->hDlg))
  523. {
  524. lf.lfHeight = -MulDiv(ppfd->PointSize, GetDeviceCaps(hdc,LOGPIXELSY), 72);
  525. lf.lfCharSet = (BYTE)ppfd->iCharset;
  526. lf.lfItalic = (BYTE)ppfd->bItalic;
  527. lf.lfWeight = FW_NORMAL;
  528. hFont = CreateFontIndirectW(&lf);
  529. if (hFont)
  530. {
  531. HFONT hFontOld = SelectObject(hdc, hFont);
  532. GetCharDimensions(hdc, &ppd->sizCache);
  533. if (hFontOld)
  534. SelectObject(hdc, hFontOld);
  535. DeleteObject(hFont);
  536. // Save these font metrics into the cache
  537. ppd->pfdCache = *ppfd;
  538. fRc = TRUE;
  539. }
  540. ReleaseDC(ppd->hDlg, hdc);
  541. }
  542. }
  543. }
  544. return fRc;
  545. }
  546. //
  547. // The "ideal page size" of a property sheet is the maximum size of all
  548. // pages.
  549. //
  550. // GIPS_SKIPINTERIOR97HEIGHT and GIPS_SKIPEXTERIOR97HEIGHT selective
  551. // exclude Wiz97 pages from the height computation. They are important
  552. // because interior pages are shorter than exterior pages by
  553. // ppd->cyHeaderHeight.
  554. //
  555. #define GIPS_SKIPINTERIOR97HEIGHT 1
  556. #define GIPS_SKIPEXTERIOR97HEIGHT 2
  557. void Prsht_GetIdealPageSize(LPPROPDATA ppd, PSIZE psiz, UINT flags)
  558. {
  559. UINT uPages;
  560. *psiz = ppd->sizMin;
  561. for (uPages = 0; uPages < ppd->psh.nPages; uPages++)
  562. {
  563. PISP pisp = GETPISP(ppd, uPages);
  564. int cy = pisp->_pfx.siz.cy;
  565. if (ppd->psh.dwFlags & PSH_WIZARD97)
  566. {
  567. if (pisp->_psp.dwFlags & PSP_HIDEHEADER)
  568. {
  569. if (flags & GIPS_SKIPEXTERIOR97HEIGHT) goto skip;
  570. }
  571. else
  572. {
  573. if (flags & GIPS_SKIPINTERIOR97HEIGHT) goto skip;
  574. }
  575. }
  576. if (psiz->cy < cy)
  577. psiz->cy = cy;
  578. skip:;
  579. if (psiz->cx < pisp->_pfx.siz.cx)
  580. psiz->cx = pisp->_pfx.siz.cx;
  581. }
  582. }
  583. #define IsMSShellDlgMapped(langid) (PRIMARYLANGID(langid) == LANG_JAPANESE)
  584. //
  585. // Given a page, decide what size it wants to be and save it in the
  586. // pisp->_pfx.siz.
  587. //
  588. void Prsht_ComputeIdealPageSize(LPPROPDATA ppd, PISP pisp, PAGEINFOEX *ppi)
  589. {
  590. BOOL fUsePageFont;
  591. // pressume page and frame dialog are in same character set
  592. LANGID wPageLang = ppd->wFrameLang;
  593. int iPageCharset = DEFAULT_CHARSET;
  594. if (SUCCEEDED(GetPageLanguage(pisp, &wPageLang)))
  595. {
  596. // GetPageLanguage fails if page is marked PSP_DLGINDIRECT;
  597. // we'll try to recover from that later. For now,
  598. // we leave pagelang to DEFAULT_CHARSET and see if we can take
  599. // the charset info from template EX.
  600. //
  601. // if PSH_USEPAGELANG is specified, we can assume that
  602. // page charset == frame charset and no need for ML adjustment
  603. // *except for* the case of NT Japanese version that replaces
  604. // frame's MS Shell Dlg to their native font. We handle this
  605. // exception later where we set up fUsePageFont;
  606. //
  607. if (!(ppd->psh.dwFlags & PSH_USEPAGELANG)
  608. && wPageLang != ppd->wFrameLang)
  609. {
  610. iPageCharset = GetDefaultCharsetFromLang(wPageLang);
  611. }
  612. else
  613. iPageCharset = ppd->iFrameCharset;
  614. }
  615. // Use the font in the page if any of these conditions are met:
  616. //
  617. // A) It's a SHELLFONT page. Do this even if the font is not
  618. // "MS Shell Dlg 2". This gives apps a way to specify that
  619. // their custom-font page should be measured against the
  620. // font in the page rather than in the frame font.
  621. //
  622. // B) ML scenario - complicated original comment below...
  623. //
  624. // 1) we've detected lang in the caller's resource and
  625. // it's different from the frame dialog
  626. // 2) the caller's page doesn't have lang info or we've
  627. // failed to get it (iPageCharset == DEFAULT_CHARSET),
  628. // then we find the page is described with DLGTEMPLATEEX
  629. // and has meaningful charset specified (!= defaultcharset)
  630. // *and* the charset is different from frame's
  631. // 3) the exception for NT Japanese platform that maps
  632. // MS Shell Dlg to their native font. For US Apps to
  633. // work on these platforms they typically specify
  634. // PSH_USEPAGELANG to get English buttons on frame
  635. // but they still need to get the frame sized based on
  636. // page font
  637. //
  638. // Otherwise, IE4 compat **requires** that we use the frame font.
  639. // ISVs have hacked around this historical bug by having large
  640. // dialog templates with extra space in them.
  641. //
  642. fUsePageFont =
  643. /* --- A) It's a SHELLFONT page --- */
  644. IsPageInfoSHELLFONT(ppi) ||
  645. /* --- B) ML scenario --- */
  646. ((ppd->psh.dwFlags & PSH_USEPAGELANG)
  647. && IsMSShellDlgMapped(NT5_GetUserDefaultUILanguage())) ||
  648. (ppd->iFrameCharset != iPageCharset
  649. && (iPageCharset != DEFAULT_CHARSET
  650. || (ppi->pfd.iCharset != DEFAULT_CHARSET
  651. && ppi->pfd.iCharset != ppd->iFrameCharset)));
  652. if (fUsePageFont &&
  653. GetPageFontMetrics(ppd, &ppi->pfd, MLIsMLHInstance(pisp->_psp.hInstance)))
  654. {
  655. // Compute Real Dialog Unit for the page
  656. pisp->_pfx.siz.cx = MulDiv(ppi->pt.x, ppd->sizCache.cx, 4);
  657. pisp->_pfx.siz.cy = MulDiv(ppi->pt.y, ppd->sizCache.cy, 8);
  658. } else {
  659. RECT rcT;
  660. // IE4 compat - Use the frame font
  661. rcT.top = rcT.left = 0; // Win95 will fault if these are uninit
  662. rcT.right = ppi->pt.x;
  663. rcT.bottom = ppi->pt.y;
  664. MapDialogRect(ppd->hDlg, &rcT);
  665. pisp->_pfx.siz.cx = rcT.right;
  666. pisp->_pfx.siz.cy = rcT.bottom;
  667. //
  668. // If this is PSP_DLGINDIRECT but the character set and face name
  669. // say this is a "generic" property sheet, then take the frame
  670. // font or the page font, whichever is bigger.
  671. //
  672. // This fixes the Chinese MingLiu font, which is not as tall as
  673. // the English MS Sans Serif font. Without this fix, we would
  674. // use MingLui (the frame font), and then your MS Shell Dlg pages
  675. // would get truncated.
  676. //
  677. // (Truncated property sheets is what you got in NT4, but I guess
  678. // looking pretty is more important than bug-for-bug compatibility.
  679. // Who knows what apps will be broken by this change.)
  680. //
  681. if ((pisp->_psp.dwFlags & PSP_DLGINDIRECT) &&
  682. ppi->pfd.iCharset == DEFAULT_CHARSET &&
  683. lstrcmpiW(ppi->pfd.szFace, L"MS Shell Dlg") == 0)
  684. {
  685. int i;
  686. GetPageFontMetrics(ppd, &ppi->pfd, FALSE);
  687. i = MulDiv(ppi->pt.x, ppd->sizCache.cx, 4);
  688. if (pisp->_pfx.siz.cx < i)
  689. pisp->_pfx.siz.cx = i;
  690. i = MulDiv(ppi->pt.y, ppd->sizCache.cy, 8);
  691. if (pisp->_pfx.siz.cy < i)
  692. pisp->_pfx.siz.cy = i;
  693. }
  694. }
  695. }
  696. void InitPropSheetDlg(HWND hDlg, LPPROPDATA ppd)
  697. {
  698. PAGEINFOEX pi;
  699. int dxDlg, dyDlg, dyGrow, dxGrow;
  700. RECT rcMinSize, rcDlg, rcPage, rcOrigTabs;
  701. UINT uPages;
  702. HIMAGELIST himl = NULL;
  703. TC_ITEMEXTRA tie;
  704. TCHAR szStartPage[128];
  705. LPCTSTR pStartPage = NULL;
  706. UINT nStartPage;
  707. BOOL fPrematurePages = FALSE;
  708. #ifdef DEBUG
  709. BOOL fStartPageFound = FALSE;
  710. #endif
  711. LANGID langidMUI;
  712. MONITORINFO mMonitorInfo;
  713. HMONITOR hMonitor;
  714. BOOL bMirrored = FALSE;
  715. // set our instance data pointer
  716. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)ppd);
  717. // Make sure this gets inited early on.
  718. ppd->nCurItem = 0;
  719. // By default we allow the "Apply" button to be enabled
  720. ppd->fAllowApply = TRUE;
  721. if (IS_WIZARD(ppd)) {
  722. // Subclass our buttons so their mnemonics won't mess up applications
  723. // that run around hiding and showing the buttons behind our back.
  724. Prsht_SubclassButton(hDlg, IDD_BACK);
  725. Prsht_SubclassButton(hDlg, IDD_NEXT);
  726. Prsht_SubclassButton(hDlg, IDD_FINISH);
  727. } else
  728. _SetTitle(hDlg, ppd);
  729. if (ppd->psh.dwFlags & PSH_USEICONID)
  730. {
  731. ppd->psh.H_hIcon = LoadImage(ppd->psh.hInstance, ppd->psh.H_pszIcon, IMAGE_ICON, g_cxSmIcon, g_cySmIcon, LR_DEFAULTCOLOR);
  732. }
  733. if ((ppd->psh.dwFlags & (PSH_USEICONID | PSH_USEHICON)) && ppd->psh.H_hIcon)
  734. SendMessage(hDlg, WM_SETICON, FALSE, (LPARAM)(UINT_PTR)ppd->psh.H_hIcon);
  735. ppd->hDlg = hDlg;
  736. // IDD_PAGELIST should definitely exist
  737. ppd->hwndTabs = GetDlgItem(hDlg, IDD_PAGELIST);
  738. ASSERT(ppd->hwndTabs);
  739. TabCtrl_SetItemExtra(ppd->hwndTabs, CB_ITEMEXTRA);
  740. // nStartPage is either ppd->psh.H_nStartPage or the page pStartPage
  741. nStartPage = ppd->psh.H_nStartPage;
  742. if (ppd->psh.dwFlags & PSH_USEPSTARTPAGE)
  743. {
  744. nStartPage = 0; // Assume we don't find the page
  745. pStartPage = ppd->psh.H_pStartPage;
  746. if (IS_INTRESOURCE(pStartPage))
  747. {
  748. szStartPage[0] = TEXT('\0');
  749. LoadString(ppd->psh.hInstance, (UINT)LOWORD(pStartPage),
  750. szStartPage, ARRAYSIZE(szStartPage));
  751. pStartPage = szStartPage;
  752. }
  753. }
  754. tie.hwndPage = NULL;
  755. tie.tci.pszText = pi.szCaption;
  756. tie.state = 0;
  757. SendMessage(ppd->hwndTabs, WM_SETREDRAW, FALSE, 0L);
  758. // load langid we chose for frame dialog template
  759. ppd->wFrameLang = LANGIDFROMLCID(CCGetProperThreadLocale(NULL));
  760. // it's charset that really matters to font
  761. ppd->iFrameCharset = GetDefaultCharsetFromLang(ppd->wFrameLang);
  762. langidMUI = GetMUILanguage();
  763. for (uPages = 0; uPages < ppd->psh.nPages; uPages++)
  764. {
  765. PISP pisp = GETPISP(ppd, uPages);
  766. if (GetPageInfoEx(ppd, pisp, &pi, langidMUI, GPI_ALL))
  767. {
  768. Prsht_ComputeIdealPageSize(ppd, pisp, &pi);
  769. // Add the page to the end of the tab list
  770. tie.tci.iImage = -1;
  771. tie.tci.mask = TCIF_TEXT | TCIF_PARAM | TCIF_IMAGE | (pi.bRTL ? TCIF_RTLREADING : 0);
  772. if (pi.hIcon) {
  773. if (!himl) {
  774. UINT flags = ILC_MASK;
  775. if(IS_WINDOW_RTL_MIRRORED(ppd->hwndTabs)) {
  776. flags |= ILC_MIRROR;
  777. }
  778. himl = ImageList_Create(g_cxSmIcon, g_cySmIcon, flags, 8, 4);
  779. TabCtrl_SetImageList(ppd->hwndTabs, himl);
  780. }
  781. tie.tci.iImage = ImageList_AddIcon(himl, pi.hIcon);
  782. // QUESTION raymondc - we always destroy even if PSP_USEHICON?
  783. DestroyIcon(pi.hIcon);
  784. }
  785. // QUESTION? What if this fails? Do we want to destroy the page?
  786. if (TabCtrl_InsertItem(ppd->hwndTabs, 1000, &tie.tci) >= 0)
  787. {
  788. // Nothing to do; all the code that was here got moved elsewhere
  789. }
  790. // remember if any page wants premature init
  791. if (pisp->_psp.dwFlags & PSP_PREMATURE)
  792. fPrematurePages = TRUE;
  793. // if the user is specifying the startpage via title, check it here
  794. if ((ppd->psh.dwFlags & PSH_USEPSTARTPAGE) &&
  795. !lstrcmpi(pStartPage, pi.szCaption))
  796. {
  797. nStartPage = uPages;
  798. #ifdef DEBUG
  799. fStartPageFound = TRUE;
  800. #endif
  801. }
  802. }
  803. else
  804. {
  805. DebugMsg(DM_ERROR, TEXT("PropertySheet failed to GetPageInfo"));
  806. RemovePropPageData(ppd, uPages--);
  807. }
  808. }
  809. SendMessage(ppd->hwndTabs, WM_SETREDRAW, TRUE, 0L);
  810. if (ppd->psh.pfnCallback) {
  811. #ifdef WX86
  812. if (ppd->fFlags & PD_WX86)
  813. Wx86Callback(ppd->psh.pfnCallback, hDlg, PSCB_INITIALIZED, 0);
  814. else
  815. #endif
  816. ppd->psh.pfnCallback(hDlg, PSCB_INITIALIZED, 0);
  817. }
  818. //
  819. // Now compute the size of the tab control.
  820. //
  821. // First get the rectangle for the whole dialog
  822. GetWindowRect(hDlg, &rcDlg);
  823. // For WIZARD_LITE style wizards, we stretch the tabs page and sunken divider
  824. // to cover the whole wizard (without the border)
  825. if (ppd->psh.dwFlags & PSH_WIZARD_LITE)
  826. {
  827. // Stretch the divider to the whole width of the wizard
  828. RECT rcDiv, rcDlgClient;
  829. HWND hDiv;
  830. // we allow both PSH_WIZARD and PSH_WIZARD_LITE to be set
  831. // it's exactly the same as setting just PSH_WIZARD_LITE
  832. RIPMSG(!(ppd->psh.dwFlags & PSH_WIZARD97),
  833. "Cannot combine PSH_WIZARD_LITE with PSH_WIZARD97");
  834. // but some people do it anyway, so turn off
  835. ppd->psh.dwFlags &= ~PSH_WIZARD97;
  836. // NOTE: GetDlgItemRect returns a rectangle relative to hDlg
  837. hDiv = GetDlgItemRect(hDlg, IDD_DIVIDER, &rcDiv);
  838. if (hDiv)
  839. SetWindowPos(hDiv, NULL, 0, rcDiv.top, RECTWIDTH(rcDlg),
  840. RECTHEIGHT(rcDiv), SWP_NOZORDER | SWP_NOACTIVATE);
  841. GetClientRect(hDlg, &rcDlgClient);
  842. // Stretch the page list control to cover the whole wizard client area above
  843. // the divider
  844. SetWindowPos(ppd->hwndTabs, NULL, 0, 0, RECTWIDTH(rcDlgClient),
  845. rcDiv.top, SWP_NOZORDER | SWP_NOACTIVATE);
  846. }
  847. //
  848. // While we're thinking about it, don't let people set both
  849. // WIZARD97IE4 *and* WIZARD97IE5. That's just way too strange.
  850. //
  851. if (ppd->psh.dwFlags & PSH_WIZARD97IE4)
  852. ppd->psh.dwFlags &= ~PSH_WIZARD97IE5;
  853. // Get the rectangle of the pagelist control in pixels.
  854. GetClientRect(ppd->hwndTabs, &rcOrigTabs);
  855. ppd->sizMin.cx = rcOrigTabs.right;
  856. ppd->sizMin.cy = rcOrigTabs.bottom;
  857. // Compute rcPage = Size of page area in pixels
  858. // For now, we only care about interior pages; we'll deal with exterior
  859. // pages later.
  860. rcPage.left = rcPage.top = 0;
  861. Prsht_GetIdealPageSize(ppd, (SIZE *)&rcPage.right, GIPS_SKIPEXTERIOR97HEIGHT);
  862. //
  863. // IE4's Wizard97 assumed that all exterior pages were exactly
  864. // DEFAULTHEADERHEIGHT dlu's taller than interior pages. That's
  865. // right, DEFAULTHEADERHEIGHT is a pixel count, but IE4 messed up
  866. // and used it as a dlu count here.
  867. //
  868. if (ppd->psh.dwFlags & PSH_WIZARD97IE4)
  869. {
  870. SIZE sizT;
  871. SetRect(&rcMinSize, 0, 0, 0, DEFAULTHEADERHEIGHT);
  872. MapDialogRect(hDlg, &rcMinSize);
  873. Prsht_GetIdealPageSize(ppd, &sizT, GIPS_SKIPINTERIOR97HEIGHT);
  874. if (rcPage.bottom < sizT.cy - rcMinSize.bottom)
  875. rcPage.bottom = sizT.cy - rcMinSize.bottom;
  876. }
  877. // Now compute the minimum size for the page region
  878. rcMinSize = rcPage;
  879. //
  880. // If this is a wizard then set the size of the page area to the entire
  881. // size of the control. If it is a normal property sheet then adjust for
  882. // the tabs, resize the control, and then compute the size of the page
  883. // region only.
  884. //
  885. if (IS_WIZARD(ppd))
  886. // initialize
  887. rcPage = rcMinSize;
  888. else
  889. {
  890. int i;
  891. RECT rcAdjSize;
  892. // initialize
  893. for (i = 0; i < 2; i++) {
  894. rcAdjSize = rcMinSize;
  895. TabCtrl_AdjustRect(ppd->hwndTabs, TRUE, &rcAdjSize);
  896. rcAdjSize.right -= rcAdjSize.left;
  897. rcAdjSize.bottom -= rcAdjSize.top;
  898. rcAdjSize.left = rcAdjSize.top = 0;
  899. if (rcAdjSize.right < rcMinSize.right)
  900. rcAdjSize.right = rcMinSize.right;
  901. if (rcAdjSize.bottom < rcMinSize.bottom)
  902. rcAdjSize.bottom = rcMinSize.bottom;
  903. SetWindowPos(ppd->hwndTabs, NULL, 0,0, rcAdjSize.right, rcAdjSize.bottom,
  904. SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  905. }
  906. rcPage = rcMinSize = rcAdjSize;
  907. TabCtrl_AdjustRect(ppd->hwndTabs, FALSE, &rcPage);
  908. }
  909. //
  910. // rcMinSize now contains the size of the control, including the tabs, and
  911. // rcPage is the rect containing the page portion (without the tabs).
  912. //
  913. // For wizard97:
  914. // Now we have the correct width for our wizard, let's compute the
  915. // header height based on that, shift the tab window and the pages
  916. // window down accordingly.
  917. //
  918. dyGrow = 0;
  919. if (ppd->psh.dwFlags & PSH_WIZARD97)
  920. {
  921. RECT rcTabs;
  922. SIZE sizT;
  923. // NOTE: we don't directly use rcPage because the verticle position for
  924. // ppd->hwndTabs is not determined, yet, even though the horizontal is
  925. // already computed. Therefore, we can only use rcPageCopy.right not
  926. // rcPageCopy.bottom in the following code.
  927. RECT rcTemp;
  928. CopyRect(&rcTemp, &rcPage);
  929. MapWindowPoints(ppd->hwndTabs, hDlg, (LPPOINT)&rcTemp, 2);
  930. GetWindowRect(ppd->hwndTabs, &rcTabs);
  931. MapWindowRect(NULL, hDlg, &rcTabs);
  932. // Set the header fonts first because we need to use the bold font
  933. // to compute the title height
  934. _SetHeaderFonts(hDlg, ppd);
  935. // Adjust the header height
  936. ppd->cyHeaderHeight = _ComputeHeaderHeight(ppd, rcTemp.right);
  937. // Since the app can change the subheader text on the fly,
  938. // our computation of the header height might end up wrong later.
  939. // Allow ISVs to precompensate for that by setting their exterior
  940. // pages larger than the interior pages by the amount they want
  941. // to reserve.
  942. // So if the largest external page is larger than the largest internal
  943. // page, then expand to enclose the external pages too.
  944. // IE4 Wizard97 didn't do this and MFC relies on the bug.
  945. if (!(ppd->psh.dwFlags & PSH_WIZARD97IE4))
  946. {
  947. // A margin of 7dlu's is placed above the page, and another
  948. // margin of 7 dlu's is placed below.
  949. SetRect(&rcTemp, 0, 0, 0, 7+7);
  950. MapDialogRect(hDlg, &rcTemp);
  951. Prsht_GetIdealPageSize(ppd, &sizT, GIPS_SKIPINTERIOR97HEIGHT);
  952. if (ppd->cyHeaderHeight < sizT.cy - RECTHEIGHT(rcPage) - rcTemp.bottom)
  953. ppd->cyHeaderHeight = sizT.cy - RECTHEIGHT(rcPage) - rcTemp.bottom;
  954. }
  955. // Move the tab window right under the header
  956. dyGrow += ppd->cyHeaderHeight;
  957. SetWindowPos(ppd->hwndTabs, NULL, rcTabs.left, rcTabs.top + dyGrow,
  958. RECTWIDTH(rcTabs), RECTHEIGHT(rcTabs), SWP_NOZORDER | SWP_NOACTIVATE);
  959. }
  960. //
  961. // Resize the dialog to make room for the control's new size. This can
  962. // only grow the size.
  963. //
  964. dxGrow = rcMinSize.right - rcOrigTabs.right;
  965. dxDlg = rcDlg.right - rcDlg.left + dxGrow;
  966. dyGrow += rcMinSize.bottom - rcOrigTabs.bottom;
  967. dyDlg = rcDlg.bottom - rcDlg.top + dyGrow;
  968. //
  969. // Cascade property sheet windows (only for comctl32 and commctrl)
  970. //
  971. //
  972. // HACK: Putting CW_USEDEFAULT in dialog template does not work because
  973. // CreateWindowEx ignores it unless the window has WS_OVERLAPPED, which
  974. // is not appropriate for a property sheet.
  975. //
  976. {
  977. const TCHAR c_szStatic[] = TEXT("Static");
  978. UINT swp = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE;
  979. if (!IsWindow(ppd->psh.hwndParent)) {
  980. HWND hwndT = CreateWindowEx(0, c_szStatic, NULL,
  981. WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT,
  982. 0, 0, NULL, NULL, HINST_THISDLL, NULL);
  983. if (hwndT) {
  984. GetWindowRect(hwndT, &rcDlg);
  985. swp = SWP_NOZORDER | SWP_NOACTIVATE;
  986. DestroyWindow(hwndT);
  987. }
  988. } else {
  989. GetWindowRect(ppd->psh.hwndParent, &rcDlg);
  990. if (IsWindowVisible(ppd->psh.hwndParent)) {
  991. bMirrored = IS_WINDOW_RTL_MIRRORED(ppd->psh.hwndParent);
  992. rcDlg.top += g_cySmIcon;
  993. if(bMirrored)
  994. {
  995. rcDlg.left = rcDlg.right - g_cxSmIcon - dxDlg;
  996. }
  997. else
  998. {
  999. rcDlg.left += g_cxSmIcon;
  1000. }
  1001. }
  1002. swp = SWP_NOZORDER | SWP_NOACTIVATE;
  1003. }
  1004. hMonitor = MonitorFromWindow(hDlg, MONITOR_DEFAULTTONEAREST);
  1005. mMonitorInfo.cbSize = sizeof(MONITORINFO);
  1006. if (GetMonitorInfo(hMonitor, &mMonitorInfo))
  1007. {
  1008. if (mMonitorInfo.rcMonitor.right < (rcDlg.left + dxDlg))
  1009. {
  1010. // Move the Window left.
  1011. rcDlg.left = mMonitorInfo.rcMonitor.right - dxDlg;
  1012. }
  1013. if (mMonitorInfo.rcMonitor.left > rcDlg.left)
  1014. {
  1015. // Move the Window Right.
  1016. rcDlg.left = mMonitorInfo.rcMonitor.left;
  1017. }
  1018. if (mMonitorInfo.rcMonitor.bottom < (rcDlg.top + dyDlg))
  1019. {
  1020. // Move the Window Up.
  1021. rcDlg.top = mMonitorInfo.rcMonitor.bottom - dyDlg;
  1022. }
  1023. if (mMonitorInfo.rcMonitor.top > rcDlg.top)
  1024. {
  1025. // Move the Window Down.
  1026. rcDlg.top = mMonitorInfo.rcMonitor.top;
  1027. }
  1028. }
  1029. SetWindowPos(hDlg, NULL, rcDlg.left, rcDlg.top, dxDlg, dyDlg, swp);
  1030. }
  1031. // Now we'll figure out where the page needs to start relative
  1032. // to the bottom of the tabs.
  1033. MapWindowRect(ppd->hwndTabs, hDlg, &rcPage);
  1034. ppd->xSubDlg = rcPage.left;
  1035. ppd->ySubDlg = rcPage.top;
  1036. ppd->cxSubDlg = rcPage.right - rcPage.left;
  1037. ppd->cySubDlg = rcPage.bottom - rcPage.top;
  1038. //
  1039. // move all the buttons down as needed and turn on appropriate buttons
  1040. // for a wizard.
  1041. //
  1042. {
  1043. RECT rcCtrl;
  1044. HWND hCtrl;
  1045. const int *pids;
  1046. if (ppd->psh.dwFlags & PSH_WIZARD97)
  1047. {
  1048. hCtrl = GetDlgItemRect(hDlg, IDD_TOPDIVIDER, &rcCtrl);
  1049. if (hCtrl)
  1050. SetWindowPos(hCtrl, NULL, rcCtrl.left, ppd->cyHeaderHeight,
  1051. RECTWIDTH(rcCtrl) + dxGrow, RECTHEIGHT(rcCtrl), SWP_NOZORDER | SWP_NOACTIVATE);
  1052. }
  1053. if (IS_WIZARD(ppd)) {
  1054. pids = WizIDs;
  1055. hCtrl = GetDlgItemRect(hDlg, IDD_DIVIDER, &rcCtrl);
  1056. if (hCtrl)
  1057. SetWindowPos(hCtrl, NULL, rcCtrl.left, rcCtrl.top + dyGrow,
  1058. RECTWIDTH(rcCtrl) + dxGrow, RECTHEIGHT(rcCtrl),
  1059. SWP_NOZORDER | SWP_NOACTIVATE);
  1060. EnableWindow(GetDlgItem(hDlg, IDD_BACK), TRUE);
  1061. ppd->idDefaultFallback = IDD_NEXT;
  1062. } else {
  1063. pids = IDs;
  1064. ppd->idDefaultFallback = IDOK;
  1065. }
  1066. // first move everything over by the same amount that
  1067. // the dialog grew by.
  1068. // If we flipped the buttons, it should be aligned to the left
  1069. // No move needed
  1070. MoveAllButtons(hDlg, pids, IDHELP, ppd->fFlipped ? 0 : dxGrow, dyGrow);
  1071. // If there's no help, then remove the help button.
  1072. if (!(ppd->psh.dwFlags & PSH_HASHELP)) {
  1073. RemoveButton(hDlg, IDHELP, pids);
  1074. }
  1075. // If we are not a wizard, and we should NOT show apply now
  1076. if ((ppd->psh.dwFlags & PSH_NOAPPLYNOW) &&
  1077. !IS_WIZARD(ppd))
  1078. {
  1079. RemoveButton(hDlg, IDD_APPLYNOW, pids);
  1080. }
  1081. if (IS_WIZARD(ppd) &&
  1082. (!(ppd->psh.dwFlags & PSH_WIZARDHASFINISH)))
  1083. {
  1084. DWORD dwStyle=0;
  1085. RemoveButton(hDlg, IDD_FINISH, pids);
  1086. // if there's no finish button showing, we need to place it where
  1087. // the next button is
  1088. GetWindowRect(GetDlgItem(hDlg, IDD_NEXT), &rcCtrl);
  1089. MapWindowPoints(HWND_DESKTOP, hDlg, (LPPOINT)&rcCtrl, 2);
  1090. SetWindowPos(GetDlgItem(hDlg, IDD_FINISH), NULL, rcCtrl.left, rcCtrl.top,
  1091. RECTWIDTH(rcCtrl), RECTHEIGHT(rcCtrl), SWP_NOZORDER | SWP_NOACTIVATE);
  1092. }
  1093. }
  1094. // (dli) compute the Pattern Brush for the watermark
  1095. // Note: This is done here because we need to know the size of the big dialog in
  1096. // case the user wants to stretch the bitmap
  1097. if (ppd->psh.dwFlags & PSH_WIZARD97)
  1098. {
  1099. int cx, cy;
  1100. ASSERT(ppd->hbmHeader == NULL);
  1101. ASSERT(ppd->hbmWatermark == NULL);
  1102. //
  1103. // WIZARD97IE4 disabled the watermark and header bitmap
  1104. // if high contrast was turned on.
  1105. //
  1106. if (ppd->psh.dwFlags & PSH_WIZARD97IE4) {
  1107. HIGHCONTRAST hc = {sizeof(hc)};
  1108. if (SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0) &&
  1109. (hc.dwFlags & HCF_HIGHCONTRASTON)) {
  1110. ppd->psh.dwFlags &= ~(PSH_WATERMARK | PSH_USEHBMWATERMARK |
  1111. PSH_USEHPLWATERMARK |
  1112. PSH_HEADER | PSH_USEHBMHEADER);
  1113. }
  1114. }
  1115. if ((ppd->psh.dwFlags & PSH_WATERMARK) && ppd->psh.H_hbmWatermark)
  1116. {
  1117. // Compute dimensions of final bitmap, which may be slightly
  1118. // goofy due to stretching
  1119. cx = cy = 0; // Assume no stretching
  1120. if (ppd->psh.dwFlags & PSH_STRETCHWATERMARK) {
  1121. RECT rc;
  1122. if (ppd->psh.dwFlags & PSH_WIZARD97IE4) {
  1123. // The WIZARD97IE4 watermark covers the entire dialog
  1124. if (GetDlgItemRect(hDlg, IDD_DIVIDER, &rc)) {
  1125. cx = dxDlg;
  1126. cy = rc.top;
  1127. }
  1128. } else {
  1129. // The WIZARD97IE5 watermark does not stretch
  1130. // (Too many people passed this flag when converting
  1131. // from WIZARD97IE4 to WIZARD97IE5 and relied on
  1132. // the nonstretchability.)
  1133. }
  1134. }
  1135. if (ppd->psh.dwFlags & PSH_USEHBMWATERMARK)
  1136. {
  1137. // LR_COPYRETURNORG means "If no stretching was needed,
  1138. // then just return the original bitmap unaltered."
  1139. // Note that we need special cleanup if a stretch occurred.
  1140. ppd->hbmWatermark = (HBITMAP)CopyImage(ppd->psh.H_hbmWatermark,
  1141. IMAGE_BITMAP, cx, cy, LR_COPYRETURNORG);
  1142. }
  1143. else
  1144. {
  1145. ppd->hbmWatermark = (HBITMAP)LoadImage(ppd->psh.hInstance,
  1146. ppd->psh.H_pszbmWatermark,
  1147. IMAGE_BITMAP, cx, cy, LR_CREATEDIBSECTION);
  1148. }
  1149. if (ppd->hbmWatermark)
  1150. {
  1151. // If app provides custom palette, then use it,
  1152. // else create one based on the bmp. (And if the bmp
  1153. // doesn't have a palette, PaletteFromBmp will use the
  1154. // halftone palette.)
  1155. if (ppd->psh.dwFlags & PSH_USEHPLWATERMARK)
  1156. ppd->hplWatermark = ppd->psh.hplWatermark;
  1157. else
  1158. ppd->hplWatermark = PaletteFromBmp(ppd->hbmWatermark);
  1159. // And WIZARD97IE4 needs to turn it into a bitmap brush.
  1160. if (ppd->psh.dwFlags & PSH_WIZARD97IE4)
  1161. ppd->hbrWatermark = CreatePatternBrush(ppd->hbmWatermark);
  1162. }
  1163. }
  1164. if ((ppd->psh.dwFlags & PSH_HEADER) && ppd->psh.H_hbmHeader)
  1165. {
  1166. cx = cy = 0; // Assume no stretching
  1167. if (ppd->psh.dwFlags & PSH_STRETCHWATERMARK) {
  1168. if (ppd->psh.dwFlags & PSH_WIZARD97IE4) {
  1169. // The WIZARD97IE4 header covers the entire header
  1170. cx = dxDlg;
  1171. cy = ppd->cyHeaderHeight;
  1172. } else {
  1173. // The WIZARD97IE5 header does not stretch
  1174. // (Too many people passed this flag when converting
  1175. // from WIZARD97IE4 to WIZARD97IE5 and relied on
  1176. // the nonstretchability.)
  1177. }
  1178. }
  1179. if (ppd->psh.dwFlags & PSH_USEHBMHEADER)
  1180. {
  1181. // LR_COPYRETURNORG means "If no stretching was needed,
  1182. // then just return the original bitmap unaltered."
  1183. // Note that we need special cleanup if a stretch occurred.
  1184. ppd->hbmHeader = (HBITMAP)CopyImage(ppd->psh.H_hbmHeader,
  1185. IMAGE_BITMAP, cx, cy, LR_COPYRETURNORG);
  1186. }
  1187. else
  1188. {
  1189. ppd->hbmHeader = (HBITMAP)LoadImage(ppd->psh.hInstance,
  1190. ppd->psh.H_pszbmHeader,
  1191. IMAGE_BITMAP, cx, cy, LR_CREATEDIBSECTION);
  1192. }
  1193. // And WIZARD97IE4 needs to turn it into a bitmap brush.
  1194. if (ppd->hbmHeader && (ppd->psh.dwFlags & PSH_WIZARD97IE4))
  1195. ppd->hbrHeader = CreatePatternBrush(ppd->hbmHeader);
  1196. }
  1197. else
  1198. {
  1199. // In case the user does not specify a header bitmap
  1200. // use the top portion of the watermark
  1201. ppd->hbmHeader = ppd->hbmWatermark;
  1202. ppd->hbrHeader = ppd->hbrWatermark;
  1203. }
  1204. }
  1205. // force the dialog to reposition itself based on its new size
  1206. SendMessage(hDlg, DM_REPOSITION, 0, 0L);
  1207. // do this here instead of using DS_SETFOREGROUND so we don't hose
  1208. // pages that do things that want to set the foreground window
  1209. SetForegroundWindow(hDlg);
  1210. // We set this to 1 if the user saves any changes.
  1211. // do this before initting or switching to any pages
  1212. ppd->nReturn = 0;
  1213. // AppHack - Some people forgot to initialize nStartPage, and they were
  1214. // lucky that the garbage value on the stack was zero. Lucky no longer.
  1215. if (nStartPage >= ppd->psh.nPages) {
  1216. RIPMSG(0, "App forgot to initialize PROPSHEETHEADER.nStartPage field, assuming zero");
  1217. nStartPage = 0;
  1218. }
  1219. // Now attempt to select the starting page.
  1220. TabCtrl_SetCurSel(ppd->hwndTabs, nStartPage);
  1221. PageChange(ppd, 1);
  1222. // Now init any other pages that require it
  1223. if (fPrematurePages)
  1224. {
  1225. int nPage;
  1226. tie.tci.mask = TCIF_PARAM;
  1227. for (nPage = 0; nPage < (int)ppd->psh.nPages; nPage++)
  1228. {
  1229. PISP pisp = GETPISP(ppd, nPage);
  1230. if (!(pisp->_psp.dwFlags & PSP_PREMATURE))
  1231. continue;
  1232. TabCtrl_GetItem(ppd->hwndTabs, nPage, &tie.tci);
  1233. if (tie.hwndPage)
  1234. continue;
  1235. if ((tie.hwndPage = _CreatePage(ppd, pisp, hDlg, langidMUI)) == NULL)
  1236. {
  1237. RemovePropPageData(ppd, nPage--);
  1238. continue;
  1239. }
  1240. TabCtrl_SetItem(ppd->hwndTabs, nPage, &tie.tci);
  1241. }
  1242. }
  1243. }
  1244. HWND _Ppd_GetPage(LPPROPDATA ppd, int nItem)
  1245. {
  1246. if (ppd->hwndTabs)
  1247. {
  1248. TC_ITEMEXTRA tie;
  1249. tie.tci.mask = TCIF_PARAM;
  1250. TabCtrl_GetItem(ppd->hwndTabs, nItem, &tie.tci);
  1251. return tie.hwndPage;
  1252. }
  1253. return NULL;
  1254. }
  1255. BOOL _Ppd_IsPageHidden(LPPROPDATA ppd, int nItem)
  1256. {
  1257. if (ppd->hwndTabs)
  1258. {
  1259. TCITEM tci;
  1260. tci.mask = TCIF_STATE;
  1261. tci.dwStateMask = TCIS_HIDDEN;
  1262. if (TabCtrl_GetItem(ppd->hwndTabs, nItem, &tci))
  1263. return tci.dwState;
  1264. }
  1265. return FALSE;
  1266. }
  1267. LRESULT _Ppd_SendNotify(LPPROPDATA ppd, int nItem, int code, LPARAM lParam)
  1268. {
  1269. PSHNOTIFY pshn;
  1270. pshn.lParam = lParam;
  1271. return SendNotifyEx(_Ppd_GetPage(ppd,nItem), ppd->hDlg, code, (LPNMHDR)&pshn, FALSE);
  1272. }
  1273. //
  1274. // dwFind = 0 means just move to the current item + iAutoAdjust
  1275. // dwFind != 0 means it's a dialog resource identifier we should look for
  1276. //
  1277. int FindPageIndex(LPPROPDATA ppd, int nCurItem, ULONG_PTR dwFind, LONG_PTR iAutoAdj)
  1278. {
  1279. LRESULT nActivate;
  1280. if (dwFind == 0) {
  1281. nActivate = nCurItem + iAutoAdj;
  1282. if (((UINT)nActivate) <= ppd->psh.nPages) {
  1283. return((int)nActivate);
  1284. }
  1285. } else {
  1286. for (nActivate = 0; (UINT)nActivate < ppd->psh.nPages; nActivate++) {
  1287. if ((DWORD_PTR)GETPPSP(ppd, nActivate)->P_pszTemplate == dwFind) {
  1288. return((int)nActivate);
  1289. }
  1290. }
  1291. }
  1292. return(-1);
  1293. }
  1294. //
  1295. // If hpage != NULL, then return the index of the page which matches it,
  1296. // or -1 on failure.
  1297. //
  1298. int FindPageIndexByHpage(LPPROPDATA ppd, HPROPSHEETPAGE hpage)
  1299. {
  1300. int i;
  1301. //
  1302. // Notice that we explicitly do not do a InternalizeHPROPSHEETPAGE,
  1303. // because the app might be passing us garbage. We just want to
  1304. // say "Nope, can't find garbage here, sorry."
  1305. //
  1306. for (i = ppd->psh.nPages - 1; i >= 0; i--) {
  1307. if (hpage == GETHPAGE(ppd, i))
  1308. break;
  1309. }
  1310. return i;
  1311. }
  1312. // This WM_NEXTDLGCTL stuff works, except for ACT!4.0 which faults randomly
  1313. // I don't know why. The USER people said that removing a
  1314. // SetFocus(NULL) call from SetDlgFocus works, but I tried that
  1315. // and the app merely faulted in a different place. so I'm going
  1316. // back to the old IE4 way, which means that there are scenarios
  1317. // where the DEFID can get out of sync with reality.
  1318. #undef WM_NEXTDLGCTL_WORKS
  1319. #ifdef WM_NEXTDLGCTL_WORKS
  1320. //
  1321. // Helper function that manages dialog box focus in a manner that keeps
  1322. // USER in the loop, so we don't get "two buttons both with the bold
  1323. // defpushbutton border" problems.
  1324. //
  1325. // We have to use WM_NEXTDLGCTL to fix defid problems, such as this one:
  1326. //
  1327. // Right-click My Computer, Properties.
  1328. // Go to Advanced tab. Click Environment Variables.
  1329. // Click New. Type a name for a new dummy environment variable.
  1330. // Click OK.
  1331. //
  1332. // At this point (with the old code), the "New" button is a DEFPUSHBUTTON,
  1333. // but the DEFID is IDOK. The USER folks said I should use WM_NEXTDLGCTL
  1334. // to avoid this problem. But using WM_NEXTDLGCTL introduces its own big
  1335. // hairy mess of problems. All the code in this function aside from the
  1336. // SendMessage(WM_NEXTDLGCTL) are to work around "quirks" in WM_NEXTDLGCTL
  1337. // or workarounds for app bugs.
  1338. //
  1339. // THIS CODE IS SUBTLE AND QUICK TO ANGER!
  1340. //
  1341. void SetDlgFocus(LPPROPDATA ppd, HWND hwndFocus)
  1342. {
  1343. //
  1344. // HACK! It's possible that by the time we get around to changing
  1345. // the dialog focus, the dialog box doesn't have focus any more!
  1346. // This happens because PSM_SETWIZBUTTONS is a posted message, so
  1347. // it can arrive *after* focus has moved elsewhere (e.g., to a
  1348. // MessageBox).
  1349. //
  1350. // There is no way to update the dialog box focus without
  1351. // letting it change the real focus (another "quirk" of
  1352. // WM_NEXTDLGCTL), so instead we remember who used to have the
  1353. // focus, let the dialog box do its focus goo, and then restore
  1354. // the focus as necessary.
  1355. //
  1356. HWND hwndFocusPrev = GetFocus();
  1357. // If focus belonged to a window within our property sheet, then
  1358. // let the dialog box code push the focus around. Otherwise,
  1359. // focus belonged to somebody outside our property sheet, so
  1360. // remember to restore it after we're done.
  1361. if (hwndFocusPrev && IsChild(ppd->hDlg, hwndFocusPrev))
  1362. hwndFocusPrev = NULL;
  1363. // USER forgot to revalidate hwndOldFocus at this point, so we have
  1364. // to exit USER (by returning to comctl32) then re-enter USER
  1365. // (in the SendMessage below) so parameter validation will happen
  1366. // again. Sigh.
  1367. //
  1368. // Bug in Win9x and NT: WM_NEXTDLGCTL will crash if the previous
  1369. // focus window destroys itself in response to WM_KILLFOCUS.
  1370. // (WebTurbo by NetMetrics does this.) There's a missed
  1371. // revalidation so USER ends up using a window handle after
  1372. // it has been destroyed. Oops.
  1373. //
  1374. // (The NT folks consider this "Won't fix, because the system stays
  1375. // up; just the app crashes". The 9x folks will try to get the fix
  1376. // into Win98 OSR.)
  1377. //
  1378. //
  1379. // Do a manual SetFocus here to make the old focus (if any)
  1380. // do all its WM_KILLFOCUS stuff, and possibly destroy itself (grrr).
  1381. //
  1382. // We have to SetFocus to NULL because some apps (e.g.,
  1383. // Visual C 6.0 setup) do funky things on SetFocus, and our early
  1384. // SetFocus interferes with the EM_SETSEL that WM_NEXTDLGCTL will
  1385. // do later.
  1386. //
  1387. // APP HACK 2: But not if the target focus is the same as the
  1388. // curreng focus, because ACT!4.0 crashes if it receives a
  1389. // WM_KILLFOCUS when it is not expecting one.
  1390. if (hwndFocus != GetFocus())
  1391. SetFocus(NULL);
  1392. //
  1393. // Note that by manually shoving the focus around, we
  1394. // have gotten focus and DEFPUSHBUTTON and DEFID all out
  1395. // of sync, which is exactly the problem we're trying to
  1396. // avoid! Fortunately, USER also contains special
  1397. // recovery code to handle the case where somebody "mistakenly" called
  1398. // SetFocus() to change the focus. (I put "mistakenly" in quotes because
  1399. // in this case, we did it on purpose.)
  1400. //
  1401. SendMessage(ppd->hDlg, WM_NEXTDLGCTL, (WPARAM)hwndFocus, MAKELPARAM(TRUE, 0));
  1402. //
  1403. // If WM_NEXTDLGCTL damaged the focus, fix it.
  1404. //
  1405. if (hwndFocusPrev)
  1406. SetFocus(hwndFocusPrev);
  1407. }
  1408. #endif
  1409. void SetNewDefID(LPPROPDATA ppd)
  1410. {
  1411. HWND hDlg = ppd->hDlg;
  1412. HWND hwndFocus;
  1413. hwndFocus = GetNextDlgTabItem(ppd->hwndCurPage, NULL, FALSE);
  1414. ASSERT(hwndFocus);
  1415. if (hwndFocus) {
  1416. #ifndef WM_NEXTDLGCTL_WORKS
  1417. int id;
  1418. if (((DWORD)SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0L)) & DLGC_HASSETSEL)
  1419. {
  1420. // select the text
  1421. Edit_SetSel(hwndFocus, 0, -1);
  1422. }
  1423. id = GetDlgCtrlID(hwndFocus);
  1424. #endif
  1425. //
  1426. // See if the handle give to us by GetNextDlgTabItem was any good.
  1427. // (For compatibility reasons, if the dialog contains no tabstops,
  1428. // it returns the first item.)
  1429. //
  1430. if ((GetWindowLong(hwndFocus, GWL_STYLE) & (WS_VISIBLE | WS_DISABLED | WS_TABSTOP)) == (WS_VISIBLE | WS_TABSTOP))
  1431. {
  1432. //
  1433. // Give the page a chance to change the default focus.
  1434. //
  1435. HWND hwndT = (HWND)_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_QUERYINITIALFOCUS, (LPARAM)hwndFocus);
  1436. // The window had better be valid and a child of the page.
  1437. if (hwndT && IsWindow(hwndT) && IsChild(ppd->hwndCurPage, hwndT))
  1438. {
  1439. hwndFocus = hwndT;
  1440. }
  1441. }
  1442. else
  1443. {
  1444. // in prop sheet mode, focus on tabs,
  1445. // in wizard mode, tabs aren't visible, go to idDefFallback
  1446. if (IS_WIZARD(ppd))
  1447. hwndFocus = GetDlgItem(hDlg, ppd->idDefaultFallback);
  1448. else
  1449. hwndFocus = ppd->hwndTabs;
  1450. }
  1451. #ifdef WM_NEXTDLGCTL_WORKS
  1452. //
  1453. // Aw-right. Go for it.
  1454. //
  1455. SetDlgFocus(ppd, hwndFocus);
  1456. //
  1457. // Hack for MFC: MFC relies on DM_SETDEFID to know when to
  1458. // update its wizard buttons.
  1459. //
  1460. SendMessage(hDlg, DM_SETDEFID, SendMessage(hDlg, DM_GETDEFID, 0, 0), 0);
  1461. #else
  1462. SetFocus(hwndFocus);
  1463. ResetWizButtons(ppd);
  1464. if (SendDlgItemMessage(ppd->hwndCurPage, id, WM_GETDLGCODE, 0, 0L) & DLGC_UNDEFPUSHBUTTON)
  1465. SendMessage(ppd->hwndCurPage, DM_SETDEFID, id, 0);
  1466. else {
  1467. SendMessage(hDlg, DM_SETDEFID, ppd->idDefaultFallback, 0);
  1468. }
  1469. #endif
  1470. }
  1471. }
  1472. /*
  1473. ** we are about to change pages. what a nice chance to let the current
  1474. ** page validate itself before we go away. if the page decides not
  1475. ** to be de-activated, then this'll cancel the page change.
  1476. **
  1477. ** return TRUE iff this page failed validation
  1478. */
  1479. BOOL PageChanging(LPPROPDATA ppd)
  1480. {
  1481. BOOL bRet = FALSE;
  1482. if (ppd && ppd->hwndCurPage)
  1483. {
  1484. bRet = BOOLFROMPTR(_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_KILLACTIVE, 0));
  1485. }
  1486. return bRet;
  1487. }
  1488. void PageChange(LPPROPDATA ppd, int iAutoAdj)
  1489. {
  1490. HWND hwndCurPage;
  1491. HWND hwndCurFocus;
  1492. int nItem;
  1493. HWND hDlg, hwndTabs;
  1494. TC_ITEMEXTRA tie;
  1495. UINT FlailCount = 0;
  1496. LRESULT lres;
  1497. if (!ppd)
  1498. {
  1499. return;
  1500. }
  1501. hDlg = ppd->hDlg;
  1502. hwndTabs = ppd->hwndTabs;
  1503. // NOTE: the page was already validated (PSN_KILLACTIVE) before
  1504. // the actual page change.
  1505. hwndCurFocus = GetFocus();
  1506. TryAgain:
  1507. FlailCount++;
  1508. if (FlailCount > ppd->psh.nPages)
  1509. {
  1510. DebugMsg(DM_TRACE, TEXT("PropSheet PageChange attempt to set activation more than 10 times."));
  1511. return;
  1512. }
  1513. nItem = TabCtrl_GetCurSel(hwndTabs);
  1514. if (nItem < 0)
  1515. {
  1516. return;
  1517. }
  1518. tie.tci.mask = TCIF_PARAM;
  1519. TabCtrl_GetItem(hwndTabs, nItem, &tie.tci);
  1520. hwndCurPage = tie.hwndPage;
  1521. if (!hwndCurPage)
  1522. {
  1523. if ((hwndCurPage = _CreatePage(ppd, GETPISP(ppd, nItem), hDlg, GetMUILanguage())) == NULL)
  1524. {
  1525. /* Should we put up some sort of error message here?
  1526. */
  1527. RemovePropPageData(ppd, nItem);
  1528. TabCtrl_SetCurSel(hwndTabs, 0);
  1529. goto TryAgain;
  1530. }
  1531. // tie.tci.mask = TCIF_PARAM;
  1532. tie.hwndPage = hwndCurPage;
  1533. TabCtrl_SetItem(hwndTabs, nItem, &tie.tci);
  1534. if (HIDEWIZ97HEADER(ppd, nItem))
  1535. // Subclass for back ground watermark painting.
  1536. SetWindowSubclass(hwndCurPage, WizardWndProc, 0, (DWORD_PTR)ppd);
  1537. }
  1538. // THI WAS REMOVED as part of the fix for bug 18327. The problem is we need to
  1539. // send a SETACTIVE message to a page if it is being activated.
  1540. // if (ppd->hwndCurPage == hwndCurPage)
  1541. // {
  1542. // /* we should be done at this point.
  1543. // */
  1544. // return;
  1545. // }
  1546. /* Size the dialog and move it to the top of the list before showing
  1547. ** it in case there is size specific initializing to be done in the
  1548. ** GETACTIVE message.
  1549. */
  1550. if (IS_WIZARD(ppd))
  1551. {
  1552. HWND hwndTopDivider= GetDlgItem(hDlg, IDD_TOPDIVIDER);
  1553. if (ppd->psh.dwFlags & PSH_WIZARD97)
  1554. {
  1555. HWND hwndDivider;
  1556. RECT rcDlg, rcDivider;
  1557. GetClientRect(hDlg, &rcDlg);
  1558. hwndDivider = GetDlgItemRect(hDlg, IDD_DIVIDER, &rcDivider);
  1559. if (hwndDivider)
  1560. SetWindowPos(hwndDivider, NULL, rcDlg.left, rcDivider.top,
  1561. RECTWIDTH(rcDlg), RECTHEIGHT(rcDivider),
  1562. SWP_NOZORDER | SWP_NOACTIVATE);
  1563. if (GETPPSP(ppd, nItem)->dwFlags & PSP_HIDEHEADER)
  1564. {
  1565. // In this case, we give the whole dialog box except for the portion under the
  1566. // Bottom divider to the property page
  1567. RECT rcTopDivider;
  1568. ShowWindow(hwndTopDivider, SW_HIDE);
  1569. ShowWindow(ppd->hwndTabs, SW_HIDE);
  1570. hwndTopDivider = GetDlgItemRect(hDlg, IDD_DIVIDER, &rcTopDivider);
  1571. SetWindowPos(hwndCurPage, HWND_TOP, rcDlg.left, rcDlg.top, RECTWIDTH(rcDlg), rcTopDivider.top - rcDlg.top, 0);
  1572. }
  1573. else
  1574. {
  1575. ShowWindow(hwndTopDivider, SW_SHOW);
  1576. ShowWindow(ppd->hwndTabs, SW_SHOW);
  1577. SetWindowPos(hwndCurPage, HWND_TOP, ppd->xSubDlg, ppd->ySubDlg, ppd->cxSubDlg, ppd->cySubDlg, 0);
  1578. }
  1579. }
  1580. else
  1581. {
  1582. ShowWindow(hwndTopDivider, SW_HIDE);
  1583. SetWindowPos(hwndCurPage, HWND_TOP, ppd->xSubDlg, ppd->ySubDlg, ppd->cxSubDlg, ppd->cySubDlg, 0);
  1584. }
  1585. } else {
  1586. RECT rcPage;
  1587. GetClientRect(ppd->hwndTabs, &rcPage);
  1588. TabCtrl_AdjustRect(ppd->hwndTabs, FALSE, &rcPage);
  1589. MapWindowPoints(ppd->hwndTabs, hDlg, (LPPOINT)&rcPage, 2);
  1590. SetWindowPos(hwndCurPage, HWND_TOP, rcPage.left, rcPage.top,
  1591. rcPage.right - rcPage.left, rcPage.bottom - rcPage.top, 0);
  1592. }
  1593. /* We want to send the SETACTIVE message before the window is visible
  1594. ** to minimize on flicker if it needs to update fields.
  1595. */
  1596. //
  1597. // If the page returns non-zero from the PSN_SETACTIVE call then
  1598. // we will set the activation to the resource ID returned from
  1599. // the call and set activation to it. This is mainly used by wizards
  1600. // to skip a step.
  1601. //
  1602. lres = _Ppd_SendNotify(ppd, nItem, PSN_SETACTIVE, 0);
  1603. if (lres) {
  1604. int iPageIndex = FindPageIndex(ppd, nItem,
  1605. (lres == -1) ? 0 : lres, iAutoAdj);
  1606. if ((lres == -1) &&
  1607. (nItem == iPageIndex || iPageIndex >= TabCtrl_GetItemCount(hwndTabs))) {
  1608. iPageIndex = ppd->nCurItem;
  1609. }
  1610. if (iPageIndex != -1) {
  1611. TabCtrl_SetCurSel(hwndTabs, iPageIndex);
  1612. ShowWindow(hwndCurPage, SW_HIDE);
  1613. goto TryAgain;
  1614. }
  1615. }
  1616. if (ppd->psh.dwFlags & PSH_HASHELP) {
  1617. // PSH_HASHELP controls the "Help" button at the bottom
  1618. // PSH_NOCONTEXTHELP controls the caption "?" button
  1619. Button_Enable(GetDlgItem(hDlg, IDHELP),
  1620. (BOOL)(GETPPSP(ppd, nItem)->dwFlags & PSP_HASHELP));
  1621. }
  1622. //
  1623. // If this is a wizard then we'll set the dialog's title to the tab
  1624. // title.
  1625. //
  1626. if (IS_WIZARD(ppd)) {
  1627. TC_ITEMEXTRA tie;
  1628. TCHAR szTemp[128 + 50];
  1629. szTemp[0] = 0;
  1630. tie.tci.mask = TCIF_TEXT;
  1631. tie.tci.pszText = szTemp;
  1632. tie.tci.cchTextMax = ARRAYSIZE(szTemp);
  1633. if (TabCtrl_GetItem(hwndTabs, nItem, &tie.tci))
  1634. {
  1635. tie.tci.mask = TCIF_RTLREADING;
  1636. tie.tci.cchTextMax = 0;
  1637. // hack, use cchTextMax to query tab item reading order
  1638. TabCtrl_GetItem(hwndTabs, nItem, &tie.tci);
  1639. if( (ppd->psh.dwFlags & PSH_RTLREADING) || (tie.tci.cchTextMax))
  1640. SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) | WS_EX_RTLREADING);
  1641. else
  1642. SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) & ~WS_EX_RTLREADING);
  1643. if (szTemp[0])
  1644. SetWindowText(hDlg, szTemp);
  1645. }
  1646. }
  1647. /* Disable all erasebkgnd messages that come through because windows
  1648. ** are getting shuffled. Note that we need to call ShowWindow (and
  1649. ** not show the window in some other way) because DavidDs is counting
  1650. ** on the correct parameters to the WM_SHOWWINDOW message, and we may
  1651. ** document how to keep your page from flashing.
  1652. */
  1653. ppd->fFlags |= PD_NOERASE;
  1654. ShowWindow(hwndCurPage, SW_SHOW);
  1655. if (ppd->hwndCurPage && (ppd->hwndCurPage != hwndCurPage))
  1656. {
  1657. ShowWindow(ppd->hwndCurPage, SW_HIDE);
  1658. }
  1659. ppd->fFlags &= ~PD_NOERASE;
  1660. ppd->hwndCurPage = hwndCurPage;
  1661. ppd->nCurItem = nItem;
  1662. /* Newly created dialogs seem to steal the focus, so we steal it back
  1663. ** to the page list, which must have had the focus to get to this
  1664. ** point. If this is a wizard then set the focus to the dialog of
  1665. ** the page. Otherwise, set the focus to the tabs.
  1666. */
  1667. if (hwndCurFocus != hwndTabs)
  1668. {
  1669. SetNewDefID(ppd);
  1670. }
  1671. else
  1672. {
  1673. // The focus may have been stolen from us, bring it back
  1674. SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hwndTabs, (LPARAM)TRUE);
  1675. }
  1676. // make sure the header is repaint
  1677. if ((ppd->psh.dwFlags & PSH_WIZARD97) && (!(GETPPSP(ppd, nItem)->dwFlags & PSP_HIDEHEADER)))
  1678. InvalidateRect(hDlg, NULL,TRUE);
  1679. }
  1680. #define DECLAREWAITCURSOR HCURSOR hcursor_wait_cursor_save
  1681. #define SetWaitCursor() hcursor_wait_cursor_save = SetCursor(LoadCursor(NULL, IDC_WAIT))
  1682. #define ResetWaitCursor() SetCursor(hcursor_wait_cursor_save)
  1683. //
  1684. // HACKHACK (reinerf)
  1685. //
  1686. // This function sends the PSN_LASTCHANCEAPPLY right after the property sheets have had "ok"
  1687. // pressed. This allows the "General" tab on the file/folder properties to do a rename, so that
  1688. // it wont rename the file out from under the other pages, and have them barf when they go to
  1689. // persist their info.
  1690. //
  1691. void SendLastChanceApply(LPPROPDATA ppd)
  1692. {
  1693. TC_ITEMEXTRA tie;
  1694. int nItem;
  1695. int nItems = TabCtrl_GetItemCount(ppd->hwndTabs);
  1696. tie.tci.mask = TCIF_PARAM;
  1697. // we start with the last tab and count towards the first. This ensures
  1698. // that the more important tabs (such as the "General" tab) will be the last
  1699. // to recieve the PSN_LASTCHANCEAPPLY message.
  1700. for (nItem = nItems - 1; nItem >= 0; nItem--)
  1701. {
  1702. TabCtrl_GetItem(ppd->hwndTabs, nItem, &tie.tci);
  1703. if (tie.hwndPage)
  1704. {
  1705. // we ignore the return vale from the PSN_LASTCHANCEAPPLY message since
  1706. // there are probably prop sheet extensions that return both TRUE and
  1707. // FALSE for messages that they dont process...(sigh)
  1708. _Ppd_SendNotify(ppd, nItem, PSN_LASTCHANCEAPPLY, (LPARAM)TRUE);
  1709. }
  1710. }
  1711. }
  1712. // return TRUE iff all sheets successfully handle the notification
  1713. BOOL ButtonPushed(LPPROPDATA ppd, WPARAM wParam)
  1714. {
  1715. HWND hwndTabs;
  1716. int nItems, nItem;
  1717. int nNotify;
  1718. TC_ITEMEXTRA tie;
  1719. BOOL bExit = FALSE;
  1720. int nReturnNew = ppd->nReturn;
  1721. int fSuccess = TRUE;
  1722. DECLAREWAITCURSOR;
  1723. LRESULT lres = 0;
  1724. LPARAM lParam = FALSE;
  1725. LPARAM nButtonPressed = 0;
  1726. switch (wParam)
  1727. {
  1728. case IDOK:
  1729. nButtonPressed = PSBTN_OK;
  1730. lParam = TRUE;
  1731. bExit = TRUE;
  1732. // Fall through...
  1733. case IDD_APPLYNOW:
  1734. if (IDD_APPLYNOW == wParam)
  1735. {
  1736. nButtonPressed = PSBTN_APPLYNOW;
  1737. }
  1738. // First allow the current dialog to validate itself.
  1739. if (_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_KILLACTIVE, 0))
  1740. return FALSE;
  1741. nReturnNew = 1;
  1742. nNotify = PSN_APPLY;
  1743. break;
  1744. case IDCLOSE:
  1745. nButtonPressed = PSBTN_FINISH;
  1746. lParam = TRUE;
  1747. // fall through
  1748. case IDCANCEL:
  1749. if (IDCANCEL == wParam)
  1750. {
  1751. nButtonPressed = PSBTN_CANCEL;
  1752. }
  1753. bExit = TRUE;
  1754. nNotify = PSN_RESET;
  1755. break;
  1756. default:
  1757. return FALSE;
  1758. }
  1759. if (ppd && ppd->psh.pfnCallback && nButtonPressed)
  1760. {
  1761. ppd->psh.pfnCallback(NULL, PSCB_BUTTONPRESSED, nButtonPressed);
  1762. }
  1763. SetWaitCursor();
  1764. hwndTabs = ppd->hwndTabs;
  1765. tie.tci.mask = TCIF_PARAM;
  1766. nItems = TabCtrl_GetItemCount(hwndTabs);
  1767. for (nItem = 0; nItem < nItems; ++nItem)
  1768. {
  1769. TabCtrl_GetItem(hwndTabs, nItem, &tie.tci);
  1770. if (tie.hwndPage)
  1771. {
  1772. /* If the dialog fails a PSN_APPY call (by returning TRUE),
  1773. ** then it has invalid information on it (should be verified
  1774. ** on the PSN_KILLACTIVE, but that is not always possible)
  1775. ** and we want to abort the notifications. We select the failed
  1776. ** page below.
  1777. */
  1778. lres = _Ppd_SendNotify(ppd, nItem, nNotify, lParam);
  1779. if (lres)
  1780. {
  1781. fSuccess = FALSE;
  1782. bExit = FALSE;
  1783. break;
  1784. } else {
  1785. // if we need a restart (Apply or OK), then this is an exit
  1786. if ((nNotify == PSN_APPLY) && !bExit && ppd->nRestart) {
  1787. DebugMsg(DM_TRACE, TEXT("PropertySheet: restart flags force close"));
  1788. bExit = TRUE;
  1789. }
  1790. }
  1791. /* We have either reset or applied, so everything is
  1792. ** up to date.
  1793. */
  1794. tie.state &= ~FLAG_CHANGED;
  1795. // tie.tci.mask = TCIF_PARAM; // already set
  1796. TabCtrl_SetItem(hwndTabs, nItem, &tie.tci);
  1797. }
  1798. }
  1799. /* If we leave ppd->hwndCurPage as NULL, it will tell the main
  1800. ** loop to exit.
  1801. */
  1802. if (fSuccess)
  1803. {
  1804. ppd->hwndCurPage = NULL;
  1805. }
  1806. else if (lres != PSNRET_INVALID_NOCHANGEPAGE)
  1807. {
  1808. // Need to change to the page that caused the failure.
  1809. // if lres == PSN_INVALID_NOCHANGEPAGE, then assume sheet has already
  1810. // changed to the page with the invalid information on it
  1811. TabCtrl_SetCurSel(hwndTabs, nItem);
  1812. }
  1813. if (fSuccess)
  1814. {
  1815. // Set to the cached value
  1816. ppd->nReturn = nReturnNew;
  1817. }
  1818. if (!bExit)
  1819. {
  1820. // before PageChange, so ApplyNow gets disabled faster.
  1821. if (fSuccess)
  1822. {
  1823. TCHAR szOK[30];
  1824. HWND hwndApply;
  1825. if (!IS_WIZARD(ppd)) {
  1826. // The ApplyNow button should always be disabled after
  1827. // a successfull apply/cancel, since no change has been made yet.
  1828. hwndApply = GetDlgItem(ppd->hDlg, IDD_APPLYNOW);
  1829. Button_SetStyle(hwndApply, BS_PUSHBUTTON, TRUE);
  1830. EnableWindow(hwndApply, FALSE);
  1831. ResetWizButtons(ppd);
  1832. SendMessage(ppd->hDlg, DM_SETDEFID, IDOK, 0);
  1833. ppd->idDefaultFallback = IDOK;
  1834. }
  1835. // Undo PSM_CANCELTOCLOSE for the same reasons.
  1836. if (ppd->fFlags & PD_CANCELTOCLOSE)
  1837. {
  1838. ppd->fFlags &= ~PD_CANCELTOCLOSE;
  1839. LocalizedLoadString(IDS_OK, szOK, ARRAYSIZE(szOK));
  1840. SetDlgItemText(ppd->hDlg, IDOK, szOK);
  1841. EnableWindow(GetDlgItem(ppd->hDlg, IDCANCEL), TRUE);
  1842. }
  1843. }
  1844. /* Re-"select" the current item and get the whole list to
  1845. ** repaint.
  1846. */
  1847. if (lres != PSNRET_INVALID_NOCHANGEPAGE)
  1848. PageChange(ppd, 1);
  1849. }
  1850. ResetWaitCursor();
  1851. return(fSuccess);
  1852. }
  1853. // Win3.1 USER didn't handle DM_SETDEFID very well-- it's very possible to get
  1854. // multiple buttons with the default button style look. This has been fixed
  1855. // for Win95, but the Setup wizard needs this hack when running from 3.1.
  1856. // it seems win95 doesn't handle it well either..
  1857. void ResetWizButtons(LPPROPDATA ppd)
  1858. {
  1859. int id;
  1860. if (IS_WIZARD(ppd)) {
  1861. for (id = 0; id < ARRAYSIZE(WizIDs); id++)
  1862. SendDlgItemMessage(ppd->hDlg, WizIDs[id], BM_SETSTYLE, BS_PUSHBUTTON, TRUE);
  1863. }
  1864. }
  1865. void SetWizButtons(LPPROPDATA ppd, LPARAM lParam)
  1866. {
  1867. int idDef;
  1868. int iShowID = IDD_NEXT;
  1869. int iHideID = IDD_FINISH;
  1870. BOOL bEnabled;
  1871. BOOL bResetFocus;
  1872. HWND hwndShow;
  1873. HWND hwndFocus = GetFocus();
  1874. HWND hwndHide;
  1875. HWND hwndBack;
  1876. HWND hDlg = ppd->hDlg;
  1877. idDef = (int)LOWORD(SendMessage(hDlg, DM_GETDEFID, 0, 0));
  1878. // Enable/Disable the IDD_BACK button
  1879. hwndBack = GetDlgItem(hDlg, IDD_BACK);
  1880. bEnabled = (lParam & PSWIZB_BACK) != 0;
  1881. EnableWindow(hwndBack, bEnabled);
  1882. // Enable/Disable the IDD_NEXT button, and Next gets shown by default
  1883. // bEnabled remembers whether hwndShow should be enabled or not
  1884. hwndShow = GetDlgItem(hDlg, IDD_NEXT);
  1885. bEnabled = (lParam & PSWIZB_NEXT) != 0;
  1886. EnableWindow(hwndShow, bEnabled);
  1887. // Enable/Disable Show/Hide the IDD_FINISH button
  1888. if (lParam & (PSWIZB_FINISH | PSWIZB_DISABLEDFINISH)) {
  1889. iShowID = IDD_FINISH; // If Finish is being shown
  1890. iHideID = IDD_NEXT; // then Next isn't
  1891. hwndShow = GetDlgItem(hDlg, IDD_FINISH);
  1892. bEnabled = (lParam & PSWIZB_FINISH) != 0;
  1893. EnableWindow(hwndShow, bEnabled);
  1894. }
  1895. if (!(ppd->psh.dwFlags & PSH_WIZARDHASFINISH)) {
  1896. hwndHide = GetDlgItem(hDlg, iHideID);
  1897. ShowWindow(hwndHide, SW_HIDE);
  1898. // Cannot disable the window; see Prsht_ButtonSubclassProc for explanation.
  1899. // WRONG - EnableWindow(hwndHide, FALSE);
  1900. hwndShow = GetDlgItem(hDlg, iShowID);
  1901. // Cannot disable the window; see Prsht_ButtonSubclassProc for explanation.
  1902. // WRONG - EnableWindow(hwndShow, bEnabled);
  1903. ShowWindow(hwndShow, SW_SHOW);
  1904. }
  1905. // bResetFocus keeps track of whether or not we need to set Focus to our button
  1906. bResetFocus = FALSE;
  1907. if (hwndFocus)
  1908. {
  1909. // if the dude that has focus is a button, we want to steal focus away
  1910. // so users can just press enter all the way through a property sheet,
  1911. // getting the default as they go. this also catches the case
  1912. // of where focus is on one of our buttons which was turned off.
  1913. if (SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0L) & (DLGC_UNDEFPUSHBUTTON|DLGC_DEFPUSHBUTTON))
  1914. bResetFocus = TRUE;
  1915. }
  1916. if (!bResetFocus)
  1917. {
  1918. // if there is no focus or we're focused on an invisible/disabled
  1919. // item on the sheet, grab focus.
  1920. bResetFocus = !hwndFocus || !IsWindowVisible(hwndFocus) || !IsWindowEnabled(hwndFocus) ;
  1921. }
  1922. // We used to do this code only if we nuked a button which had default
  1923. // or if bResetFocus. Unfortunately, some wizards turn off BACK+NEXT
  1924. // and then when they turn them back on, they want DEFID on NEXT.
  1925. // So now we always reset DEFID.
  1926. {
  1927. static const int ids[4] = { IDD_NEXT, IDD_FINISH, IDD_BACK, IDCANCEL };
  1928. int i;
  1929. HWND hwndNewFocus = NULL;
  1930. for (i = 0; i < ARRAYSIZE(ids); i++) {
  1931. hwndNewFocus = GetDlgItem(hDlg, ids[i]);
  1932. // can't do IsVisible because we may be doing this
  1933. // before the prop sheet as a whole is shown
  1934. if ((GetWindowLong(hwndNewFocus, GWL_STYLE) & WS_VISIBLE) &&
  1935. IsWindowEnabled(hwndNewFocus)) {
  1936. hwndFocus = hwndNewFocus;
  1937. break;
  1938. }
  1939. }
  1940. ppd->idDefaultFallback = ids[i];
  1941. if (bResetFocus) {
  1942. if (!hwndNewFocus)
  1943. hwndNewFocus = hDlg;
  1944. #ifdef WM_NEXTDLGCTL_WORKS
  1945. SetDlgFocus(ppd, hwndNewFocus);
  1946. #else
  1947. // 337614 - Since PSM_SETWIZBUTTONS is often a posted message,
  1948. // we may end up here when we don't even have focus at all
  1949. // (caller went on and called MessageBox or something before
  1950. // we got a chance to set the buttons). So do this only if
  1951. // focus belongs to our dialog box (or if it's nowhere).
  1952. hwndFocus = GetFocus();
  1953. if (!hwndFocus || (ppd->hDlg == hwndFocus || IsChild(ppd->hDlg, hwndFocus)))
  1954. SetFocus(hwndNewFocus);
  1955. #endif
  1956. }
  1957. ResetWizButtons(ppd);
  1958. SendMessage(hDlg, DM_SETDEFID, ids[i], 0);
  1959. }
  1960. }
  1961. //
  1962. // lptie = NULL means "I don't care about the other goop, just give me
  1963. // the index."
  1964. //
  1965. int FindItem(HWND hwndTabs, HWND hwndPage, TC_ITEMEXTRA * lptie)
  1966. {
  1967. int i;
  1968. TC_ITEMEXTRA tie;
  1969. if (!lptie)
  1970. {
  1971. tie.tci.mask = TCIF_PARAM;
  1972. lptie = &tie;
  1973. }
  1974. for (i = TabCtrl_GetItemCount(hwndTabs) - 1; i >= 0; --i)
  1975. {
  1976. TabCtrl_GetItem(hwndTabs, i, &lptie->tci);
  1977. if (lptie->hwndPage == hwndPage)
  1978. {
  1979. break;
  1980. }
  1981. }
  1982. //this will be -1 if the for loop falls out.
  1983. return i;
  1984. }
  1985. // a page is telling us that something on it has changed and thus
  1986. // "Apply Now" should be enabled
  1987. void PageInfoChange(LPPROPDATA ppd, HWND hwndPage)
  1988. {
  1989. int i;
  1990. TC_ITEMEXTRA tie;
  1991. tie.tci.mask = TCIF_PARAM;
  1992. i = FindItem(ppd->hwndTabs, hwndPage, &tie);
  1993. if (i == -1)
  1994. return;
  1995. if (!(tie.state & FLAG_CHANGED))
  1996. {
  1997. // tie.tci.mask = TCIF_PARAM; // already set
  1998. tie.state |= FLAG_CHANGED;
  1999. TabCtrl_SetItem(ppd->hwndTabs, i, &tie.tci);
  2000. }
  2001. if (ppd->fAllowApply)
  2002. EnableWindow(GetDlgItem(ppd->hDlg, IDD_APPLYNOW), TRUE);
  2003. }
  2004. // a page is telling us that everything has reverted to its last
  2005. // saved state.
  2006. void PageInfoUnChange(LPPROPDATA ppd, HWND hwndPage)
  2007. {
  2008. int i;
  2009. TC_ITEMEXTRA tie;
  2010. tie.tci.mask = TCIF_PARAM;
  2011. i = FindItem(ppd->hwndTabs, hwndPage, &tie);
  2012. if (i == -1)
  2013. return;
  2014. if (tie.state & FLAG_CHANGED)
  2015. {
  2016. tie.state &= ~FLAG_CHANGED;
  2017. TabCtrl_SetItem(ppd->hwndTabs, i, &tie.tci);
  2018. }
  2019. // check all the pages, if none are FLAG_CHANGED, disable IDD_APLYNOW
  2020. for (i = ppd->psh.nPages-1 ; i >= 0 ; i--)
  2021. {
  2022. if (!TabCtrl_GetItem(ppd->hwndTabs, i, &tie.tci))
  2023. break;
  2024. if (tie.state & FLAG_CHANGED)
  2025. break;
  2026. }
  2027. if (i<0)
  2028. EnableWindow(GetDlgItem(ppd->hDlg, IDD_APPLYNOW), FALSE);
  2029. }
  2030. HDWP Prsht_RepositionControl(LPPROPDATA ppd, HWND hwnd, HDWP hdwp,
  2031. int dxMove, int dyMove, int dxSize, int dySize)
  2032. {
  2033. if (hwnd) {
  2034. RECT rc;
  2035. GetWindowRect(hwnd, &rc);
  2036. MapWindowRect(HWND_DESKTOP, ppd->hDlg, &rc);
  2037. hdwp = DeferWindowPos(hdwp, hwnd, NULL,
  2038. rc.left + dxMove, rc.top + dyMove,
  2039. RECTWIDTH(rc) + dxSize, RECTHEIGHT(rc) + dySize,
  2040. SWP_NOZORDER | SWP_NOACTIVATE);
  2041. }
  2042. return hdwp;
  2043. }
  2044. //
  2045. // dxSize/(dySize+dyMove) is the amount by which to resize the tab control.
  2046. // dxSize/dySize controls how much the dialog should be grown.
  2047. // Buttons move by (dxSize, dySize+dyMove).
  2048. //
  2049. BOOL Prsht_ResizeDialog(LPPROPDATA ppd, int dxSize, int dySize, int dyMove)
  2050. {
  2051. BOOL fChanged = dxSize || dySize || dyMove;
  2052. if (fChanged)
  2053. {
  2054. int dxMove = 0; // To make the code more symmetric in x and y
  2055. int dxAll = dxSize + dxMove;
  2056. int dyAll = dySize + dyMove;
  2057. RECT rc;
  2058. UINT i;
  2059. const int *rgid;
  2060. UINT cid;
  2061. HDWP hdwp;
  2062. HWND hwnd;
  2063. // Use DeferWindowPos to avoid flickering. We expect to move
  2064. // the tab control, up to five buttons, two possible dividers,
  2065. // plus the current page. (And a partridge in a pear tree.)
  2066. //
  2067. hdwp = BeginDeferWindowPos(1 + 5 + 2 + 1);
  2068. // The tab control just sizes.
  2069. hdwp = Prsht_RepositionControl(ppd, ppd->hwndTabs, hdwp,
  2070. 0, 0, dxAll, dyAll);
  2071. //
  2072. // Move and size the current page. We can't trust its location
  2073. // or size, since PageChange shoves it around without updating
  2074. // ppd->ySubDlg.
  2075. //
  2076. if (ppd->hwndCurPage) {
  2077. hdwp = DeferWindowPos(hdwp, ppd->hwndCurPage, NULL,
  2078. ppd->xSubDlg, ppd->ySubDlg,
  2079. ppd->cxSubDlg, ppd->cySubDlg,
  2080. SWP_NOZORDER | SWP_NOACTIVATE);
  2081. }
  2082. //
  2083. // And our buttons just move by both the size and move (since they
  2084. // lie below both the tabs and the pages).
  2085. //
  2086. if (IS_WIZARD(ppd)) {
  2087. //
  2088. // Ooh, wait, reposition the separator lines, too.
  2089. // Moves vertically but resizes horizontally.
  2090. //
  2091. hwnd = GetDlgItem(ppd->hDlg, IDD_DIVIDER);
  2092. hdwp = Prsht_RepositionControl(ppd, hwnd, hdwp,
  2093. 0, dyAll, dxAll, 0);
  2094. //
  2095. // The top divider does not move vertically since it lies
  2096. // above the area that is changing.
  2097. //
  2098. hwnd = GetDlgItem(ppd->hDlg, IDD_TOPDIVIDER);
  2099. hdwp = Prsht_RepositionControl(ppd, hwnd, hdwp,
  2100. 0, 0, dxAll, 0);
  2101. rgid = WizIDs;
  2102. cid = ARRAYSIZE(WizIDs);
  2103. } else {
  2104. rgid = IDs;
  2105. cid = ARRAYSIZE(IDs);
  2106. }
  2107. for (i = 0 ; i < cid; i++)
  2108. {
  2109. hwnd = GetDlgItem(ppd->hDlg, rgid[i]);
  2110. hdwp = Prsht_RepositionControl(ppd, hwnd, hdwp,
  2111. dxAll, dyAll, 0, 0);
  2112. }
  2113. // All finished sizing and moving. Let 'er rip!
  2114. if (hdwp)
  2115. EndDeferWindowPos(hdwp);
  2116. // Grow ourselves as well
  2117. GetWindowRect(ppd->hDlg, &rc);
  2118. SetWindowPos(ppd->hDlg, NULL, 0, 0,
  2119. RECTWIDTH(rc) + dxAll, RECTHEIGHT(rc) + dyAll,
  2120. SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  2121. }
  2122. return fChanged;
  2123. }
  2124. BOOL Prsht_RecalcPageSizes(LPPROPDATA ppd)
  2125. {
  2126. SIZE siz;
  2127. int dxSize = 0, dySize = 0, dyMove = 0;
  2128. // After inserting or removing a page, the tab control may have
  2129. // changed height. If so, then we need to resize ourselves to
  2130. // accomodate the growth or shrinkage, so that all the tabs remain
  2131. // visible.
  2132. //
  2133. // APP COMPAT! We cannot do this by default because Jamba 1.1
  2134. // **FAULTS** if the property sheet changes size after creation.
  2135. // Grrrrrr...
  2136. // Wizards don't have a visible tab control,
  2137. // so do this only for non-wizards
  2138. if (!IS_WIZARD(ppd))
  2139. {
  2140. RECT rc;
  2141. // Get the client rect of the tab control in dialog coords
  2142. GetClientRect(ppd->hwndTabs, &rc);
  2143. MapWindowRect(ppd->hwndTabs, ppd->hDlg, &rc);
  2144. // See how many rows there are now
  2145. TabCtrl_AdjustRect(ppd->hwndTabs, FALSE, &rc);
  2146. // rc.top is the new ySubDlg. Compute the amount we have to move.
  2147. dyMove = rc.top - ppd->ySubDlg;
  2148. ppd->ySubDlg = rc.top;
  2149. }
  2150. Prsht_GetIdealPageSize(ppd, &siz, GIPS_SKIPEXTERIOR97HEIGHT);
  2151. dxSize = siz.cx - ppd->cxSubDlg;
  2152. dySize = siz.cy - ppd->cySubDlg;
  2153. ppd->cxSubDlg = siz.cx;
  2154. ppd->cySubDlg = siz.cy;
  2155. return Prsht_ResizeDialog(ppd, dxSize, dySize, dyMove);
  2156. }
  2157. //
  2158. // InsertPropPage
  2159. //
  2160. // hpage is the page being inserted.
  2161. //
  2162. // hpageInsertAfter described where it should be inserted.
  2163. //
  2164. // hpageInsertAfter can be...
  2165. //
  2166. // MAKEINTRESOURCE(index) to insert at a specific index.
  2167. //
  2168. // NULL to insert at the beginning
  2169. //
  2170. // an HPROPSHEETPAGE to insert *after* that page
  2171. //
  2172. BOOL InsertPropPage(LPPROPDATA ppd, PSP * hpageInsertAfter,
  2173. PSP * hpage)
  2174. {
  2175. TC_ITEMEXTRA tie;
  2176. int nPage;
  2177. HIMAGELIST himl;
  2178. PAGEINFOEX pi;
  2179. PISP pisp;
  2180. int idx;
  2181. hpage = _Hijaak95Hack(ppd, hpage);
  2182. if (!hpage)
  2183. return FALSE;
  2184. if (ppd->psh.nPages >= MAXPROPPAGES)
  2185. return FALSE; // we're full
  2186. if (IS_INTRESOURCE(hpageInsertAfter))
  2187. {
  2188. // Inserting by index
  2189. idx = (int) PtrToLong(hpageInsertAfter);
  2190. // Attempting to insert past the end is the same as appending.
  2191. if (idx > (int)ppd->psh.nPages)
  2192. idx = (int)ppd->psh.nPages;
  2193. }
  2194. else
  2195. {
  2196. // Inserting by hpageInsertAfter.
  2197. for (idx = 0; idx < (int)(ppd->psh.nPages); idx++) {
  2198. if (hpageInsertAfter == GETHPAGE(ppd, idx))
  2199. break;
  2200. }
  2201. if (idx >= (int)(ppd->psh.nPages))
  2202. return FALSE; // hpageInsertAfter not found
  2203. idx++; // idx Points to the insertion location (to the right of hpageInsertAfter)
  2204. ASSERT(hpageInsertAfter == GETHPAGE(ppd, idx-1));
  2205. }
  2206. ASSERT(idx <= (int)(ppd->psh.nPages+1));
  2207. // Shift all pages adjacent to the insertion point to the right
  2208. for (nPage=ppd->psh.nPages - 1; nPage >= idx; nPage--)
  2209. SETPISP(ppd, nPage+1, GETPISP(ppd, nPage));
  2210. // Insert the new page
  2211. pisp = InternalizeHPROPSHEETPAGE(hpage);
  2212. SETPISP(ppd, idx, pisp);
  2213. ppd->psh.nPages++;
  2214. himl = TabCtrl_GetImageList(ppd->hwndTabs);
  2215. if (!GetPageInfoEx(ppd, pisp, &pi, GetMUILanguage(),
  2216. GPI_ICON | GPI_BRTL | GPI_CAPTION | GPI_FONT | GPI_DIALOGEX))
  2217. {
  2218. DebugMsg(DM_ERROR, TEXT("InsertPropPage: GetPageInfo failed"));
  2219. goto bogus;
  2220. }
  2221. Prsht_ComputeIdealPageSize(ppd, pisp, &pi);
  2222. tie.tci.mask = TCIF_TEXT | TCIF_PARAM | TCIF_IMAGE | (pi.bRTL ? TCIF_RTLREADING : 0);
  2223. tie.hwndPage = NULL;
  2224. tie.tci.pszText = pi.szCaption;
  2225. tie.state = 0;
  2226. if (pi.hIcon) {
  2227. if (himl)
  2228. tie.tci.iImage = ImageList_AddIcon(himl, pi.hIcon);
  2229. DestroyIcon(pi.hIcon);
  2230. } else {
  2231. tie.tci.iImage = -1;
  2232. }
  2233. // Insert the page into the tab list
  2234. TabCtrl_InsertItem(ppd->hwndTabs, idx, &tie.tci);
  2235. // If this page wants premature initialization then init it
  2236. // do this last so pages can rely on "being there" at init time
  2237. if (pisp->_psp.dwFlags & PSP_PREMATURE)
  2238. {
  2239. if ((tie.hwndPage = _CreatePage(ppd, pisp, ppd->hDlg, GetMUILanguage())) == NULL)
  2240. {
  2241. TabCtrl_DeleteItem(ppd->hwndTabs, idx);
  2242. // don't free the pisp here let the caller do it
  2243. // REARCHITECT raymondc - but caller doesn't know if hIcon has been destroyed
  2244. goto bogus;
  2245. }
  2246. tie.tci.mask = TCIF_PARAM;
  2247. TabCtrl_SetItem(ppd->hwndTabs, idx, &tie.tci);
  2248. }
  2249. // Adjust the internally track current item if it is to the right of our insertion point
  2250. if (ppd->nCurItem >= idx)
  2251. ppd->nCurItem++;
  2252. return TRUE;
  2253. bogus:
  2254. // Shift everything back
  2255. for (nPage=idx; nPage < (int)(ppd->psh.nPages-1); nPage++)
  2256. SETPISP(ppd, nPage, GETPISP(ppd, nPage+1));
  2257. ppd->psh.nPages--;
  2258. return FALSE;
  2259. }
  2260. #define AddPropPage(ppd, hpage) InsertPropPage(ppd, (LPVOID)MAKEINTRESOURCE(-1), hpage)
  2261. // removes property sheet hpage (index if NULL)
  2262. void RemovePropPage(LPPROPDATA ppd, int index, HPROPSHEETPAGE hpage)
  2263. {
  2264. int i = -1;
  2265. BOOL fReturn = TRUE;
  2266. TC_ITEMEXTRA tie;
  2267. //
  2268. // Notice that we explicitly do not do a InternalizeHPROPSHEETPAGE,
  2269. // because the app might be passing us garbage. We just want to
  2270. // say "Nope, can't find garbage here, sorry."
  2271. //
  2272. tie.tci.mask = TCIF_PARAM;
  2273. if (hpage) {
  2274. i = FindPageIndexByHpage(ppd, hpage);
  2275. }
  2276. if (i == -1) {
  2277. i = index;
  2278. // this catches i < 0 && i >= (int)(ppd->psh.nPages)
  2279. if ((UINT)i >= ppd->psh.nPages)
  2280. {
  2281. DebugMsg(DM_ERROR, TEXT("RemovePropPage: invalid page"));
  2282. return;
  2283. }
  2284. }
  2285. index = TabCtrl_GetCurSel(ppd->hwndTabs);
  2286. if (i == index) {
  2287. // if we're removing the current page, select another (don't worry
  2288. // about this page having invalid information on it -- we're nuking it)
  2289. PageChanging(ppd);
  2290. if (index == 0)
  2291. index++;
  2292. else
  2293. index--;
  2294. if (SendMessage(ppd->hwndTabs, TCM_SETCURSEL, index, 0L) == -1) {
  2295. // if we couldn't select (find) the new one, punt to 0th
  2296. SendMessage(ppd->hwndTabs, TCM_SETCURSEL, 0, 0L);
  2297. }
  2298. PageChange(ppd, 1);
  2299. }
  2300. // REARCHITECT if removing a page below ppd->nCurItem, need to update
  2301. // nCurItem to prevent it from getting out of sync with hwndCurPage?
  2302. tie.tci.mask = TCIF_PARAM;
  2303. TabCtrl_GetItem(ppd->hwndTabs, i, &tie.tci);
  2304. if (tie.hwndPage) {
  2305. if (ppd->hwndCurPage == tie.hwndPage)
  2306. ppd->hwndCurPage = NULL;
  2307. DestroyWindow(tie.hwndPage);
  2308. }
  2309. RemovePropPageData(ppd, i);
  2310. }
  2311. void RemovePropPageData(LPPROPDATA ppd, int nPage)
  2312. {
  2313. ULONG_PTR dw = PropPageActivateContext(ppd, GETPISP(ppd, nPage));
  2314. TabCtrl_DeleteItem(ppd->hwndTabs, nPage);
  2315. DestroyPropertySheetPage(GETHPAGE(ppd, nPage));
  2316. PropPageDeactivateContext(dw);
  2317. //
  2318. // Delete the HPROPSHEETPAGE from our table and slide everybody down.
  2319. //
  2320. ppd->psh.nPages--;
  2321. hmemcpy(&ppd->psh.H_phpage[nPage], &ppd->psh.H_phpage[nPage + 1],
  2322. sizeof(ppd->psh.H_phpage[0]) * (ppd->psh.nPages - nPage));
  2323. }
  2324. // returns TRUE iff the page was successfully set to index/hpage
  2325. // Note: The iAutoAdj should be set to 1 or -1. This value is used
  2326. // by PageChange if a page refuses a SETACTIVE to either increment
  2327. // or decrement the page index.
  2328. BOOL PageSetSelection(LPPROPDATA ppd, int index, HPROPSHEETPAGE hpage,
  2329. int iAutoAdj)
  2330. {
  2331. int i = -1;
  2332. BOOL fReturn = FALSE;
  2333. TC_ITEMEXTRA tie;
  2334. tie.tci.mask = TCIF_PARAM;
  2335. if (hpage) {
  2336. for (i = ppd->psh.nPages - 1; i >= 0; i--) {
  2337. if (hpage == GETHPAGE(ppd, i))
  2338. break;
  2339. }
  2340. }
  2341. if (i == -1) {
  2342. if (index == -1)
  2343. return FALSE;
  2344. i = index;
  2345. }
  2346. if (i >= MAXPROPPAGES)
  2347. {
  2348. // don't go off the end of our HPROPSHEETPAGE array
  2349. return FALSE;
  2350. }
  2351. fReturn = !PageChanging(ppd);
  2352. if (fReturn)
  2353. {
  2354. index = TabCtrl_GetCurSel(ppd->hwndTabs);
  2355. if (SendMessage(ppd->hwndTabs, TCM_SETCURSEL, i, 0L) == -1) {
  2356. // if we couldn't select (find) the new one, fail out
  2357. // and restore the old one
  2358. SendMessage(ppd->hwndTabs, TCM_SETCURSEL, index, 0L);
  2359. fReturn = FALSE;
  2360. }
  2361. PageChange(ppd, iAutoAdj);
  2362. }
  2363. return fReturn;
  2364. }
  2365. LRESULT QuerySiblings(LPPROPDATA ppd, WPARAM wParam, LPARAM lParam)
  2366. {
  2367. UINT i;
  2368. for (i = 0 ; i < ppd->psh.nPages ; i++)
  2369. {
  2370. HWND hwndSibling = _Ppd_GetPage(ppd, i);
  2371. if (hwndSibling)
  2372. {
  2373. LRESULT lres = SendMessage(hwndSibling, PSM_QUERYSIBLINGS, wParam, lParam);
  2374. if (lres)
  2375. return lres;
  2376. }
  2377. }
  2378. return FALSE;
  2379. }
  2380. // REVIEW HACK This gets round the problem of having a hotkey control
  2381. // up and trying to enter the hotkey that is already in use by a window.
  2382. BOOL HandleHotkey(LPARAM lparam)
  2383. {
  2384. WORD wHotkey;
  2385. TCHAR szClass[32];
  2386. HWND hwnd;
  2387. // What hotkey did the user type hit?
  2388. wHotkey = (WORD)SendMessage((HWND)lparam, WM_GETHOTKEY, 0, 0);
  2389. // Were they typing in a hotkey window?
  2390. hwnd = GetFocus();
  2391. GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
  2392. if (lstrcmp(szClass, HOTKEY_CLASS) == 0)
  2393. {
  2394. // Yes.
  2395. SendMessage(hwnd, HKM_SETHOTKEY, wHotkey, 0);
  2396. return TRUE;
  2397. }
  2398. return FALSE;
  2399. }
  2400. //
  2401. // Function handles Next and Back functions for wizards. The code will
  2402. // be either PSN_WIZNEXT or PSN_WIZBACK
  2403. //
  2404. BOOL WizNextBack(LPPROPDATA ppd, int code)
  2405. {
  2406. LRESULT dwFind;
  2407. int iPageIndex;
  2408. int iAutoAdj = (code == PSN_WIZNEXT) ? 1 : -1;
  2409. dwFind = _Ppd_SendNotify(ppd, ppd->nCurItem, code, 0);
  2410. if (dwFind == -1) {
  2411. return(FALSE);
  2412. }
  2413. iPageIndex = FindPageIndex(ppd, ppd->nCurItem, dwFind, iAutoAdj);
  2414. if (iPageIndex == -1) {
  2415. return(FALSE);
  2416. }
  2417. return(PageSetSelection(ppd, iPageIndex, NULL, iAutoAdj));
  2418. }
  2419. BOOL Prsht_OnCommand(LPPROPDATA ppd, int id, HWND hwndCtrl, UINT codeNotify)
  2420. {
  2421. //
  2422. // There's a bug in USER that when the user highlights a defpushbutton
  2423. // and presses ENTER, the WM_COMMAND is sent to the top-level dialog
  2424. // (i.e., the property sheet) instead of to the parent of the button.
  2425. // So if a property sheet page has a control whose ID coincidentally
  2426. // matches any of our own, we will think it's ours instead of theirs.
  2427. if (hwndCtrl && GetParent(hwndCtrl) != ppd->hDlg)
  2428. goto Forward;
  2429. if (!hwndCtrl)
  2430. hwndCtrl = GetDlgItem(ppd->hDlg, id);
  2431. switch (id) {
  2432. case IDCLOSE:
  2433. case IDCANCEL:
  2434. if (_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_QUERYCANCEL, 0) == 0) {
  2435. ButtonPushed(ppd, id);
  2436. }
  2437. break;
  2438. case IDD_APPLYNOW:
  2439. case IDOK:
  2440. if (!IS_WIZARD(ppd)) {
  2441. //ButtonPushed returns true if and only if all pages have processed PSN_LASTCHANCEAPPLY
  2442. if (ButtonPushed(ppd, id))
  2443. {
  2444. //Everyone has processed the PSN_APPLY Message. Now send PSN_LASTCHANCEAPPLY message.
  2445. //
  2446. // HACKHACK (reinerF)
  2447. //
  2448. // We send out a private PSN_LASTCHANCEAPPLY message telling all the pages
  2449. // that everyone is done w/ the apply. This is needed for pages who have to do
  2450. // something after every other page has applied. Currently, the "General" tab
  2451. // of the file properties needs a last-chance to rename files as well as new print
  2452. // dialog in comdlg32.dll.
  2453. SendLastChanceApply(ppd);
  2454. }
  2455. }
  2456. break;
  2457. case IDHELP:
  2458. if (IsWindowEnabled(hwndCtrl))
  2459. {
  2460. _Ppd_SendNotify(ppd, ppd->nCurItem, PSN_HELP, 0);
  2461. }
  2462. break;
  2463. case IDD_FINISH:
  2464. {
  2465. HWND hwndNewFocus;
  2466. EnableWindow(ppd->hDlg, FALSE);
  2467. hwndNewFocus = (HWND)_Ppd_SendNotify(ppd, ppd->nCurItem, PSN_WIZFINISH, 0);
  2468. // b#11346 - dont let multiple clicks on FINISH.
  2469. if (!hwndNewFocus)
  2470. {
  2471. ppd->hwndCurPage = NULL;
  2472. ppd->nReturn = 1;
  2473. }
  2474. else
  2475. {
  2476. EnableWindow(ppd->hDlg, TRUE);
  2477. if (IsWindow(hwndNewFocus) && IsChild(ppd->hDlg, hwndNewFocus))
  2478. #ifdef WM_NEXTDLGCTL_WORKS
  2479. SetDlgFocus(ppd, hwndNewFocus);
  2480. #else
  2481. SetFocus(hwndNewFocus);
  2482. #endif
  2483. }
  2484. }
  2485. break;
  2486. case IDD_NEXT:
  2487. case IDD_BACK:
  2488. ppd->idDefaultFallback = id;
  2489. WizNextBack(ppd, id == IDD_NEXT ? PSN_WIZNEXT : PSN_WIZBACK);
  2490. break;
  2491. default:
  2492. Forward:
  2493. FORWARD_WM_COMMAND(_Ppd_GetPage(ppd, ppd->nCurItem), id, hwndCtrl, codeNotify, SendMessage);
  2494. }
  2495. return TRUE;
  2496. }
  2497. BOOL Prop_IsDialogMessage(LPPROPDATA ppd, LPMSG32 pmsg32)
  2498. {
  2499. // Don't fault if we don't have a pointer...
  2500. if (!pmsg32)
  2501. return FALSE;
  2502. if ((pmsg32->message == WM_KEYDOWN) && (GetKeyState(VK_CONTROL) < 0))
  2503. {
  2504. BOOL bBack = FALSE;
  2505. switch (pmsg32->wParam) {
  2506. case VK_TAB:
  2507. bBack = GetKeyState(VK_SHIFT) < 0;
  2508. break;
  2509. case VK_PRIOR: // VK_PAGE_UP
  2510. case VK_NEXT: // VK_PAGE_DOWN
  2511. bBack = (pmsg32->wParam == VK_PRIOR);
  2512. break;
  2513. default:
  2514. goto NoKeys;
  2515. }
  2516. //notify of navigation key usage
  2517. SendMessage(ppd->hDlg, WM_CHANGEUISTATE,
  2518. MAKELONG(UIS_CLEAR, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
  2519. if (IS_WIZARD(ppd))
  2520. {
  2521. int idWiz;
  2522. int idDlg;
  2523. HWND hwnd;
  2524. if (bBack) {
  2525. idWiz = PSN_WIZBACK;
  2526. idDlg = IDD_BACK;
  2527. } else {
  2528. idWiz = PSN_WIZNEXT;
  2529. idDlg = IDD_NEXT;
  2530. }
  2531. hwnd = GetDlgItem(ppd->hDlg, idDlg);
  2532. if (IsWindowVisible(hwnd) && IsWindowEnabled(hwnd))
  2533. WizNextBack(ppd, idWiz);
  2534. }
  2535. else
  2536. {
  2537. int iStart = TabCtrl_GetCurSel(ppd->hwndTabs);
  2538. int iCur;
  2539. //
  2540. // Skip over hidden tabs, but don't go into an infinite loop.
  2541. //
  2542. iCur = iStart;
  2543. do {
  2544. // tab in reverse if shift is down
  2545. if (bBack)
  2546. iCur += (ppd->psh.nPages - 1);
  2547. else
  2548. iCur++;
  2549. iCur %= ppd->psh.nPages;
  2550. } while (_Ppd_IsPageHidden(ppd, iCur) && iCur != iStart);
  2551. PageSetSelection(ppd, iCur, NULL, 1);
  2552. }
  2553. return TRUE;
  2554. }
  2555. NoKeys:
  2556. //
  2557. // Since we now send out a PSN_TRANSLATEACCELERATOR, add a
  2558. // short-circuit so we don't do all this work for things
  2559. // that can't possibly be accelerators.
  2560. //
  2561. if (pmsg32->message >= WM_KEYFIRST && pmsg32->message <= WM_KEYLAST &&
  2562. // And there had better be a target window...
  2563. pmsg32->hwnd &&
  2564. // and the target window must live either outside the propsheet
  2565. // altogether or completely inside the propsheet page.
  2566. // (This is so that the propsheet can display its own popup dialog,
  2567. // but can't futz with the tab control or OK/Cancel buttons.)
  2568. (!IsChild(ppd->hDlg, pmsg32->hwnd) ||
  2569. IsChild(ppd->hwndCurPage, pmsg32->hwnd)) &&
  2570. // Then ask the propsheet if he wants to eat it.
  2571. _Ppd_SendNotify(ppd, ppd->nCurItem,
  2572. PSN_TRANSLATEACCELERATOR, (LPARAM)pmsg32) == PSNRET_MESSAGEHANDLED)
  2573. return TRUE;
  2574. if (IsDialogMessage32(ppd->hDlg, pmsg32, TRUE))
  2575. return TRUE;
  2576. return FALSE;
  2577. }
  2578. HRESULT Prsht_GetObject (LPPROPDATA ppd, HWND hDlg, int iItem, const IID *piid, void **pObject)
  2579. {
  2580. TC_ITEMEXTRA tie;
  2581. NMOBJECTNOTIFY non;
  2582. PISP pisp = GETPISP(ppd, iItem);
  2583. *pObject = NULL;
  2584. tie.tci.mask = TCIF_PARAM;
  2585. TabCtrl_GetItem(ppd->hwndTabs, iItem, &tie.tci);
  2586. if (!tie.hwndPage && ((tie.hwndPage = _CreatePage(ppd, pisp, hDlg, GetMUILanguage())) == NULL))
  2587. {
  2588. RemovePropPageData(ppd, iItem);
  2589. return E_UNEXPECTED;
  2590. }
  2591. TabCtrl_SetItem(ppd->hwndTabs, iItem, &tie.tci);
  2592. non.iItem = -1;
  2593. non.piid = piid;
  2594. non.pObject = NULL;
  2595. non.hResult = E_NOINTERFACE;
  2596. non.dwFlags = 0;
  2597. SendNotifyEx (tie.hwndPage, ppd->hwndTabs, PSN_GETOBJECT,
  2598. &non.hdr,
  2599. TRUE
  2600. );
  2601. if (SUCCEEDED (non.hResult))
  2602. {
  2603. *pObject = non.pObject;
  2604. if (pObject == NULL)
  2605. non.hResult = E_UNEXPECTED;
  2606. }
  2607. else if (non.pObject)
  2608. {
  2609. ((LPDROPTARGET) non.pObject)->lpVtbl->Release ((LPDROPTARGET) non.pObject);
  2610. non.pObject = NULL;
  2611. }
  2612. return non.hResult;
  2613. }
  2614. //
  2615. // We would not normally need IDD_PAGELIST except that DefWindowProc() and
  2616. // WinHelp() do hit-testing differently. DefWindowProc() will do cool
  2617. // things like checking against the SetWindowRgn and skipping over windows
  2618. // that return HTTRANSPARENT. WinHelp() on the other hand
  2619. // ignores window regions and transparency. So what happens is if you
  2620. // click on the transparent part of a tab control, DefWindowProc() says
  2621. // (correctly) "He clicked on the dialog background". We then say, "Okay,
  2622. // WinHelp(), go display context help for the dialog background", and it
  2623. // says, "Hey, I found a tab control. I'm going to display help for the
  2624. // tab control now." To keep a bogus context menu from appearing, we
  2625. // explicitly tell WinHelp that "If you found a tab control (IDD_PAGELIST),
  2626. // then ignore it (NO_HELP)."
  2627. //
  2628. const static DWORD aPropHelpIDs[] = { // Context Help IDs
  2629. IDD_APPLYNOW, IDH_COMM_APPLYNOW,
  2630. IDD_PAGELIST, NO_HELP,
  2631. 0, 0
  2632. };
  2633. void HandlePaletteChange(LPPROPDATA ppd, UINT uMessage, HWND hDlg)
  2634. {
  2635. HDC hdc;
  2636. hdc = GetDC(hDlg);
  2637. if (hdc)
  2638. {
  2639. BOOL fRepaint;
  2640. SelectPalette(hdc,ppd->hplWatermark,(uMessage == WM_PALETTECHANGED));
  2641. fRepaint = RealizePalette(hdc);
  2642. if (fRepaint)
  2643. InvalidateRect(hDlg,NULL,TRUE);
  2644. }
  2645. ReleaseDC(hDlg,hdc);
  2646. }
  2647. //
  2648. // Paint a rectangle with the specified brush and palette.
  2649. //
  2650. void PaintWithPaletteBrush(HDC hdc, LPRECT lprc, HPALETTE hplPaint, HBRUSH hbrPaint)
  2651. {
  2652. HBRUSH hbrPrev = SelectBrush(hdc, hbrPaint);
  2653. UnrealizeObject(hbrPaint);
  2654. if (hplPaint)
  2655. {
  2656. SelectPalette(hdc, hplPaint, FALSE);
  2657. RealizePalette(hdc);
  2658. }
  2659. FillRect(hdc, lprc, hbrPaint);
  2660. SelectBrush(hdc, hbrPrev);
  2661. }
  2662. //
  2663. // lprc is the target rectangle.
  2664. // Use as much of the bitmap as will fit into the target rectangle.
  2665. // If the bitmap is smaller than the target rectangle, then fill the rest with
  2666. // the pixel in the upper left corner of the hbmpPaint.
  2667. //
  2668. void PaintWithPaletteBitmap(HDC hdc, LPRECT lprc, HPALETTE hplPaint, HBITMAP hbmpPaint)
  2669. {
  2670. HDC hdcBmp;
  2671. BITMAP bm;
  2672. int cxRect, cyRect, cxBmp, cyBmp;
  2673. GetObject(hbmpPaint, sizeof(BITMAP), &bm);
  2674. hdcBmp = CreateCompatibleDC(hdc);
  2675. SelectObject(hdcBmp, hbmpPaint);
  2676. if (hplPaint)
  2677. {
  2678. SelectPalette(hdc, hplPaint, FALSE);
  2679. RealizePalette(hdc);
  2680. }
  2681. cxRect = RECTWIDTH(*lprc);
  2682. cyRect = RECTHEIGHT(*lprc);
  2683. // Never use more pixels from the bmp as we have room in the rect.
  2684. cxBmp = min(bm.bmWidth, cxRect);
  2685. cyBmp = min(bm.bmHeight, cyRect);
  2686. BitBlt(hdc, lprc->left, lprc->top, cxBmp, cyBmp, hdcBmp, 0, 0, SRCCOPY);
  2687. // If bitmap is too narrow, then StretchBlt to fill the width.
  2688. if (cxBmp < cxRect)
  2689. StretchBlt(hdc, lprc->left + cxBmp, lprc->top,
  2690. cxRect - cxBmp, cyBmp,
  2691. hdcBmp, 0, 0, 1, 1, SRCCOPY);
  2692. // If bitmap is to short, then StretchBlt to fill the height.
  2693. if (cyBmp < cyRect)
  2694. StretchBlt(hdc, lprc->left, cyBmp,
  2695. cxRect, cyRect - cyBmp,
  2696. hdcBmp, 0, 0, 1, 1, SRCCOPY);
  2697. DeleteDC(hdcBmp);
  2698. }
  2699. void _SetHeaderTitles(HWND hDlg, LPPROPDATA ppd, UINT uPage, LPCTSTR pszNewTitle, BOOL bTitle)
  2700. {
  2701. PISP pisp = NULL;
  2702. // Must be for wizard97
  2703. if (ppd->psh.dwFlags & PSH_WIZARD97)
  2704. {
  2705. // Page number must be within range
  2706. if (uPage < ppd->psh.nPages)
  2707. {
  2708. // Get the page structure
  2709. pisp = GETPISP(ppd, uPage);
  2710. // We should have this page if it's within range
  2711. ASSERT(pisp);
  2712. // Do this only if this page has header.
  2713. if (!(pisp->_psp.dwFlags & PSP_HIDEHEADER))
  2714. {
  2715. LPCTSTR pszOldTitle = bTitle ? pisp->_psp.pszHeaderTitle : pisp->_psp.pszHeaderSubTitle;
  2716. if (!IS_INTRESOURCE(pszOldTitle))
  2717. LocalFree((LPVOID)pszOldTitle);
  2718. // Set the new title
  2719. if (bTitle)
  2720. pisp->_psp.pszHeaderTitle = pszNewTitle;
  2721. else
  2722. pisp->_psp.pszHeaderSubTitle = pszNewTitle;
  2723. // set pszNewTitle to NULL here so that we don't free it later
  2724. pszNewTitle = NULL;
  2725. // set the correct flags
  2726. pisp->_psp.dwFlags |= bTitle ? PSP_USEHEADERTITLE : PSP_USEHEADERSUBTITLE;
  2727. // force redrawing of the titles
  2728. if (uPage == (UINT)ppd->nCurItem)
  2729. {
  2730. RECT rcHeader;
  2731. GetClientRect(hDlg, &rcHeader);
  2732. rcHeader.bottom = ppd->cyHeaderHeight;
  2733. InvalidateRect(hDlg, &rcHeader, FALSE);
  2734. }
  2735. }
  2736. }
  2737. }
  2738. if (pszNewTitle)
  2739. LocalFree((LPVOID)pszNewTitle);
  2740. }
  2741. void PropSheetPaintHeader(LPPROPDATA ppd, PISP pisp, HWND hDlg, HDC hdc)
  2742. {
  2743. RECT rcHeader;
  2744. GetClientRect(hDlg, &rcHeader);
  2745. if ((rcHeader.right > 0) && (rcHeader.bottom > 0))
  2746. {
  2747. RECT rcHeaderBitmap;
  2748. rcHeader.bottom = ppd->cyHeaderHeight;
  2749. // do we need to paint the header?
  2750. if (ppd->psh.dwFlags & PSH_WIZARD97IE4)
  2751. {
  2752. // Do it the WIZARD97IE4 way
  2753. // Bug-for-bug compatibility: WIZARD97IE4 tested the wrong flag here
  2754. if ((ppd->psh.dwFlags & PSH_WATERMARK) && (ppd->hbrWatermark))
  2755. PaintWithPaletteBrush(hdc, &rcHeader, ppd->hplWatermark, ppd->hbrHeader);
  2756. SetBkMode(hdc, TRANSPARENT);
  2757. }
  2758. else
  2759. {
  2760. // Do it the WIZARD97IE5 way
  2761. if ((ppd->psh.dwFlags & PSH_HEADER) && (ppd->hbmHeader))
  2762. {
  2763. // compute the rectangle for the bitmap depending on the size of the header
  2764. int bx = RECTWIDTH(rcHeader) - HEADERBITMAP_CXBACK;
  2765. ASSERT(bx > 0);
  2766. FillRect(hdc, &rcHeader, g_hbrWindow);
  2767. SetRect(&rcHeaderBitmap, bx, HEADERBITMAP_Y, bx + HEADERBITMAP_WIDTH, HEADERBITMAP_Y + HEADERBITMAP_HEIGHT);
  2768. PaintWithPaletteBitmap(hdc, &rcHeaderBitmap, ppd->hplWatermark, ppd->hbmHeader);
  2769. SetBkColor(hdc, g_clrWindow);
  2770. SetTextColor(hdc, g_clrWindowText);
  2771. }
  2772. else
  2773. {
  2774. SendMessage(hDlg, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hDlg);
  2775. }
  2776. }
  2777. //
  2778. // WIZARD97IE5 subtracts out the space used by the header bitmap.
  2779. // WIZARD97IE4 uses the full width since the header bitmap
  2780. // in IE4 is a watermark and occupies no space.
  2781. //
  2782. if (!(ppd->psh.dwFlags & PSH_WIZARD97IE4))
  2783. {
  2784. rcHeader.right -= HEADERBITMAP_CXBACK + HEADERSUBTITLE_WRAPOFFSET;
  2785. }
  2786. ASSERT(rcHeader.right);
  2787. if (HASHEADERTITLE(pisp))
  2788. {
  2789. _WriteHeaderTitle(ppd, hdc, &rcHeader, pisp->_psp.pszHeaderTitle,
  2790. TRUE, DRAWTEXT_WIZARD97FLAGS);
  2791. }
  2792. if (HASHEADERSUBTITLE(pisp))
  2793. {
  2794. _WriteHeaderTitle(ppd, hdc, &rcHeader, pisp->_psp.pszHeaderSubTitle,
  2795. FALSE, DRAWTEXT_WIZARD97FLAGS);
  2796. }
  2797. }
  2798. }
  2799. // Free the title if we need to
  2800. void Prsht_FreeTitle(LPPROPDATA ppd)
  2801. {
  2802. if (ppd->fFlags & PD_FREETITLE) {
  2803. ppd->fFlags &= ~PD_FREETITLE;
  2804. if (!IS_INTRESOURCE(ppd->psh.pszCaption)) {
  2805. LocalFree((LPVOID)ppd->psh.pszCaption);
  2806. }
  2807. }
  2808. }
  2809. //
  2810. // pfnStrDup is the function that converts lParam into a native character
  2811. // set string. (Either StrDup or StrDup_AtoW).
  2812. //
  2813. void Prsht_OnSetTitle(LPPROPDATA ppd, WPARAM wParam, LPARAM lParam, STRDUPPROC pfnStrDup)
  2814. {
  2815. LPTSTR pszTitle;
  2816. //
  2817. // The ppd->psh.pszCaption is not normally LocalAlloc()d; it's
  2818. // just a pointer copy. But if the app does a PSM_SETTITLE,
  2819. // then all of a sudden it got LocalAlloc()d and needs to be
  2820. // freed. PD_FREETITLE is the flag that tell us that this has
  2821. // happened.
  2822. //
  2823. if (IS_INTRESOURCE(lParam)) {
  2824. pszTitle = (LPTSTR)lParam;
  2825. } else {
  2826. pszTitle = pfnStrDup((LPTSTR)lParam);
  2827. }
  2828. if (pszTitle) {
  2829. Prsht_FreeTitle(ppd); // Free old title if necessary
  2830. ppd->psh.pszCaption = pszTitle;
  2831. ppd->fFlags |= PD_FREETITLE; // Need to free this
  2832. ppd->psh.dwFlags = ((((DWORD)wParam) & PSH_PROPTITLE) | (ppd->psh.dwFlags & ~PSH_PROPTITLE));
  2833. _SetTitle(ppd->hDlg, ppd);
  2834. }
  2835. }
  2836. BOOL_PTR CALLBACK PropSheetDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
  2837. {
  2838. HWND hwndT;
  2839. LPPROPDATA ppd = (LPPROPDATA)GetWindowLongPtr(hDlg, DWLP_USER);
  2840. LRESULT lres;
  2841. if (!ppd && (uMessage != WM_INITDIALOG))
  2842. return FALSE;
  2843. switch (uMessage)
  2844. {
  2845. case WM_INITDIALOG:
  2846. InitPropSheetDlg(hDlg, (LPPROPDATA)lParam);
  2847. return FALSE;
  2848. case WM_SYSCOMMAND:
  2849. if (wParam == SC_HOTKEY)
  2850. return HandleHotkey(lParam);
  2851. else if (wParam == SC_CLOSE)
  2852. {
  2853. UINT id = IDCLOSE;
  2854. if (IS_WIZARD(ppd))
  2855. id = IDCANCEL;
  2856. else if (ppd->fFlags & PD_CANCELTOCLOSE)
  2857. id = IDOK;
  2858. // system menu close should be IDCANCEL, but if we're in the
  2859. // PSM_CANCELTOCLOSE state, treat it as an IDOK (ie, "Close").
  2860. return Prsht_OnCommand(ppd, id, NULL, 0);
  2861. }
  2862. return FALSE; // Let default process happen
  2863. case WM_NCDESTROY:
  2864. {
  2865. int iPage;
  2866. ASSERT(GetDlgItem(hDlg, IDD_PAGELIST) == NULL);
  2867. ppd->hwndTabs = NULL;
  2868. // NOTE: all of the hwnds for the pages must be destroyed by now!
  2869. // Release all page objects in REVERSE ORDER so we can have
  2870. // pages that are dependant on eachother based on the initial
  2871. // order of those pages
  2872. //
  2873. for (iPage = ppd->psh.nPages - 1; iPage >= 0; iPage--)
  2874. {
  2875. ULONG_PTR dw = PropPageActivateContext(ppd, GETPISP(ppd, iPage));
  2876. DestroyPropertySheetPage(GETHPAGE(ppd, iPage));
  2877. PropPageDeactivateContext(dw);
  2878. }
  2879. // hwndCurPage is no longer valid from here on
  2880. ppd->hwndCurPage = NULL;
  2881. if (ppd->hActCtxInit)
  2882. ReleaseActCtx(ppd->hActCtxInit);
  2883. // If we are modeless, we need to free our ppd. If we are modal,
  2884. // we let _RealPropertySheet free it since one of our pages may
  2885. // set the restart flag during DestroyPropertySheetPage above.
  2886. if (ppd->psh.dwFlags & PSH_MODELESS)
  2887. {
  2888. LocalFree(ppd);
  2889. }
  2890. }
  2891. //
  2892. // NOTES:
  2893. // Must return FALSE to avoid DS leak!!!
  2894. //
  2895. return FALSE;
  2896. case WM_DESTROY:
  2897. {
  2898. int i;
  2899. HIMAGELIST himl;
  2900. int c = TabCtrl_GetItemCount(ppd->hwndTabs);
  2901. for (i = 0; i < c; i++)
  2902. {
  2903. EnableThemeDialogTexture(_Ppd_GetPage(ppd, i), ETDT_DISABLE);
  2904. }
  2905. // Destroy the image list we created during our init call.
  2906. himl = TabCtrl_GetImageList(ppd->hwndTabs);
  2907. if (himl)
  2908. ImageList_Destroy(himl);
  2909. if (ppd->psh.dwFlags & PSH_WIZARD97)
  2910. {
  2911. // Even if the PSH_USEHBMxxxxxx flag is set, we might
  2912. // need to delete the bitmap if we had to create a
  2913. // stretched copy.
  2914. if (ppd->psh.dwFlags & PSH_WATERMARK)
  2915. {
  2916. if ((!(ppd->psh.dwFlags & PSH_USEHBMWATERMARK) ||
  2917. ppd->hbmWatermark != ppd->psh.H_hbmWatermark) &&
  2918. ppd->hbmWatermark)
  2919. DeleteObject(ppd->hbmWatermark);
  2920. if (!(ppd->psh.dwFlags & PSH_USEHPLWATERMARK) &&
  2921. ppd->hplWatermark)
  2922. DeleteObject(ppd->hplWatermark);
  2923. if (ppd->hbrWatermark)
  2924. DeleteObject(ppd->hbrWatermark);
  2925. }
  2926. if ((ppd->psh.dwFlags & PSH_HEADER) && ppd->psh.H_hbmHeader)
  2927. {
  2928. if ((!(ppd->psh.dwFlags & PSH_USEHBMHEADER) ||
  2929. ppd->hbmHeader != ppd->psh.H_hbmHeader) &&
  2930. ppd->hbmHeader)
  2931. {
  2932. ASSERT(ppd->hbmHeader != ppd->hbmWatermark);
  2933. DeleteObject(ppd->hbmHeader);
  2934. }
  2935. if (ppd->hbrHeader)
  2936. {
  2937. ASSERT(ppd->hbrHeader != ppd->hbrWatermark);
  2938. DeleteObject(ppd->hbrHeader);
  2939. }
  2940. }
  2941. if (ppd->hFontBold)
  2942. DeleteObject(ppd->hFontBold);
  2943. }
  2944. if ((ppd->psh.dwFlags & PSH_USEICONID) && ppd->psh.H_hIcon)
  2945. DestroyIcon(ppd->psh.H_hIcon);
  2946. Prsht_FreeTitle(ppd);
  2947. }
  2948. break;
  2949. case WM_ERASEBKGND:
  2950. return ppd->fFlags & PD_NOERASE;
  2951. break;
  2952. case WM_PAINT:
  2953. {
  2954. PAINTSTRUCT ps;
  2955. HDC hdc;
  2956. PISP pisp;
  2957. hdc = BeginPaint(hDlg, &ps);
  2958. // only paint if the invalid rect has a non-zero widht or height
  2959. if (((ps.rcPaint.bottom - ps.rcPaint.top) > 0) &&
  2960. ((ps.rcPaint.right - ps.rcPaint.left) > 0))
  2961. {
  2962. // (dli) paint the header
  2963. if ((ppd->psh.dwFlags & PSH_WIZARD97) &&
  2964. (!((pisp = GETPISP(ppd, ppd->nCurItem))->_psp.dwFlags & PSP_HIDEHEADER)))
  2965. {
  2966. PropSheetPaintHeader(ppd, pisp, hDlg, hdc);
  2967. }
  2968. if (ps.fErase) {
  2969. SendMessage (hDlg, WM_ERASEBKGND, (WPARAM) hdc, 0);
  2970. }
  2971. }
  2972. EndPaint(hDlg, &ps);
  2973. }
  2974. break;
  2975. case WM_COMMAND:
  2976. // Cannot use HANDLE_WM_COMMAND, because we want to pass a result!
  2977. return Prsht_OnCommand(ppd, GET_WM_COMMAND_ID(wParam, lParam),
  2978. GET_WM_COMMAND_HWND(wParam, lParam),
  2979. GET_WM_COMMAND_CMD(wParam, lParam));
  2980. case WM_NOTIFY:
  2981. switch (((NMHDR *)lParam)->code)
  2982. {
  2983. case TCN_SELCHANGE:
  2984. PageChange(ppd, 1);
  2985. break;
  2986. case TCN_SELCHANGING:
  2987. {
  2988. lres = PageChanging(ppd);
  2989. if (!lres) {
  2990. SetWindowPos(ppd->hwndCurPage, HWND_BOTTOM, 0,0,0,0, SWP_NOACTIVATE | SWP_NOSIZE |SWP_NOMOVE);
  2991. }
  2992. goto ReturnLres;
  2993. }
  2994. break;
  2995. case TCN_GETOBJECT:
  2996. {
  2997. LPNMOBJECTNOTIFY lpnmon = (LPNMOBJECTNOTIFY)lParam;
  2998. lpnmon->hResult = Prsht_GetObject(ppd, hDlg, lpnmon->iItem,
  2999. lpnmon->piid, &lpnmon->pObject);
  3000. }
  3001. break;
  3002. default:
  3003. return FALSE;
  3004. }
  3005. return TRUE;
  3006. case PSM_SETWIZBUTTONS:
  3007. SetWizButtons(ppd, lParam);
  3008. break;
  3009. case PSM_SETFINISHTEXTA:
  3010. case PSM_SETFINISHTEXT:
  3011. {
  3012. HWND hFinish = GetDlgItem(hDlg, IDD_FINISH);
  3013. HWND hwndFocus = GetFocus();
  3014. HWND hwnd;
  3015. BOOL fSetFocus = FALSE;
  3016. if (!(ppd->psh.dwFlags & PSH_WIZARDHASFINISH)) {
  3017. hwnd = GetDlgItem(hDlg, IDD_NEXT);
  3018. if (hwnd == hwndFocus)
  3019. fSetFocus = TRUE;
  3020. ShowWindow(hwnd, SW_HIDE);
  3021. }
  3022. hwnd = GetDlgItem(hDlg, IDD_BACK);
  3023. if (hwnd == hwndFocus)
  3024. fSetFocus = TRUE;
  3025. ShowWindow(hwnd, SW_HIDE);
  3026. if (lParam)
  3027. {
  3028. if (uMessage == PSM_SETFINISHTEXTA)
  3029. {
  3030. SetWindowTextA(hFinish, (LPSTR)lParam);
  3031. }
  3032. else
  3033. Button_SetText(hFinish, (LPTSTR)lParam);
  3034. }
  3035. ShowWindow(hFinish, SW_SHOW);
  3036. Button_Enable(hFinish, TRUE);
  3037. ResetWizButtons(ppd);
  3038. SendMessage(hDlg, DM_SETDEFID, IDD_FINISH, 0);
  3039. ppd->idDefaultFallback = IDD_FINISH;
  3040. if (fSetFocus)
  3041. #ifdef WM_NEXTDLGCTL_WORKS
  3042. SetDlgFocus(ppd, hFinish);
  3043. #else
  3044. SetFocus(hFinish);
  3045. #endif
  3046. }
  3047. break;
  3048. case PSM_SETTITLEA:
  3049. Prsht_OnSetTitle(ppd, wParam, lParam, StrDup_AtoW);
  3050. break;
  3051. case PSM_SETTITLE:
  3052. Prsht_OnSetTitle(ppd, wParam, lParam, StrDup);
  3053. break;
  3054. case PSM_SETHEADERTITLEA:
  3055. {
  3056. LPWSTR lpHeaderTitle = (lParam && HIWORD(lParam)) ?
  3057. ProduceWFromA(CP_ACP, (LPCSTR)lParam) : StrDupW((LPWSTR)lParam);
  3058. if (lpHeaderTitle)
  3059. _SetHeaderTitles(hDlg, ppd, (UINT)wParam, lpHeaderTitle, TRUE);
  3060. }
  3061. break;
  3062. case PSM_SETHEADERTITLE:
  3063. {
  3064. LPTSTR lpHeaderTitle = StrDup((LPCTSTR)lParam);
  3065. if (lpHeaderTitle)
  3066. _SetHeaderTitles(hDlg, ppd, (UINT)wParam, lpHeaderTitle, TRUE);
  3067. }
  3068. break;
  3069. case PSM_SETHEADERSUBTITLEA:
  3070. {
  3071. LPWSTR lpHeaderSubTitle = (lParam && HIWORD(lParam)) ?
  3072. ProduceWFromA(CP_ACP, (LPCSTR)lParam) : StrDupW((LPWSTR)lParam);
  3073. if (lpHeaderSubTitle)
  3074. _SetHeaderTitles(hDlg, ppd, (UINT)wParam, lpHeaderSubTitle, FALSE);
  3075. }
  3076. break;
  3077. case PSM_SETHEADERSUBTITLE:
  3078. {
  3079. LPTSTR lpHeaderSubTitle = StrDup((LPCTSTR)lParam);
  3080. if (lpHeaderSubTitle)
  3081. _SetHeaderTitles(hDlg, ppd, (UINT)wParam, lpHeaderSubTitle, FALSE);
  3082. }
  3083. break;
  3084. case PSM_CHANGED:
  3085. PageInfoChange(ppd, (HWND)wParam);
  3086. break;
  3087. case PSM_RESTARTWINDOWS:
  3088. ppd->nRestart |= ID_PSRESTARTWINDOWS;
  3089. break;
  3090. case PSM_REBOOTSYSTEM:
  3091. ppd->nRestart |= ID_PSREBOOTSYSTEM;
  3092. break;
  3093. case PSM_DISABLEAPPLY:
  3094. // the page is asking us to gray the "Apply" button and not let
  3095. // anyone else re-enable it
  3096. if (ppd->fAllowApply)
  3097. {
  3098. ppd->fAllowApply = FALSE;
  3099. EnableWindow(GetDlgItem(ppd->hDlg, IDD_APPLYNOW), FALSE);
  3100. }
  3101. break;
  3102. case PSM_ENABLEAPPLY:
  3103. // the page is asking us to allow the the "Apply" button to be
  3104. // once again enabled
  3105. if (!ppd->fAllowApply)
  3106. ppd->fAllowApply = TRUE;
  3107. // REARCHITECT - raymondc - shouldn't we call EnableWindow?
  3108. break;
  3109. case PSM_CANCELTOCLOSE:
  3110. if (!(ppd->fFlags & PD_CANCELTOCLOSE))
  3111. {
  3112. TCHAR szClose[20];
  3113. ppd->fFlags |= PD_CANCELTOCLOSE;
  3114. LocalizedLoadString(IDS_CLOSE, szClose, ARRAYSIZE(szClose));
  3115. SetDlgItemText(hDlg, IDOK, szClose);
  3116. EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
  3117. }
  3118. break;
  3119. case PSM_SETCURSEL:
  3120. lres = PageSetSelection(ppd, (int)wParam, (HPROPSHEETPAGE)lParam, 1);
  3121. goto ReturnLres;
  3122. case PSM_SETCURSELID:
  3123. {
  3124. int iPageIndex;
  3125. iPageIndex = FindPageIndex(ppd, ppd->nCurItem, (DWORD)lParam, 1);
  3126. if (iPageIndex == -1)
  3127. lres = 0;
  3128. else
  3129. lres = PageSetSelection(ppd, iPageIndex, NULL, 1);
  3130. goto ReturnLres;
  3131. }
  3132. break;
  3133. case PSM_REMOVEPAGE:
  3134. RemovePropPage(ppd, (int)wParam, (HPROPSHEETPAGE)lParam);
  3135. break;
  3136. case PSM_ADDPAGE:
  3137. lres = AddPropPage(ppd,(HPROPSHEETPAGE)lParam);
  3138. goto ReturnLres;
  3139. case PSM_INSERTPAGE:
  3140. lres = InsertPropPage(ppd, (HPROPSHEETPAGE)wParam, (HPROPSHEETPAGE)lParam);
  3141. goto ReturnLres;
  3142. case PSM_QUERYSIBLINGS:
  3143. lres = QuerySiblings(ppd, wParam, lParam);
  3144. goto ReturnLres;
  3145. case PSM_UNCHANGED:
  3146. PageInfoUnChange(ppd, (HWND)wParam);
  3147. break;
  3148. case PSM_APPLY:
  3149. // a page is asking us to simulate an "Apply Now".
  3150. // let the page know if we're successful
  3151. lres = ButtonPushed(ppd, IDD_APPLYNOW);
  3152. goto ReturnLres;
  3153. case PSM_GETTABCONTROL:
  3154. lres = (LRESULT)ppd->hwndTabs;
  3155. goto ReturnLres;
  3156. case PSM_GETCURRENTPAGEHWND:
  3157. lres = (LRESULT)ppd->hwndCurPage;
  3158. goto ReturnLres;
  3159. case PSM_PRESSBUTTON:
  3160. if (wParam <= PSBTN_MAX)
  3161. {
  3162. const static int IndexToID[] = {IDD_BACK, IDD_NEXT, IDD_FINISH, IDOK,
  3163. IDD_APPLYNOW, IDCANCEL, IDHELP};
  3164. Prsht_OnCommand(ppd, IndexToID[wParam], NULL, 0);
  3165. }
  3166. break;
  3167. case PSM_ISDIALOGMESSAGE:
  3168. // returning TRUE means we handled it, do a continue
  3169. // FALSE do standard translate/dispatch
  3170. lres = Prop_IsDialogMessage(ppd, (LPMSG32)lParam);
  3171. goto ReturnLres;
  3172. case PSM_HWNDTOINDEX:
  3173. lres = FindItem(ppd->hwndTabs, (HWND)wParam, NULL);
  3174. goto ReturnLres;
  3175. case PSM_INDEXTOHWND:
  3176. if ((UINT)wParam < ppd->psh.nPages)
  3177. lres = (LRESULT)_Ppd_GetPage(ppd, (int)wParam);
  3178. else
  3179. lres = 0;
  3180. goto ReturnLres;
  3181. case PSM_PAGETOINDEX:
  3182. lres = FindPageIndexByHpage(ppd, (HPROPSHEETPAGE)lParam);
  3183. goto ReturnLres;
  3184. case PSM_INDEXTOPAGE:
  3185. if ((UINT)wParam < ppd->psh.nPages)
  3186. lres = (LRESULT)GETHPAGE(ppd, wParam);
  3187. else
  3188. lres = 0;
  3189. goto ReturnLres;
  3190. case PSM_INDEXTOID:
  3191. if ((UINT)wParam < ppd->psh.nPages)
  3192. {
  3193. lres = (LRESULT)GETPPSP(ppd, wParam)->P_pszTemplate;
  3194. // Need to be careful -- return a value only if pszTemplate
  3195. // is an ID. Don't return out our internal pointers!
  3196. if (!IS_INTRESOURCE(lres))
  3197. lres = 0;
  3198. }
  3199. else
  3200. lres = 0;
  3201. goto ReturnLres;
  3202. case PSM_IDTOINDEX:
  3203. lres = FindPageIndex(ppd, ppd->nCurItem, (DWORD)lParam, 0);
  3204. goto ReturnLres;
  3205. case PSM_GETRESULT:
  3206. // This is valid only after the property sheet is gone
  3207. if (ppd->hwndCurPage)
  3208. {
  3209. lres = -1; // you shouldn't be calling me yet
  3210. } else {
  3211. lres = ppd->nReturn;
  3212. if (lres > 0 && ppd->nRestart)
  3213. lres = ppd->nRestart;
  3214. }
  3215. goto ReturnLres;
  3216. break;
  3217. case PSM_RECALCPAGESIZES:
  3218. lres = Prsht_RecalcPageSizes(ppd);
  3219. goto ReturnLres;
  3220. // these should be relayed to all created dialogs
  3221. case WM_WININICHANGE:
  3222. case WM_SYSCOLORCHANGE:
  3223. case WM_DISPLAYCHANGE:
  3224. {
  3225. int nItem, nItems = TabCtrl_GetItemCount(ppd->hwndTabs);
  3226. for (nItem = 0; nItem < nItems; ++nItem)
  3227. {
  3228. hwndT = _Ppd_GetPage(ppd, nItem);
  3229. if (hwndT)
  3230. SendMessage(hwndT, uMessage, wParam, lParam);
  3231. }
  3232. SendMessage(ppd->hwndTabs, uMessage, wParam, lParam);
  3233. }
  3234. break;
  3235. //
  3236. // send toplevel messages to the current page and tab control
  3237. //
  3238. case WM_PALETTECHANGED:
  3239. //
  3240. // If this is our window we need to avoid selecting and realizing
  3241. // because doing so would cause an infinite loop between WM_QUERYNEWPALETTE
  3242. // and WM_PALETTECHANGED.
  3243. //
  3244. if((HWND)wParam == hDlg) {
  3245. return(FALSE);
  3246. }
  3247. //
  3248. // FALL THROUGH
  3249. //
  3250. case WM_QUERYNEWPALETTE:
  3251. // This is needed when another window which has different palette clips
  3252. // us
  3253. if ((ppd->psh.dwFlags & PSH_WIZARD97) &&
  3254. (ppd->psh.dwFlags & PSH_WATERMARK) &&
  3255. (ppd->psh.hplWatermark))
  3256. HandlePaletteChange(ppd, uMessage, hDlg);
  3257. //
  3258. // FALL THROUGH
  3259. //
  3260. case WM_ENABLE:
  3261. case WM_DEVICECHANGE:
  3262. case WM_QUERYENDSESSION:
  3263. case WM_ENDSESSION:
  3264. if (ppd->hwndTabs)
  3265. SendMessage(ppd->hwndTabs, uMessage, wParam, lParam);
  3266. //
  3267. // FALL THROUGH
  3268. //
  3269. case WM_ACTIVATEAPP:
  3270. case WM_ACTIVATE:
  3271. {
  3272. hwndT = _Ppd_GetPage(ppd, ppd->nCurItem);
  3273. if (hwndT && IsWindow(hwndT))
  3274. {
  3275. //
  3276. // By doing this, we are "handling" the message. Therefore
  3277. // we must set the dialog return value to whatever the child
  3278. // wanted.
  3279. //
  3280. lres = SendMessage(hwndT, uMessage, wParam, lParam);
  3281. goto ReturnLres;
  3282. }
  3283. }
  3284. if ((uMessage == WM_PALETTECHANGED) || (uMessage == WM_QUERYNEWPALETTE))
  3285. return TRUE;
  3286. else
  3287. return FALSE;
  3288. case WM_CONTEXTMENU:
  3289. // ppd->hwndTabs is handled by aPropHelpIDs to work around a USER bug.
  3290. // See aPropHelpIDs for gory details.
  3291. if ((ppd->hwndCurPage != (HWND)wParam) && (!IS_WIZARD(ppd)))
  3292. WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(LPVOID) aPropHelpIDs);
  3293. break;
  3294. case WM_HELP:
  3295. hwndT = (HWND)((LPHELPINFO)lParam)->hItemHandle;
  3296. if ((GetParent(hwndT) == hDlg) && (hwndT != ppd->hwndTabs))
  3297. WinHelp(hwndT, NULL, HELP_WM_HELP, (ULONG_PTR)(LPVOID) aPropHelpIDs);
  3298. break;
  3299. default:
  3300. return FALSE;
  3301. }
  3302. return TRUE;
  3303. ReturnLres:
  3304. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, lres);
  3305. return TRUE;
  3306. }
  3307. //
  3308. // Draw the background for wizard pages.
  3309. //
  3310. BOOL Prsht_EraseWizBkgnd(LPPROPDATA ppd, HDC hdc)
  3311. {
  3312. RECT rc;
  3313. BOOL fPainted = FALSE;
  3314. GetClientRect(ppd->hDlg, &rc);
  3315. if (ppd->psh.dwFlags & PSH_WIZARD97IE4)
  3316. {
  3317. if (ppd->hbrWatermark)
  3318. {
  3319. PaintWithPaletteBrush(hdc, &rc, ppd->hplWatermark, ppd->hbrWatermark);
  3320. fPainted = TRUE;
  3321. }
  3322. }
  3323. else // PSH_WIZARD97IE5
  3324. {
  3325. if (ppd->hbmWatermark)
  3326. {
  3327. // Right-hand side gets g_hbrWindow.
  3328. rc.left = BITMAP_WIDTH;
  3329. FillRect(hdc, &rc, g_hbrWindow);
  3330. // Left-hand side gets watermark in top portion with autofill...
  3331. rc.right = rc.left;
  3332. rc.left = 0;
  3333. PaintWithPaletteBitmap(hdc, &rc, ppd->hplWatermark, ppd->hbmWatermark);
  3334. fPainted = TRUE;
  3335. }
  3336. }
  3337. return fPainted;
  3338. }
  3339. LRESULT CALLBACK WizardWndProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData)
  3340. {
  3341. LPPROPDATA ppd = (LPPROPDATA)dwRefData;
  3342. switch (uMessage)
  3343. {
  3344. case WM_ERASEBKGND:
  3345. if (Prsht_EraseWizBkgnd(ppd, (HDC)wParam))
  3346. return TRUE;
  3347. break;
  3348. // Only PSH_WIZARD97IE4 cares about these messages
  3349. case WM_CTLCOLOREDIT:
  3350. case WM_CTLCOLORDLG:
  3351. if (!(ppd->psh.dwFlags & PSH_WIZARD97IE4))
  3352. break;
  3353. // fall through
  3354. case WM_CTLCOLOR:
  3355. case WM_CTLCOLORMSGBOX:
  3356. case WM_CTLCOLORLISTBOX:
  3357. case WM_CTLCOLORBTN:
  3358. case WM_CTLCOLORSCROLLBAR:
  3359. case WM_CTLCOLORSTATIC:
  3360. if (ppd->psh.dwFlags & PSH_WIZARD97IE4)
  3361. {
  3362. if (ppd->hbrWatermark) {
  3363. POINT pt;
  3364. // Bug-for-bug compatibility: TRANSPARENT messes up edit
  3365. // controls when they scroll, but that's what IE4 did.
  3366. SetBkMode((HDC)wParam, TRANSPARENT);
  3367. if (ppd->hplWatermark)
  3368. {
  3369. SelectPalette((HDC)wParam, ppd->hplWatermark, FALSE);
  3370. RealizePalette((HDC)wParam);
  3371. }
  3372. UnrealizeObject(ppd->hbrWatermark);
  3373. GetDCOrgEx((HDC)wParam, &pt);
  3374. // Bug-for-bug compatibility: We shouldn't use GetParent
  3375. // because the notification might be forwarded up from an
  3376. // embedded dialog child, but that's what IE4 did.
  3377. ScreenToClient(GetParent((HWND)lParam), &pt);
  3378. SetBrushOrgEx((HDC)wParam, -pt.x, -pt.y, NULL);
  3379. return (LRESULT)(HBRUSH)ppd->hbrWatermark;
  3380. }
  3381. }
  3382. else // PSH_WIZARD97IE5
  3383. {
  3384. if (ppd->hbmWatermark)
  3385. {
  3386. LRESULT lRet = DefWindowProc(hDlg, uMessage, wParam, lParam);
  3387. if (lRet == DefSubclassProc(hDlg, uMessage, wParam, lParam))
  3388. {
  3389. SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
  3390. SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
  3391. return (LRESULT)g_hbrWindow;
  3392. }
  3393. else
  3394. return lRet;
  3395. }
  3396. }
  3397. break;
  3398. case WM_PALETTECHANGED:
  3399. if((HWND)wParam == hDlg)
  3400. return(FALSE);
  3401. case WM_QUERYNEWPALETTE:
  3402. HandlePaletteChange(ppd, uMessage, hDlg);
  3403. return TRUE;
  3404. case WM_DESTROY:
  3405. // Clean up subclass
  3406. RemoveWindowSubclass(hDlg, WizardWndProc, 0);
  3407. break;
  3408. default:
  3409. break;
  3410. }
  3411. return DefSubclassProc(hDlg, uMessage, wParam, lParam);
  3412. }
  3413. //
  3414. // EnumResLangProc
  3415. //
  3416. // purpose: a callback function for EnumResourceLanguages().
  3417. // look into the type passed in and if it is RT_DIALOG
  3418. // copy the lang of the first resource to our buffer
  3419. // this also counts # of lang if more than one of them
  3420. // are passed in
  3421. //
  3422. //
  3423. typedef struct {
  3424. WORD wLang;
  3425. BOOL fFoundLang;
  3426. LPCTSTR lpszType;
  3427. } ENUMLANGDATA;
  3428. BOOL CALLBACK EnumResLangProc(HINSTANCE hinst, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIdLang, LPARAM lparam)
  3429. {
  3430. ENUMLANGDATA *pel = (ENUMLANGDATA *)lparam;
  3431. BOOL fContinue = TRUE;
  3432. ASSERT(pel);
  3433. if (lpszType == pel->lpszType)
  3434. {
  3435. // When comctl's been initialized with a particular MUI language,
  3436. // we pass in the langid to GetPageLanguage(), then it's given to this proc.
  3437. // we want to look for a template that matches to the langid,
  3438. // and if it's not found, we have to use the first instance of templates.
  3439. //
  3440. if (pel->wLang == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
  3441. || (pel->wLang == wIdLang))
  3442. {
  3443. pel->wLang = wIdLang;
  3444. pel->fFoundLang = TRUE;
  3445. fContinue = FALSE;
  3446. }
  3447. }
  3448. return fContinue; // continue until we get langs...
  3449. }
  3450. // GetPageLanguage
  3451. //
  3452. // purpose: tries to retrieve language information out of
  3453. // given page's dialog template. We get the first language
  3454. // in which the template is localized in.
  3455. // currently doesn't support PSP_DLGINDIRECT case
  3456. //
  3457. // we luck out with browselc since there's only one lang per resid,
  3458. // we should cache the langid we loaded up front and pull it out here.
  3459. //
  3460. HRESULT GetPageLanguage(PISP pisp, WORD *pwLang)
  3461. {
  3462. if (pisp && pwLang)
  3463. {
  3464. if (pisp->_psp.dwFlags & PSP_DLGINDIRECT)
  3465. {
  3466. // try something other than dialog
  3467. return E_FAIL; // not supported yet.
  3468. }
  3469. else
  3470. {
  3471. ENUMLANGDATA el;
  3472. // the caller passes-in the langid with which we're initialized
  3473. //
  3474. el.wLang = *pwLang;
  3475. el.fFoundLang = FALSE;
  3476. el.lpszType = RT_DIALOG;
  3477. // check with the dialog template specified
  3478. EnumResourceLanguages(pisp->_psp.hInstance, RT_DIALOG, pisp->_psp.P_pszTemplate, EnumResLangProc, (LPARAM)&el);
  3479. if (!el.fFoundLang)
  3480. {
  3481. // we couldn't find a matching lang in the given page's resource
  3482. // so we'll take the first one
  3483. el.wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  3484. // it doesn't matter if this fails, because we'll then end up with
  3485. // the neutral langid, which is the best guess here after failing
  3486. // to get any page lang.
  3487. //
  3488. EnumResourceLanguages(pisp->_psp.hInstance, RT_DIALOG,
  3489. pisp->_psp.P_pszTemplate, EnumResLangProc, (LPARAM)&el);
  3490. }
  3491. *pwLang = el.wLang;
  3492. }
  3493. return S_OK;
  3494. }
  3495. return E_FAIL;
  3496. }
  3497. //
  3498. // FindResourceExRetry
  3499. //
  3500. // Just like FindResourceEx, except that if we can't find the resource,
  3501. // we try again with MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL).
  3502. //
  3503. HRSRC FindResourceExRetry(HMODULE hmod, LPCTSTR lpType, LPCTSTR lpName, WORD wLang)
  3504. {
  3505. HRSRC hrsrc = FindResourceEx(hmod, lpType, lpName, wLang);
  3506. // if failed because we couldn't find the resouce in requested lang
  3507. // and requested lang wasn't neutral, then try neutral.
  3508. if (!hrsrc && wLang != MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))
  3509. {
  3510. wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  3511. hrsrc = FindResourceEx(hmod, lpType, lpName, wLang);
  3512. }
  3513. return hrsrc;
  3514. }
  3515. WORD GetShellResourceLangID(void);
  3516. // NT5_GetUserDefaultUILanguage
  3517. //
  3518. // NT5 has a new function GetUserDefaultUILanguage which returns the
  3519. // language the user as selected for UI.
  3520. //
  3521. // If the function is not available (e.g., NT4), then use the
  3522. // shell resource language ID.
  3523. //
  3524. typedef LANGID (CALLBACK* GETUSERDEFAULTUILANGUAGE)(void);
  3525. GETUSERDEFAULTUILANGUAGE _GetUserDefaultUILanguage;
  3526. LANGID NT5_GetUserDefaultUILanguage(void)
  3527. {
  3528. if (_GetUserDefaultUILanguage == NULL)
  3529. {
  3530. HMODULE hmod = GetModuleHandle(TEXT("KERNEL32"));
  3531. //
  3532. // Must keep in a local to avoid thread races.
  3533. //
  3534. GETUSERDEFAULTUILANGUAGE pfn = NULL;
  3535. if (hmod)
  3536. pfn = (GETUSERDEFAULTUILANGUAGE)
  3537. GetProcAddress(hmod, "GetUserDefaultUILanguage");
  3538. //
  3539. // If function is not available, then use our fallback
  3540. //
  3541. if (pfn == NULL)
  3542. pfn = GetShellResourceLangID;
  3543. ASSERT(pfn != NULL);
  3544. _GetUserDefaultUILanguage = pfn;
  3545. }
  3546. return _GetUserDefaultUILanguage();
  3547. }
  3548. LCID CCGetSystemDefaultThreadLocale(LCID iLcidThreadOrig)
  3549. {
  3550. UINT uLangThread, uLangThreadOrig;
  3551. uLangThreadOrig = LANGIDFROMLCID(iLcidThreadOrig);
  3552. // uLangThread is the language we think we want to use
  3553. uLangThread = uLangThreadOrig;
  3554. if (staticIsOS(OS_NT4ORGREATER) && !staticIsOS(OS_WIN2000ORGREATER))
  3555. {
  3556. int iLcidUserDefault = GetUserDefaultLCID();
  3557. UINT uLangUD = LANGIDFROMLCID(iLcidUserDefault);
  3558. //
  3559. // If we are running on Enabled Arabic NT4, we should always
  3560. // display the US English resources (since the UI is English), however NT4
  3561. // Resource Loader will look for the current Thread Locale (which is Arabic).
  3562. // This is no problem in NT5 since the Resource Loader will check for
  3563. // the UI Language (newly introduced) when loading such resources. To
  3564. // fix this, we will change the thread locale to US English
  3565. // and restore it back to Arabic/Hebrew if we are running on an Enabled Arabic/Hebrew NT4.
  3566. // The check is done to make sure we are running within a Araic/Hebrew user locale
  3567. // and the thread locale is still Arabic/Hebrew (i.e. nobody tried to SetThreadLocale).
  3568. // [samera]
  3569. //
  3570. if( ((PRIMARYLANGID(uLangUD ) == LANG_ARABIC) &&
  3571. (PRIMARYLANGID(uLangThread) == LANG_ARABIC)) ||
  3572. ((PRIMARYLANGID(uLangUD ) == LANG_HEBREW) &&
  3573. (PRIMARYLANGID(uLangThread) == LANG_HEBREW)))
  3574. {
  3575. uLangThread = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
  3576. }
  3577. }
  3578. //
  3579. // Make locale match UI locale if not otherwise overridden.
  3580. //
  3581. if (uLangThread == uLangThreadOrig)
  3582. {
  3583. uLangThread = NT5_GetUserDefaultUILanguage();
  3584. }
  3585. //
  3586. // Now see if we actually changed the thread language.
  3587. //
  3588. if (uLangThread == uLangThreadOrig)
  3589. {
  3590. // No change, return the original locale, including sort stuff
  3591. return iLcidThreadOrig;
  3592. }
  3593. else
  3594. {
  3595. // It changed, return a generic sort order, since we don't use
  3596. // this information for sorting.
  3597. return MAKELCID(uLangThread, SORT_DEFAULT);
  3598. }
  3599. }
  3600. //
  3601. // GetAltFontLangId
  3602. //
  3603. // used to detect "MS UI Gothic" on Jpn localized non NT5 platforms
  3604. // the font is shipped with IE5 for the language but comctl can't
  3605. // always assume the font so we have a fake sublang id assigned to
  3606. // the secondary resource file for the language
  3607. //
  3608. int CALLBACK FontEnumProc(
  3609. ENUMLOGFONTEX *lpelfe,
  3610. NEWTEXTMETRICEX *lpntme,
  3611. int FontType,
  3612. LPARAM lParam
  3613. )
  3614. {
  3615. if (lParam)
  3616. {
  3617. *(BOOL *)lParam = TRUE;
  3618. }
  3619. return 0; // stop at the first callback
  3620. }
  3621. UINT GetDefaultCharsetFromLang(LANGID wLang)
  3622. {
  3623. TCHAR szData[6+1]; // 6 chars are max allowed for this lctype
  3624. UINT uiRet = DEFAULT_CHARSET;
  3625. // JPN hack here: GetLocaleInfo() DOES return > 0 for Jpn altfont langid,
  3626. // but doesn't get us any useful info. So for JPN, we ripout the SUBLANG
  3627. // portion of id. we can't do this for other langs since sublang can affect
  3628. // charset (ex. chinese)
  3629. //
  3630. if(PRIMARYLANGID(wLang) == LANG_JAPANESE)
  3631. wLang = MAKELANGID(PRIMARYLANGID(wLang), SUBLANG_NEUTRAL);
  3632. if (GetLocaleInfo(MAKELCID(wLang, SORT_DEFAULT),
  3633. LOCALE_IDEFAULTANSICODEPAGE,
  3634. szData, ARRAYSIZE(szData)) > 0)
  3635. {
  3636. UINT uiCp = StrToInt(szData);
  3637. CHARSETINFO csinfo;
  3638. if (TranslateCharsetInfo(IntToPtr_(DWORD *, uiCp), &csinfo, TCI_SRCCODEPAGE))
  3639. uiRet = csinfo.ciCharset;
  3640. }
  3641. return uiRet;
  3642. }
  3643. BOOL IsFontInstalled(LANGID wLang, LPCTSTR szFace)
  3644. {
  3645. BOOL fInstalled = FALSE;
  3646. HDC hdc;
  3647. LOGFONT lf = {0};
  3648. StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), szFace);
  3649. // retrieve charset from given language
  3650. lf.lfCharSet = (BYTE)GetDefaultCharsetFromLang(wLang);
  3651. // then see if we can enumrate the font
  3652. hdc = GetDC(NULL);
  3653. if (hdc)
  3654. {
  3655. EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROC)FontEnumProc, (LPARAM)&fInstalled, 0);
  3656. ReleaseDC(NULL, hdc);
  3657. }
  3658. return fInstalled;
  3659. }
  3660. LANGID GetAltFontLangId(LANGID wLang)
  3661. {
  3662. LPCTSTR pszTypeFace = NULL;
  3663. USHORT usAltSubLang = SUBLANG_NEUTRAL;
  3664. const static TCHAR s_szUIGothic[] = TEXT("MS UI Gothic");
  3665. static int iPrimaryFontInstalled = -1;
  3666. // most of the case we return the lang just as is
  3667. switch(PRIMARYLANGID(wLang))
  3668. {
  3669. case LANG_JAPANESE:
  3670. pszTypeFace = s_szUIGothic;
  3671. usAltSubLang = SUBLANG_JAPANESE_ALTFONT;
  3672. break;
  3673. // add code here to handle any other cases like Jpn
  3674. default:
  3675. return wLang;
  3676. }
  3677. // check existence of the font if we haven't
  3678. if (iPrimaryFontInstalled < 0 && pszTypeFace)
  3679. {
  3680. iPrimaryFontInstalled = IsFontInstalled(wLang, pszTypeFace);
  3681. }
  3682. // return secondary lang id if our alternative font *is* installed
  3683. if (iPrimaryFontInstalled == 1)
  3684. wLang = MAKELANGID(PRIMARYLANGID(wLang), usAltSubLang);
  3685. return wLang;
  3686. }
  3687. // GetShellResourceLangID
  3688. //
  3689. // On NT4, we want to match our ML resource to the one that OS is localized.
  3690. // this is to prevent general UI (buttons) from changing along with regional
  3691. // setting change.
  3692. // Win95 won't change system default locale, NT5 will load from matching satelite
  3693. // resource dll automatically so this won't be needed on these platforms.
  3694. // This function finds shell32.dll and gets the language in which the dll is
  3695. // localized, then cache the lcid so we won't have to detect it again.
  3696. //
  3697. WORD GetShellResourceLangID(void)
  3698. {
  3699. static WORD langRes = 0L;
  3700. // we do this only once
  3701. if (langRes == 0L)
  3702. {
  3703. HINSTANCE hinstShell;
  3704. ENUMLANGDATA el = {MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), FALSE, RT_DIALOG};
  3705. hinstShell = LoadLibrary(TEXT("shell32.dll"));
  3706. if (hinstShell)
  3707. {
  3708. EnumResourceLanguages(hinstShell, RT_DIALOG, MAKEINTRESOURCE(DLG_EXITWINDOWS), EnumResLangProc, (LPARAM)&el);
  3709. FreeLibrary(hinstShell);
  3710. }
  3711. if (PRIMARYLANGID(el.wLang) == LANG_CHINESE
  3712. || PRIMARYLANGID(el.wLang) == LANG_PORTUGUESE )
  3713. {
  3714. // these two languages need special handling
  3715. langRes = el.wLang;
  3716. }
  3717. else
  3718. {
  3719. // otherwise we use only primary langid.
  3720. langRes = MAKELANGID(PRIMARYLANGID(el.wLang), SUBLANG_NEUTRAL);
  3721. }
  3722. }
  3723. return langRes;
  3724. }
  3725. //
  3726. // CCGetProperThreadLocale
  3727. //
  3728. // This function computes its brains out and tries to decide
  3729. // which thread locale we should use for our UI components.
  3730. //
  3731. // Returns the desired locale.
  3732. //
  3733. // Adjustment - For Arabic / Hebrew - NT4 Only
  3734. //
  3735. // Converts the thread locale to US, so that neutral resources
  3736. // loaded by the thread will be the US-English one, if available.
  3737. // This is used when the locale is Arabic/Hebrew and the system is
  3738. // NT4 enabled ( There was no localized NT4), as a result we need
  3739. // always to see the English resources on NT4 Arabic/Hebrew.
  3740. // [samera]
  3741. //
  3742. // Adjustment - For all languages - NT4 Only
  3743. //
  3744. // Convert the thread locale to the shell locale if not otherwise
  3745. // altered by previous adjustments.
  3746. //
  3747. // Adjustment - For all languages - NT5 Only
  3748. //
  3749. // Always use the default UI language. If that fails, then use the
  3750. // shell locale.
  3751. //
  3752. // The last two adjustments are handled in a common function, because
  3753. // the NT5 fallback turns out to be equal to the NT4 algorithm.
  3754. //
  3755. LCID CCGetProperThreadLocale(OPTIONAL LCID *plcidPrev)
  3756. {
  3757. LANGID uLangAlt, uLangMUI;
  3758. LCID lcidRet, iLcidThreadOrig;
  3759. iLcidThreadOrig = GetThreadLocale();
  3760. if (plcidPrev)
  3761. *plcidPrev = iLcidThreadOrig;
  3762. uLangMUI = GetMUILanguage();
  3763. if ( uLangMUI == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))
  3764. {
  3765. // return adjusted system default locale if MUI isn't initialized
  3766. //
  3767. lcidRet = CCGetSystemDefaultThreadLocale(iLcidThreadOrig);
  3768. }
  3769. else
  3770. {
  3771. // our host has initialized us with prefered MUI language
  3772. //
  3773. lcidRet = MAKELCID(uLangMUI, SORT_DEFAULT);
  3774. }
  3775. uLangAlt = GetAltFontLangId(LANGIDFROMLCID(lcidRet));
  3776. if (uLangAlt != LANGIDFROMLCID(lcidRet))
  3777. {
  3778. // use secondary resource for the language
  3779. // if the platform *does* have the alternative font
  3780. lcidRet = MAKELCID(uLangAlt, SORTIDFROMLCID(lcidRet));
  3781. }
  3782. return lcidRet;
  3783. }
  3784. //
  3785. // CCLoadStringEx
  3786. //
  3787. // Just like LoadString, except you can specify the language, too.
  3788. //
  3789. // This is harder than you think, because NT5 changed the way strings
  3790. // are loaded. Quote:
  3791. //
  3792. // We changed the resource loader in NT5, to only load resources
  3793. // in the language of the thread locale, if the thread locale is
  3794. // different to the user locale. The reasoning behind this was
  3795. // the "random" loading of the language of the user locale in
  3796. // the UI. This breaks if you do a SetThreadLocale to the User
  3797. // Locale, because then the whole step is ignored and the
  3798. // InstallLanguage of the system is loaded.
  3799. //
  3800. // Therefore, we have to use FindResourceEx.
  3801. //
  3802. //
  3803. int CCLoadStringEx(UINT uID, LPWSTR lpBuffer, int nBufferMax, WORD wLang)
  3804. {
  3805. return CCLoadStringExInternal(HINST_THISDLL, uID, lpBuffer, nBufferMax, wLang);
  3806. }
  3807. int CCLoadStringExInternal(HINSTANCE hInst, UINT uID, LPWSTR lpBuffer, int nBufferMax, WORD wLang)
  3808. {
  3809. PWCHAR pwch;
  3810. HRSRC hrsrc;
  3811. int cwch = 0;
  3812. if (nBufferMax <= 0) return 0; // sanity check
  3813. /*
  3814. * String tables are broken up into "bundles" of 16 strings each.
  3815. */
  3816. hrsrc = FindResourceExRetry(hInst, RT_STRING,
  3817. (LPCTSTR)(LONG_PTR)(1 + (USHORT)uID / 16),
  3818. wLang);
  3819. if (hrsrc) {
  3820. pwch = (PWCHAR)LoadResource(hInst, hrsrc);
  3821. if (pwch) {
  3822. /*
  3823. * Now skip over the strings in the resource until we
  3824. * hit the one we want. Each entry is a counted string,
  3825. * just like Pascal.
  3826. */
  3827. for (uID %= 16; uID; uID--) {
  3828. pwch += *pwch + 1;
  3829. }
  3830. cwch = min(*pwch, nBufferMax - 1);
  3831. memcpy(lpBuffer, pwch+1, cwch * sizeof(WCHAR)); /* Copy the goo */
  3832. }
  3833. }
  3834. lpBuffer[cwch] = L'\0'; /* Terminate the string */
  3835. return cwch;
  3836. }
  3837. //
  3838. // LocalizedLoadString
  3839. //
  3840. // Loads a string from our resources, using the correct language.
  3841. //
  3842. int LocalizedLoadString(UINT uID, LPWSTR lpBuffer, int nBufferMax)
  3843. {
  3844. return CCLoadStringEx(uID, lpBuffer, nBufferMax,
  3845. LANGIDFROMLCID(CCGetProperThreadLocale(NULL)));
  3846. }
  3847. //
  3848. // Determine if the prop sheet frame should use the new
  3849. // "MS Shell Dlg 2" font. To do this, we examine each page's dlg template.
  3850. // If all pages have SHELLFONT enabled, then
  3851. // we want to use the new font.
  3852. //
  3853. BOOL ShouldUseMSShellDlg2Font(LPPROPDATA ppd)
  3854. {
  3855. UINT iPage;
  3856. PAGEINFOEX pi;
  3857. LANGID langidMUI;
  3858. if (!staticIsOS(OS_WIN2000ORGREATER))
  3859. return FALSE;
  3860. langidMUI = GetMUILanguage();
  3861. for (iPage = 0; iPage < ppd->psh.nPages; iPage++)
  3862. {
  3863. if (GetPageInfoEx(ppd, GETPISP(ppd, iPage), &pi, langidMUI, GPI_DIALOGEX))
  3864. {
  3865. if (!IsPageInfoSHELLFONT(&pi))
  3866. {
  3867. return FALSE;
  3868. }
  3869. }
  3870. }
  3871. return TRUE;
  3872. }
  3873. PSPT_OS Prsht_GetOS()
  3874. {
  3875. static PSPT_OS pspt_os = (PSPT_OS)-1;
  3876. int iIsOSBiDiEnabled = 0;
  3877. if (pspt_os != (PSPT_OS)-1)
  3878. {
  3879. return pspt_os;
  3880. }
  3881. iIsOSBiDiEnabled = GetSystemMetrics(SM_MIDEASTENABLED);
  3882. if (staticIsOS(OS_WIN2000ORGREATER))
  3883. {
  3884. pspt_os = PSPT_OS_WINNT5;
  3885. }
  3886. else if (iIsOSBiDiEnabled && staticIsOS(OS_NT4ORGREATER) && (!staticIsOS(OS_WIN2000ORGREATER)))
  3887. {
  3888. pspt_os = PSPT_OS_WINNT4_ENA;
  3889. }
  3890. else if (iIsOSBiDiEnabled && staticIsOS(OS_WIN95ORGREATER) && (!staticIsOS(OS_WIN98)))
  3891. {
  3892. pspt_os = PSPT_OS_WIN95_BIDI;
  3893. }
  3894. else if (iIsOSBiDiEnabled && staticIsOS(OS_WIN98))
  3895. {
  3896. pspt_os = PSPT_OS_WIN98_BIDI;
  3897. }
  3898. else
  3899. {
  3900. pspt_os = PSPT_OS_OTHER;
  3901. }
  3902. return pspt_os;
  3903. }
  3904. PSPT_OVERRIDE Prsht_GetOverrideState(LPPROPDATA ppd)
  3905. {
  3906. // if passed bad argument, assume no override
  3907. if(!ppd)
  3908. return PSPT_OVERRIDE_NOOVERRIDE;
  3909. if (ppd->psh.dwFlags & PSH_USEPAGELANG)
  3910. return PSPT_OVERRIDE_USEPAGELANG;
  3911. return PSPT_OVERRIDE_NOOVERRIDE;
  3912. }
  3913. PSPT_TYPE Prsht_GetType(LPPROPDATA ppd, WORD wLang)
  3914. {
  3915. PISP pisp = NULL;
  3916. // if passed bad argument, give it the english resources
  3917. if(!ppd)
  3918. return PSPT_TYPE_ENGLISH;
  3919. pisp = GETPISP(ppd, 0);
  3920. if(pisp)
  3921. {
  3922. PAGEINFOEX pi = {0};
  3923. if ((IS_PROCESS_RTL_MIRRORED()) ||
  3924. (GetPageInfoEx(ppd, pisp, &pi, wLang, GPI_BMIRROR) && pi.bMirrored))
  3925. return PSPT_TYPE_MIRRORED;
  3926. else
  3927. {
  3928. WORD wLang = LANGIDFROMLCID(CCGetProperThreadLocale(NULL));
  3929. GetPageLanguage(pisp,&wLang);
  3930. if((PRIMARYLANGID(wLang) == LANG_ARABIC) || (PRIMARYLANGID(wLang) == LANG_HEBREW))
  3931. return PSPT_TYPE_ENABLED;
  3932. }
  3933. }
  3934. return PSPT_TYPE_ENGLISH;
  3935. }
  3936. PSPT_ACTION Prsht_GetAction(PSPT_TYPE pspt_type, PSPT_OS pspt_os, PSPT_OVERRIDE pspt_override)
  3937. {
  3938. if ((pspt_type < 0) || (pspt_type >= PSPT_TYPE_MAX)
  3939. || (pspt_os < 0) || (pspt_os >= PSPT_OS_MAX)
  3940. || (pspt_override < 0) || (pspt_override >= PSPT_OVERRIDE_MAX))
  3941. return PSPT_ACTION_NOACTION;
  3942. return g_PSPT_Action[pspt_type][pspt_os][pspt_override];
  3943. }
  3944. void Prsht_PrepareTemplate(LPPROPDATA ppd, HINSTANCE hInst, HGLOBAL *phDlgTemplate, HRSRC *phResInfo,
  3945. LPCSTR lpName, HWND hWndOwner, LPWORD lpwLangID)
  3946. {
  3947. LPDLGTEMPLATE pDlgTemplate = NULL;
  3948. PSPT_ACTION pspt_action;
  3949. if (pDlgTemplate = (LPDLGTEMPLATE)LockResource(*phDlgTemplate))
  3950. {
  3951. // We save BiDi templates as DIALOG (not DIALOGEX)
  3952. // If we got an extended template then it is not ours
  3953. if (((LPDLGTEMPLATEEX)pDlgTemplate)->wSignature == 0xFFFF)
  3954. return;
  3955. // Cut it short to save time
  3956. //
  3957. if (!(pDlgTemplate->dwExtendedStyle & (RTL_MIRRORED_WINDOW | RTL_NOINHERITLAYOUT)))
  3958. return;
  3959. }
  3960. pspt_action = Prsht_GetAction(Prsht_GetType(ppd, *lpwLangID), Prsht_GetOS(),
  3961. Prsht_GetOverrideState(ppd));
  3962. switch(pspt_action)
  3963. {
  3964. case PSPT_ACTION_NOACTION:
  3965. return;
  3966. case PSPT_ACTION_NOMIRRORING:
  3967. {
  3968. if (pDlgTemplate)
  3969. {
  3970. EditBiDiDLGTemplate(pDlgTemplate, EBDT_NOMIRROR, NULL, 0);
  3971. }
  3972. }
  3973. break;
  3974. case PSPT_ACTION_FLIP:
  3975. {
  3976. if (pDlgTemplate)
  3977. {
  3978. EditBiDiDLGTemplate(pDlgTemplate, EBDT_NOMIRROR, NULL, 0);
  3979. EditBiDiDLGTemplate(pDlgTemplate, EBDT_FLIP, (PWORD)&wIgnoreIDs, ARRAYSIZE(wIgnoreIDs));
  3980. ppd->fFlipped = TRUE;
  3981. }
  3982. }
  3983. break;
  3984. case PSPT_ACTION_LOADENGLISH:
  3985. {
  3986. HGLOBAL hDlgTemplateTemp = NULL;
  3987. HRSRC hResInfoTemp;
  3988. //
  3989. //Try to load an English resource.
  3990. //
  3991. *lpwLangID = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
  3992. if ((hResInfoTemp = FindResourceExA( hInst, (LPCSTR)RT_DIALOG, lpName, *lpwLangID)))
  3993. {
  3994. hDlgTemplateTemp = LoadResource(hInst, hResInfoTemp);
  3995. }
  3996. if (hDlgTemplateTemp)
  3997. {
  3998. //
  3999. //And return it to the caller to use it.
  4000. // Since we loaeded a new template, we should copy it to a local memory
  4001. // in case there is a callback.
  4002. //
  4003. DWORD cbTemplate = SizeofResource(hInst, hResInfoTemp);
  4004. LPVOID pTemplateMod;
  4005. pTemplateMod = (LPVOID)LocalAlloc(LPTR, cbTemplate * 2);
  4006. if (pTemplateMod)
  4007. {
  4008. memmove(pTemplateMod, hDlgTemplateTemp, cbTemplate);
  4009. LocalFree(*phDlgTemplate);
  4010. *phResInfo = hResInfoTemp;
  4011. *phDlgTemplate = pTemplateMod;
  4012. }
  4013. }
  4014. }
  4015. break;
  4016. case PSPT_ACTION_WIN9XCOMPAT:
  4017. {
  4018. if (pDlgTemplate)
  4019. {
  4020. pDlgTemplate->style |= DS_BIDI_RTL;
  4021. }
  4022. }
  4023. }
  4024. }
  4025. INT_PTR _RealPropertySheet(LPPROPDATA ppd)
  4026. {
  4027. HWND hwndMain;
  4028. MSG32 msg32;
  4029. HWND hwndTopOwner;
  4030. int nReturn = -1;
  4031. HWND hwndOriginalFocus;
  4032. WORD wLang, wUserLang;
  4033. LCID iLcidThread=0L;
  4034. HRSRC hrsrc = 0;
  4035. LPVOID pTemplate, pTemplateMod;
  4036. LPTSTR lpDlgId;
  4037. if (ppd->psh.nPages == 0)
  4038. {
  4039. DebugMsg(DM_ERROR, TEXT("no pages for prop sheet"));
  4040. goto FreePpdAndReturn;
  4041. }
  4042. ppd->hwndCurPage = NULL;
  4043. ppd->nReturn = -1;
  4044. ppd->nRestart = 0;
  4045. hwndTopOwner = ppd->psh.hwndParent;
  4046. hwndOriginalFocus = GetFocus();
  4047. #ifdef DEBUG
  4048. if (GetAsyncKeyState(VK_CONTROL) < 0) {
  4049. ppd->psh.dwFlags |= PSH_WIZARDHASFINISH;
  4050. }
  4051. #endif
  4052. if (!(ppd->psh.dwFlags & PSH_MODELESS))
  4053. {
  4054. //
  4055. // Like dialog boxes, we only want to disable top level windows.
  4056. // NB The mail guys would like us to be more like a regular
  4057. // dialog box and disable the parent before putting up the sheet.
  4058. if (hwndTopOwner)
  4059. {
  4060. while (GetWindowLong(hwndTopOwner, GWL_STYLE) & WS_CHILD)
  4061. hwndTopOwner = GetParent(hwndTopOwner);
  4062. ASSERT(hwndTopOwner); // Should never get this!
  4063. if ((hwndTopOwner == GetDesktopWindow()) ||
  4064. (EnableWindow(hwndTopOwner, FALSE)))
  4065. {
  4066. //
  4067. // If the window was the desktop window, then don't disable
  4068. // it now and don't reenable it later.
  4069. // Also, if the window was already disabled, then don't
  4070. // enable it later.
  4071. //
  4072. hwndTopOwner = NULL;
  4073. }
  4074. }
  4075. }
  4076. //
  4077. // WARNING! WARNING! WARNING! WARNING!
  4078. //
  4079. // Before you mess with any language stuff, be aware that MFC loads
  4080. // resources directly out of comctl32.dll, so if you change the
  4081. // way we choose the proper resource, you may break MFC apps.
  4082. // See NT bug 302959.
  4083. //
  4084. // Support PSH_USEPAGELANG
  4085. //
  4086. // Presume we load our template based on thread lang id.
  4087. wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  4088. wUserLang= MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  4089. if (ppd->psh.dwFlags & PSH_USEPAGELANG)
  4090. {
  4091. // Get callers language version. We know we have at least one page
  4092. if (FAILED(GetPageLanguage(GETPISP(ppd, 0), &wLang)))
  4093. {
  4094. // failed to get langid out of caller's resource
  4095. // just pretend nothing happened.
  4096. wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  4097. }
  4098. wUserLang = wLang;
  4099. }
  4100. else
  4101. wLang = LANGIDFROMLCID(CCGetProperThreadLocale(NULL));
  4102. //
  4103. // The only thing we need the thread locale for is to locate the
  4104. // correct dialog template. We don't want it to affect page
  4105. // initialization or anything else like that, so get the template
  4106. // and quickly set the locale back before anyone notices.
  4107. //
  4108. // If we can't get the requested language, retry with the neutral
  4109. // language.
  4110. //
  4111. // We have seperate dialog templates for Win95 BiDi localized
  4112. // The code used to check to see if we are running on Win98 BiDi localized
  4113. // and load this template.
  4114. // We have a special case when running Office2000 with Arabic/Hebrew SKU on
  4115. // BiDi win95 Enabled where we need to load this template as well
  4116. if(Prsht_GetOS() == PSPT_OS_WIN95_BIDI)
  4117. {
  4118. lpDlgId = MAKEINTRESOURCE(IS_WIZARD(ppd) ? DLG_WIZARD95 : DLG_PROPSHEET95);
  4119. hrsrc = FindResourceEx(
  4120. HINST_THISDLL, RT_DIALOG,
  4121. lpDlgId,
  4122. wLang );
  4123. // we only have DLG_WIZARD95 and DLG_PROPSHEET95 in Arabic & Hebrew language
  4124. // if we got any other language we will fail
  4125. // In this case, let's use the normal templates
  4126. if(hrsrc)
  4127. {
  4128. ppd->fFlipped = TRUE;
  4129. }
  4130. else
  4131. {
  4132. lpDlgId = MAKEINTRESOURCE(IS_WIZARD(ppd) ? DLG_WIZARD : DLG_PROPSHEET);
  4133. hrsrc = FindResourceExRetry(
  4134. HINST_THISDLL, RT_DIALOG,
  4135. lpDlgId,
  4136. wLang );
  4137. }
  4138. }
  4139. else
  4140. {
  4141. lpDlgId = MAKEINTRESOURCE(IS_WIZARD(ppd) ? DLG_WIZARD : DLG_PROPSHEET);
  4142. hrsrc = FindResourceExRetry(
  4143. HINST_THISDLL, RT_DIALOG,
  4144. lpDlgId,
  4145. wLang );
  4146. }
  4147. // Setup for failure
  4148. hwndMain = NULL;
  4149. if (hrsrc &&
  4150. (pTemplate = (LPVOID)LoadResource(HINST_THISDLL, hrsrc)))
  4151. {
  4152. DWORD cbTemplate;
  4153. cbTemplate = SizeofResource(HINST_THISDLL, hrsrc);
  4154. pTemplateMod = (LPVOID)LocalAlloc(LPTR, cbTemplate * 2); //double it to give some play leeway
  4155. if (pTemplateMod)
  4156. {
  4157. hmemcpy(pTemplateMod, pTemplate, cbTemplate);
  4158. //Check the direction of this dialog and change it if it does not match the owner.
  4159. Prsht_PrepareTemplate(ppd, HINST_THISDLL, &pTemplateMod, (HRSRC *)&hrsrc,
  4160. (LPSTR)lpDlgId,ppd->psh.hwndParent, &wUserLang);
  4161. }
  4162. else
  4163. {
  4164. pTemplateMod = pTemplate; // no modifications
  4165. }
  4166. //
  4167. // Template editing and callbacks happen only if we were able
  4168. // to create a copy for modifying.
  4169. //
  4170. if (pTemplateMod != pTemplate)
  4171. {
  4172. if (ppd->psh.dwFlags & PSH_NOCONTEXTHELP)
  4173. {
  4174. if (((LPDLGTEMPLATEEX)pTemplateMod)->wSignature == 0xFFFF){
  4175. ((LPDLGTEMPLATEEX)pTemplateMod)->dwStyle &= ~DS_CONTEXTHELP;
  4176. } else {
  4177. ((LPDLGTEMPLATE)pTemplateMod)->style &= ~DS_CONTEXTHELP;
  4178. }
  4179. }
  4180. if (IS_WIZARD(ppd) &&
  4181. (ppd->psh.dwFlags & PSH_WIZARDCONTEXTHELP)) {
  4182. if (((LPDLGTEMPLATEEX)pTemplateMod)->wSignature == 0xFFFF){
  4183. ((LPDLGTEMPLATEEX)pTemplateMod)->dwStyle |= DS_CONTEXTHELP;
  4184. } else {
  4185. ((LPDLGTEMPLATE)pTemplateMod)->style |= DS_CONTEXTHELP;
  4186. }
  4187. }
  4188. // extra check for PSH_USEPAGELANG case
  4189. if (ppd->psh.pfnCallback)
  4190. {
  4191. #ifdef WX86
  4192. if (ppd->fFlags & PD_WX86)
  4193. Wx86Callback(ppd->psh.pfnCallback, NULL, PSCB_PRECREATE, (LPARAM)(LPVOID)pTemplateMod);
  4194. else
  4195. #endif
  4196. ppd->psh.pfnCallback(NULL, PSCB_PRECREATE, (LPARAM)(LPVOID)pTemplateMod);
  4197. }
  4198. }
  4199. if (pTemplateMod)
  4200. {
  4201. //
  4202. // For NT, we want to use MS Shell Dlg 2 font in the prop sheet if
  4203. // all of the pages in the sheet use MS Shell Dlg 2.
  4204. // To do this, we ensure the template is DIALOGEX and that the
  4205. // DS_SHELLFONT style bits (DS_SHELLFONT | DS_FIXEDSYS) are set.
  4206. //
  4207. if (ShouldUseMSShellDlg2Font(ppd))
  4208. {
  4209. if (((LPDLGTEMPLATEEX)pTemplateMod)->wSignature != 0xFFFF)
  4210. {
  4211. //
  4212. // Convert DLGTEMPLATE to DLGTEMPLATEEX.
  4213. //
  4214. LPVOID pTemplateCvtEx;
  4215. int iCharset = GetDefaultCharsetFromLang(wLang);
  4216. if (SUCCEEDED(CvtDlgToDlgEx(pTemplateMod, (LPDLGTEMPLATEEX *)&pTemplateCvtEx, iCharset)))
  4217. {
  4218. LocalFree(pTemplateMod);
  4219. pTemplateMod = pTemplateCvtEx;
  4220. } else {
  4221. // Unable to convert to ShellFont; oh well
  4222. goto NotShellFont;
  4223. }
  4224. }
  4225. //
  4226. // Set DS_SHELLFONT style bits so we get "MS Shell Dlg2" font.
  4227. //
  4228. ((LPDLGTEMPLATEEX)pTemplateMod)->dwStyle |= DS_SHELLFONT;
  4229. ppd->fFlags |= PD_SHELLFONT;
  4230. NotShellFont:;
  4231. }
  4232. // pTemplateMod is always unicode, even for the A function - no need to thunk
  4233. hwndMain = CreateDialogIndirectParam(HINST_THISDLL, pTemplateMod,
  4234. ppd->psh.hwndParent, PropSheetDlgProc, (LPARAM)(LPPROPDATA)ppd);
  4235. // WORK AROUND WOW/USER BUG: Even though InitPropSheetDlg sets
  4236. // ppd->hDlg, in the WOW scenario, the incoming hDlg is WRONG!
  4237. // The USER guys say "Tough. You have to work around it."
  4238. ppd->hDlg = hwndMain;
  4239. }
  4240. if (pTemplateMod != pTemplate)
  4241. LocalFree(pTemplateMod);
  4242. }
  4243. if (!hwndMain)
  4244. {
  4245. int iPage;
  4246. DebugMsg(DM_ERROR, TEXT("PropertySheet: unable to create main dialog"));
  4247. if (hwndTopOwner && !(ppd->psh.dwFlags & PSH_MODELESS))
  4248. EnableWindow(hwndTopOwner, TRUE);
  4249. // Release all page objects in REVERSE ORDER so we can have
  4250. // pages that are dependant on eachother based on the initial
  4251. // order of those pages
  4252. //
  4253. for (iPage = (int)ppd->psh.nPages - 1; iPage >= 0; iPage--)
  4254. {
  4255. ULONG_PTR dw = PropPageActivateContext(ppd, GETPISP(ppd, iPage));
  4256. DestroyPropertySheetPage(GETHPAGE(ppd, iPage));
  4257. PropPageDeactivateContext(dw);
  4258. }
  4259. goto FreePpdAndReturn;
  4260. }
  4261. if (ppd->psh.dwFlags & PSH_MODELESS)
  4262. return (INT_PTR)hwndMain;
  4263. while( ppd->hwndCurPage && GetMessage32(&msg32, NULL, 0, 0, TRUE) )
  4264. {
  4265. // if (PropSheet_IsDialogMessage(ppd->hDlg, (LPMSG)&msg32))
  4266. if (Prop_IsDialogMessage(ppd, &msg32))
  4267. continue;
  4268. TranslateMessage32(&msg32, TRUE);
  4269. DispatchMessage32(&msg32, TRUE);
  4270. }
  4271. if( ppd->hwndCurPage )
  4272. {
  4273. // GetMessage returned FALSE (WM_QUIT)
  4274. DebugMsg( DM_TRACE, TEXT("PropertySheet: bailing in response to WM_QUIT (and reposting quit)") );
  4275. ButtonPushed( ppd, IDCANCEL ); // nuke ourselves
  4276. PostQuitMessage( (int) msg32.wParam ); // repost quit for next enclosing loop
  4277. }
  4278. // don't let this get mangled during destroy processing
  4279. nReturn = ppd->nReturn ;
  4280. if (ppd->psh.hwndParent && (GetActiveWindow() == hwndMain)) {
  4281. DebugMsg(DM_TRACE, TEXT("Passing activation up"));
  4282. SetActiveWindow(ppd->psh.hwndParent);
  4283. }
  4284. if (hwndTopOwner)
  4285. EnableWindow(hwndTopOwner, TRUE);
  4286. if (IsWindow(hwndOriginalFocus)) {
  4287. SetFocus(hwndOriginalFocus);
  4288. }
  4289. DestroyWindow(hwndMain);
  4290. // do pickup any PSM_REBOOTSYSTEM or PSM_RESTARTWINDOWS sent during destroy
  4291. if ((nReturn > 0) && ppd->nRestart)
  4292. nReturn = ppd->nRestart;
  4293. FreePpdAndReturn:
  4294. LocalFree((HLOCAL)ppd);
  4295. return nReturn;
  4296. }
  4297. HPROPSHEETPAGE WINAPI CreateProxyPage(HPROPSHEETPAGE hpage16, HINSTANCE hinst16)
  4298. {
  4299. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  4300. return NULL;
  4301. }
  4302. // DestroyPropsheetPageArray
  4303. //
  4304. // Helper function used during error handling. It destroys the
  4305. // incoming property sheet pages.
  4306. void DestroyPropsheetPageArray(LPCPROPSHEETHEADER ppsh)
  4307. {
  4308. int iPage;
  4309. if (!(ppsh->dwFlags & PSH_PROPSHEETPAGE))
  4310. {
  4311. // Release all page objects in REVERSE ORDER so we can have
  4312. // pages that are dependant on eachother based on the initial
  4313. // order of those pages
  4314. for (iPage = (int)ppsh->nPages - 1; iPage >= 0; iPage--)
  4315. {
  4316. DestroyPropertySheetPage(ppsh->H_phpage[iPage]);
  4317. }
  4318. }
  4319. }
  4320. // PropertySheet API
  4321. //
  4322. // This function displays the property sheet described by ppsh.
  4323. //
  4324. // Since I don't expect anyone to ever check the return value
  4325. // (we certainly don't), we need to make sure any provided phpage array
  4326. // is always freed with DestroyPropertySheetPage, even if an error occurs.
  4327. //
  4328. //
  4329. // The fNeedShadow parameter means "The incoming LPCPROPSHEETHEADER is in the
  4330. // opposite character set from what you implement natively".
  4331. //
  4332. // If we are compiling UNICODE, then fNeedShadow is TRUE if the incoming
  4333. // LPCPROPSHEETHEADER is really an ANSI property sheet page.
  4334. //
  4335. // If we are compiling ANSI-only, then fNeedShadow is always FALSE because
  4336. // we don't support UNICODE in the ANSI-only version.
  4337. //
  4338. INT_PTR WINAPI _PropertySheet(LPCPROPSHEETHEADER ppsh, BOOL fNeedShadow)
  4339. {
  4340. PROPDATA *ppd;
  4341. int iPage;
  4342. //
  4343. // validate header
  4344. //
  4345. ASSERT(IsValidPROPSHEETHEADERSIZE(sizeof(PROPSHEETHEADER)));
  4346. if (!IsValidPROPSHEETHEADERSIZE(ppsh->dwSize))
  4347. {
  4348. DebugMsg( DM_ERROR, TEXT("PropertySheet: dwSize is not correct") );
  4349. goto invalid_call;
  4350. }
  4351. if (ppsh->dwFlags & ~PSH_ALL)
  4352. {
  4353. DebugMsg( DM_ERROR, TEXT("PropertySheet: invalid flags") );
  4354. goto invalid_call;
  4355. }
  4356. if (ppsh->nPages >= MAXPROPPAGES)
  4357. {
  4358. DebugMsg( DM_ERROR, TEXT("PropertySheet: too many pages ( use MAXPROPPAGES )") );
  4359. goto invalid_call;
  4360. }
  4361. ppd = (PROPDATA *)LocalAlloc(LPTR, sizeof(PROPDATA));
  4362. if (ppd == NULL)
  4363. {
  4364. DebugMsg(DM_ERROR, TEXT("failed to alloc property page data"));
  4365. invalid_call:
  4366. DestroyPropsheetPageArray(ppsh);
  4367. return -1;
  4368. }
  4369. // Initialize the flags.
  4370. ppd->fFlags = FALSE;
  4371. #ifdef WX86
  4372. //
  4373. // If Wx86 is calling, set the flag that thunks the callbacks.
  4374. //
  4375. if ( Wx86IsCallThunked() ) {
  4376. ppd->fFlags |= PD_WX86;
  4377. }
  4378. #endif
  4379. if (fNeedShadow)
  4380. ppd->fFlags |= PD_NEEDSHADOW;
  4381. // make a copy of the header so we can party on it
  4382. hmemcpy(&ppd->psh, ppsh, ppsh->dwSize);
  4383. // so we don't have to check later...
  4384. if (!(ppd->psh.dwFlags & PSH_USECALLBACK))
  4385. ppd->psh.pfnCallback = NULL;
  4386. // fix up the page pointer to point to our copy of the page array
  4387. ppd->psh.H_phpage = ppd->rghpage;
  4388. GetCurrentActCtx(&ppd->hActCtxInit);
  4389. if (ppd->psh.dwFlags & PSH_PROPSHEETPAGE)
  4390. {
  4391. // for lazy clients convert PROPSHEETPAGE structures into page handles
  4392. LPCPROPSHEETPAGE ppsp = ppsh->H_ppsp;
  4393. for (iPage = 0; iPage < (int)ppd->psh.nPages; iPage++)
  4394. {
  4395. ppd->psh.H_phpage[iPage] = _CreatePropertySheetPage(ppsp, fNeedShadow,
  4396. ppd->fFlags & PD_WX86);
  4397. if (!ppd->psh.H_phpage[iPage])
  4398. {
  4399. iPage--;
  4400. ppd->psh.nPages--;
  4401. }
  4402. ppsp = (LPCPROPSHEETPAGE)((LPBYTE)ppsp + ppsp->dwSize); // next PROPSHEETPAGE structure
  4403. }
  4404. }
  4405. else
  4406. {
  4407. // The UNICODE build needs to hack around Hijaak 95.
  4408. //
  4409. ppd->psh.nPages = 0;
  4410. for (iPage = 0; iPage < (int)ppsh->nPages; iPage++)
  4411. {
  4412. ppd->psh.H_phpage[ppd->psh.nPages] = _Hijaak95Hack(ppd, ppsh->H_phpage[iPage]);
  4413. if (ppd->psh.H_phpage[ppd->psh.nPages])
  4414. {
  4415. ppd->psh.nPages++;
  4416. }
  4417. }
  4418. }
  4419. //
  4420. // Everybody else assumes that the HPROPSHEETPAGEs have been
  4421. // internalized, so let's do that before anybody notices.
  4422. //
  4423. for (iPage = 0; iPage < (int)ppd->psh.nPages; iPage++)
  4424. {
  4425. SETPISP(ppd, iPage, InternalizeHPROPSHEETPAGE(ppd->psh.H_phpage[iPage]));
  4426. }
  4427. //
  4428. // Walk all pages to see if any have help and if so, set the PSH_HASHELP
  4429. // flag in the header.
  4430. //
  4431. if (!(ppd->psh.dwFlags & PSH_HASHELP))
  4432. {
  4433. for (iPage = 0; iPage < (int)ppd->psh.nPages; iPage++)
  4434. {
  4435. if (GETPPSP(ppd, iPage)->dwFlags & PSP_HASHELP)
  4436. {
  4437. ppd->psh.dwFlags |= PSH_HASHELP;
  4438. break;
  4439. }
  4440. }
  4441. }
  4442. return _RealPropertySheet(ppd);
  4443. }
  4444. INT_PTR WINAPI PropertySheetW(LPCPROPSHEETHEADERW ppsh)
  4445. {
  4446. return _PropertySheet(ppsh, FALSE);
  4447. }
  4448. INT_PTR WINAPI PropertySheetA(LPCPROPSHEETHEADERA ppsh)
  4449. {
  4450. PROPSHEETHEADERW pshW;
  4451. INT_PTR iResult;
  4452. //
  4453. // Most validation is done by _PropertySheet, but we need
  4454. // to validate the header size, or we won't survive the thunk.
  4455. //
  4456. if (!IsValidPROPSHEETHEADERSIZE(ppsh->dwSize))
  4457. {
  4458. DebugMsg( DM_ERROR, TEXT("PropertySheet: dwSize is not correct") );
  4459. goto Error;
  4460. }
  4461. if (!ThunkPropSheetHeaderAtoW(ppsh, &pshW))
  4462. goto Error;
  4463. iResult = _PropertySheet(&pshW, TRUE);
  4464. FreePropSheetHeaderW(&pshW);
  4465. return iResult;
  4466. Error:
  4467. DestroyPropsheetPageArray((LPCPROPSHEETHEADER)ppsh);
  4468. return -1;
  4469. }
  4470. //
  4471. // CopyPropertyPageStrings
  4472. //
  4473. // We have a PROPSHEETPAGE structure that contains pointers to strings.
  4474. // For each string, create a copy and smash the pointer-to-copy in the
  4475. // place where the original static pointer used to be.
  4476. //
  4477. // The method of copying varies depending on what kind of copy we want
  4478. // to make, so we use a callback procedure.
  4479. //
  4480. // UNICODE-to-UNICODE: StrDupW
  4481. // ANSI-to-UNICODE: StrDup_AtoW
  4482. // ANSI-to-ANSI: StrDupA
  4483. //
  4484. // On failure, all strings that did not get properly duplicated are set
  4485. // to NULL. You still have to call FreePropertyPageStrings to clear
  4486. // them out. Notice that when we fail to allocate, we merely make a note
  4487. // of the fact and continue onward. This ensures that all string fields
  4488. // are set to NULL if they could not be dup'd.
  4489. //
  4490. // ppsp - A pointer to either a PROPSHEETPAGEA or PROPSHEETPAGEW.
  4491. // The two structures are laid out identically, so it doesn't matter.
  4492. //
  4493. // pfnStrDup - function that will make the appropriate copy.
  4494. //
  4495. BOOL CopyPropertyPageStrings(LPPROPSHEETPAGE ppsp, STRDUPPROC pfnStrDup)
  4496. {
  4497. BOOL fSuccess = TRUE;
  4498. if (!(ppsp->dwFlags & PSP_DLGINDIRECT) && !IS_INTRESOURCE(ppsp->P_pszTemplate))
  4499. {
  4500. ppsp->P_pszTemplate = pfnStrDup(ppsp->P_pszTemplate);
  4501. if (!ppsp->P_pszTemplate)
  4502. fSuccess = FALSE;
  4503. }
  4504. if ((ppsp->dwFlags & PSP_USEICONID) && !IS_INTRESOURCE(ppsp->P_pszIcon))
  4505. {
  4506. ppsp->P_pszIcon = pfnStrDup(ppsp->P_pszIcon);
  4507. if (!ppsp->P_pszIcon)
  4508. fSuccess = FALSE;
  4509. }
  4510. if ((ppsp->dwFlags & PSP_USETITLE) && !IS_INTRESOURCE(ppsp->pszTitle))
  4511. {
  4512. ppsp->pszTitle = pfnStrDup(ppsp->pszTitle);
  4513. if (!ppsp->pszTitle)
  4514. fSuccess = FALSE;
  4515. }
  4516. if ((ppsp->dwFlags & PSP_USEHEADERTITLE) && !IS_INTRESOURCE(ppsp->pszHeaderTitle))
  4517. {
  4518. ppsp->pszHeaderTitle = pfnStrDup(ppsp->pszHeaderTitle);
  4519. if (!ppsp->pszHeaderTitle)
  4520. fSuccess = FALSE;
  4521. }
  4522. if ((ppsp->dwFlags & PSP_USEHEADERSUBTITLE) && !IS_INTRESOURCE(ppsp->pszHeaderSubTitle))
  4523. {
  4524. ppsp->pszHeaderSubTitle = pfnStrDup(ppsp->pszHeaderSubTitle);
  4525. if (!ppsp->pszHeaderSubTitle)
  4526. fSuccess = FALSE;
  4527. }
  4528. return fSuccess;
  4529. }
  4530. //
  4531. // FreePropertyPageStrings
  4532. //
  4533. // Free the strings that live inside a property sheet page structure.
  4534. //
  4535. // ppsp - A pointer to either a PROPSHEETPAGEA or PROPSHEETPAGEW.
  4536. // The two structures are laid out identically, so it doesn't matter.
  4537. //
  4538. void FreePropertyPageStrings(LPCPROPSHEETPAGE ppsp)
  4539. {
  4540. if (!(ppsp->dwFlags & PSP_DLGINDIRECT) && !IS_INTRESOURCE(ppsp->P_pszTemplate))
  4541. LocalFree((LPVOID)ppsp->P_pszTemplate);
  4542. if ((ppsp->dwFlags & PSP_USEICONID) && !IS_INTRESOURCE(ppsp->P_pszIcon))
  4543. LocalFree((LPVOID)ppsp->P_pszIcon);
  4544. if ((ppsp->dwFlags & PSP_USETITLE) && !IS_INTRESOURCE(ppsp->pszTitle))
  4545. LocalFree((LPVOID)ppsp->pszTitle);
  4546. if ((ppsp->dwFlags & PSP_USEHEADERTITLE) && !IS_INTRESOURCE(ppsp->pszHeaderTitle))
  4547. LocalFree((LPVOID)ppsp->pszHeaderTitle);
  4548. if ((ppsp->dwFlags & PSP_USEHEADERSUBTITLE) && !IS_INTRESOURCE(ppsp->pszHeaderSubTitle))
  4549. LocalFree((LPVOID)ppsp->pszHeaderSubTitle);
  4550. }
  4551. //*************************************************************
  4552. //
  4553. // ThunkPropSheetHeaderAtoW ()
  4554. //
  4555. // Purpose: Thunks the Ansi version of PROPSHEETHEADER to
  4556. // Unicode.
  4557. //
  4558. // Note that the H_phpage / H_ppsp field is not thunked.
  4559. // We'll deal with that separately.
  4560. //
  4561. //*************************************************************
  4562. BOOL ThunkPropSheetHeaderAtoW (LPCPROPSHEETHEADERA ppshA,
  4563. LPPROPSHEETHEADERW ppsh)
  4564. {
  4565. //
  4566. // Deciding whether an item should be freed or not is tricky, so we
  4567. // keep a private array of all the pointers we've allocated, so we
  4568. // know what to free when we fail.
  4569. //
  4570. LPTSTR Alloced[5] = { 0 };
  4571. ASSERT(IsValidPROPSHEETHEADERSIZE(ppshA->dwSize));
  4572. hmemcpy(ppsh, ppshA, ppshA->dwSize);
  4573. ppsh->dwFlags |= PSH_THUNKED;
  4574. if ((ppsh->dwFlags & PSH_USEICONID) && !IS_INTRESOURCE(ppsh->H_pszIcon))
  4575. {
  4576. ppsh->H_pszIcon = Alloced[0] = StrDup_AtoW(ppsh->H_pszIcon);
  4577. if (!ppsh->H_pszIcon)
  4578. goto ExitIcon;
  4579. }
  4580. if (!IS_WIZARDPSH(*ppsh) && !IS_INTRESOURCE(ppsh->pszCaption))
  4581. {
  4582. ppsh->pszCaption = Alloced[1] = StrDup_AtoW(ppsh->pszCaption);
  4583. if (!ppsh->pszCaption)
  4584. goto ExitCaption;
  4585. }
  4586. if ((ppsh->dwFlags & PSH_USEPSTARTPAGE) && !IS_INTRESOURCE(ppsh->H_pStartPage))
  4587. {
  4588. ppsh->H_pStartPage = Alloced[2] = StrDup_AtoW(ppsh->H_pStartPage);
  4589. if (!ppsh->H_pStartPage)
  4590. goto ExitStartPage;
  4591. }
  4592. if (ppsh->dwFlags & PSH_WIZARD97)
  4593. {
  4594. if ((ppsh->dwFlags & PSH_WATERMARK) &&
  4595. !(ppsh->dwFlags & PSH_USEHBMWATERMARK) &&
  4596. !IS_INTRESOURCE(ppsh->H_pszbmWatermark))
  4597. {
  4598. ppsh->H_pszbmWatermark = Alloced[3] = StrDup_AtoW(ppsh->H_pszbmWatermark);
  4599. if (!ppsh->H_pszbmWatermark)
  4600. goto ExitWatermark;
  4601. }
  4602. if ((ppsh->dwFlags & PSH_HEADER) &&
  4603. !(ppsh->dwFlags & PSH_USEHBMHEADER) &&
  4604. !IS_INTRESOURCE(ppsh->H_pszbmHeader))
  4605. {
  4606. ppsh->H_pszbmHeader = Alloced[4] = StrDup_AtoW(ppsh->H_pszbmHeader);
  4607. if (!ppsh->H_pszbmHeader)
  4608. goto ExitHeader;
  4609. }
  4610. }
  4611. return TRUE;
  4612. ExitHeader:
  4613. if (Alloced[3]) LocalFree(Alloced[3]);
  4614. ExitWatermark:
  4615. if (Alloced[2]) LocalFree(Alloced[2]);
  4616. ExitStartPage:
  4617. if (Alloced[1]) LocalFree(Alloced[1]);
  4618. ExitCaption:
  4619. if (Alloced[0]) LocalFree(Alloced[0]);
  4620. ExitIcon:
  4621. return FALSE;
  4622. }
  4623. void FreePropSheetHeaderW(LPPROPSHEETHEADERW ppsh)
  4624. {
  4625. if ((ppsh->dwFlags & PSH_USEICONID) && !IS_INTRESOURCE(ppsh->H_pszIcon))
  4626. LocalFree((LPVOID)ppsh->H_pszIcon);
  4627. if (!IS_WIZARDPSH(*ppsh) && !IS_INTRESOURCE(ppsh->pszCaption))
  4628. LocalFree((LPVOID)ppsh->pszCaption);
  4629. if ((ppsh->dwFlags & PSH_USEPSTARTPAGE) && !IS_INTRESOURCE(ppsh->H_pStartPage))
  4630. LocalFree((LPVOID)ppsh->H_pStartPage);
  4631. if (ppsh->dwFlags & PSH_WIZARD97)
  4632. {
  4633. if ((ppsh->dwFlags & PSH_WATERMARK) &&
  4634. !(ppsh->dwFlags & PSH_USEHBMWATERMARK) &&
  4635. !IS_INTRESOURCE(ppsh->H_pszbmWatermark))
  4636. LocalFree((LPVOID)ppsh->H_pszbmWatermark);
  4637. if ((ppsh->dwFlags & PSH_HEADER) &&
  4638. !(ppsh->dwFlags & PSH_USEHBMHEADER) &&
  4639. !IS_INTRESOURCE(ppsh->H_pszbmHeader))
  4640. LocalFree((LPVOID)ppsh->H_pszbmHeader);
  4641. }
  4642. }