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

5370 lines
172 KiB

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