Source code of Windows XP (NT5)
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.

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