Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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