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.

955 lines
32 KiB

  1. /*++
  2. Copyright (c) 2000-2001, Microsoft Corporation All rights reserved.
  3. Module Name:
  4. hook.c
  5. Abstract:
  6. This file contains functions that wrap various hook procedures,
  7. and the helper functions that support their efforts.
  8. Hooks found in this file:
  9. WindowProc
  10. DialogProc
  11. CBTProc
  12. EnumChildToTagProc
  13. comdlg32.dll hooks:
  14. FRHookProcFind
  15. FRHookProcReplace
  16. OFNHookProc
  17. OFNHookProcSave
  18. PagePaintHook
  19. CCHookProc
  20. CFHookProc
  21. PageSetupHook
  22. PrintHookProc
  23. SetupHookProc
  24. OFNHookProcOldStyle
  25. OFNHookProcOldStyleSave
  26. Helper functions:
  27. FRHelper
  28. UpdateTextAndFlags
  29. NotifyFindReplaceParent
  30. OFNHookHelper
  31. OFNotifyHelper
  32. GenericHookHelper
  33. RemoveFontPropIfPresent
  34. SetNewFileOpenProp
  35. SetFontProp
  36. Externally available helper functions:
  37. IsFontDialog
  38. IsNewFileOpenDialog
  39. IsCaptureWindow
  40. SetCaptureWindowProp
  41. Revision History:
  42. 27 Jan 2001 v-michka Created.
  43. --*/
  44. #include "precomp.h"
  45. // Stolen from commdlg.h
  46. #define CCHCLOSE 9
  47. #define iszClose 0x040d // "Close" text for find/replace
  48. static WCHAR m_szClose [CCHCLOSE];
  49. // So we can keep track of font dialogs
  50. const char m_szComdlgClass[] = "GodotComdlgClass";
  51. ATOM m_aComdlgClass;
  52. #define CHOOSEFONT_DIALOG (HANDLE)1
  53. #define NEWFILEOPEN_DIALOG (HANDLE)2
  54. #define CAPTURE_WINDOW (HANDLE)3 // Not really a common dialog, but close enough
  55. // forward declares we will need -- we must have Unicode text, so
  56. // why write new wrappers when we have some lying around already?
  57. UINT __stdcall GodotGetDlgItemTextW(HWND hDlg, int nIDDlgItem, LPWSTR lpString, int nMaxCount);
  58. int __stdcall GodotLoadStringW(HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer, int nBufferMax);
  59. BOOL __stdcall GodotSetWindowTextW(HWND hWnd, LPCWSTR lpString);
  60. LRESULT __stdcall GodotSendMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
  61. // our own forward declares
  62. BOOL CALLBACK EnumChildToTagProc(HWND hwnd, LPARAM lParam);
  63. UINT FRHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fFindText);
  64. VOID UpdateTextAndFlags(HWND hDlg, LPFINDREPLACEW lpfrw, DWORD dwActionFlag, BOOL fFindText);
  65. LRESULT NotifyFindReplaceParent(LPFINDREPLACEW lpfr, LPFRHOOKPROC lpfrhp, UINT uMsg, BOOL fFindText);
  66. UINT_PTR OFNHookHelperOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn);
  67. UINT_PTR OFNHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fOpenFile);
  68. UINT_PTR OFNotifyHelper(HWND hdlg, WNDPROC lpfn, WPARAM wParam, LPARAM lParam);
  69. UINT_PTR GenericHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn);
  70. void SetFontProp(HWND hdlg);
  71. void SetNewFileOpenProp(HWND hdlg);
  72. /*-------------------------------
  73. WindowProc
  74. This is our global wrapper around *all* window
  75. procedrures for windows that we create or subclass.
  76. -------------------------------*/
  77. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  78. {
  79. LRESULT RetVal;
  80. WNDPROC lpfn = WndprocFromFauxWndproc(hwnd, 0, fptWndproc);
  81. // Chain to the user's wndproc
  82. RetVal = GodotDoCallback(hwnd, uMsg, wParam, lParam, lpfn, FALSE, fptWndproc);
  83. // If we get a final destroy message, lets unhook ourselves
  84. if(uMsg==WM_NCDESTROY)
  85. CleanupWindow(hwnd);
  86. return(RetVal);
  87. }
  88. /*-------------------------------
  89. DialogProc
  90. This is our global wrapper around *all* dialog
  91. procedrures for windows that we create or subclass.
  92. -------------------------------*/
  93. INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  94. {
  95. DLGPROC lpfn;
  96. INT_PTR RetVal;
  97. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  98. // See if we have a DLGPROC waiting to be assigned somewhere.
  99. if(lpgti && lpgti->pfnDlgProc)
  100. {
  101. // Perhaps it is for *this* dialog. We must make sure it is, or else we will be
  102. // assigning the proc to the wrong window! See Windows Bugs # 350862 for details.
  103. if(OUTSIDE_GODOT_RANGE(GetWindowLongInternal(hwndDlg, DWL_DLGPROC, TRUE)))
  104. {
  105. // Set the dlg proc and clear out the dlg proc pointer
  106. lpfn = (DLGPROC)SetWindowLongInternal(hwndDlg, DWL_DLGPROC, (LONG)lpgti->pfnDlgProc, TRUE);
  107. lpgti->pfnDlgProc = NULL;
  108. }
  109. }
  110. // Preprocess: also set some RetVal values, which may
  111. // or may not be overridden by the user's dlgproc.
  112. switch(uMsg)
  113. {
  114. case WM_CTLCOLORBTN:
  115. case WM_CTLCOLORDLG:
  116. case WM_CTLCOLOREDIT:
  117. case WM_CTLCOLORLISTBOX:
  118. case WM_CTLCOLORSCROLLBAR:
  119. case WM_CTLCOLORSTATIC:
  120. case WM_VKEYTOITEM:
  121. RetVal = (INT_PTR)(BOOL)FALSE;
  122. break;
  123. case WM_INITDIALOG:
  124. // Mark all the children now: before the client gets a
  125. // chance to init controls, but afte we are sure the
  126. // controls exist.
  127. EnumChildWindows(hwndDlg, &EnumChildToTagProc, 0);
  128. RetVal = (INT_PTR)(BOOL)FALSE;
  129. break;
  130. case WM_CHARTOITEM:
  131. case WM_COMPAREITEM:
  132. // The user's proc MUST do something for these messages,
  133. // we have no idea what to return here!
  134. RetVal = 0;
  135. break;
  136. case WM_QUERYDRAGICON:
  137. RetVal = (INT_PTR)NULL;
  138. break;
  139. default:
  140. RetVal = (INT_PTR)(BOOL)FALSE;
  141. break;
  142. }
  143. // Chain to the user's dialog procedure. They ought to have one, right?
  144. if(lpfn = WndprocFromFauxWndproc(hwndDlg, 0, fptDlgproc))
  145. RetVal = GodotDoCallback(hwndDlg, uMsg, wParam, lParam, lpfn, FALSE, fptDlgproc);
  146. return(RetVal);
  147. }
  148. /*-------------------------------
  149. CBTProc
  150. This is our CBT hook that we use to get information about a window
  151. about to be created. We also do our window subclassing here so we
  152. can be assured that we are handling the messages properly.
  153. -------------------------------*/
  154. LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
  155. {
  156. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  157. HHOOK hh = (lpgti ? lpgti->hHook : NULL);
  158. switch(nCode)
  159. {
  160. // We only care about the window creation notification
  161. case HCBT_CREATEWND:
  162. {
  163. LRESULT RetVal;
  164. InitWindow((HWND)wParam, NULL);
  165. RetVal = CallNextHookEx(hh, nCode, wParam, lParam);
  166. // Make sure no one cancelled window creation; if they did, the
  167. // window will be destroyed without a WM_DESTROY call. Therefore
  168. // we must be sure to clean ourselves up or we will watch Windows
  169. // lose our global atom!
  170. if(RetVal != 0)
  171. CleanupWindow((HWND)wParam);
  172. // Lets unhook here, we have done our duty
  173. TERM_WINDOW_SNIFF(hh);
  174. return(RetVal);
  175. break;
  176. }
  177. default:
  178. // Should be impossible, but just in case,
  179. // we do a nice default sorta thing here.
  180. if(hh)
  181. return(CallNextHookEx(hh, nCode, wParam, lParam));
  182. else
  183. return(0);
  184. break;
  185. }
  186. }
  187. /*-------------------------------
  188. EnumChildToTagProc
  189. Tag some child windows
  190. -------------------------------*/
  191. BOOL CALLBACK EnumChildToTagProc(HWND hwnd, LPARAM lParam)
  192. {
  193. InitWindow(hwnd, NULL);
  194. // Keep on enumerating
  195. return(TRUE);
  196. }
  197. /*-------------------------------
  198. FRHookProcFind/FRHookProcReplace
  199. -------------------------------*/
  200. UINT_PTR CALLBACK FRHookProcFind(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  201. {
  202. return(FRHelper(hdlg, uiMsg, wParam, lParam, TRUE));
  203. }
  204. UINT_PTR CALLBACK FRHookProcReplace(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  205. {
  206. return(FRHelper(hdlg, uiMsg, wParam, lParam, FALSE));
  207. }
  208. /*-------------------------------
  209. // OFNHookProc/OFNHookProcSave
  210. -------------------------------*/
  211. UINT_PTR CALLBACK OFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  212. {
  213. return(OFNHookHelper(hdlg, uiMsg, wParam, lParam, TRUE));
  214. }
  215. UINT_PTR CALLBACK OFNHookProcSave(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  216. {
  217. return(OFNHookHelper(hdlg, uiMsg, wParam, lParam, FALSE));
  218. }
  219. /*-------------------------------
  220. // PagePaintHook
  221. //
  222. //
  223. // We use this hook to handle the WM_PSD_PAGESETUPDLG when it comes through
  224. -------------------------------*/
  225. UINT_PTR CALLBACK PagePaintHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  226. {
  227. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  228. WNDPROC lpfnP = (lpgti ? lpgti->pfnPagePaint : NULL);
  229. if(uiMsg != WM_PSD_PAGESETUPDLG)
  230. return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, lpfnP));
  231. else
  232. {
  233. // Ok, this is the WM_PSD_PAGESETUPDLG event. wParam is some random stuff
  234. // to pass on and lParam is a PAGESETUPDLGA structure that we need to turn
  235. // into a PAGESETUPDLGW.
  236. if(lpfnP)
  237. {
  238. LPPAGESETUPDLGA lppsda;
  239. PAGESETUPDLGW psdw;
  240. UINT_PTR RetVal;
  241. ALLOCRETURN ar = arNoAlloc;
  242. WNDPROC lpfnS = (lpgti ? lpgti->pfnPageSetup : NULL);
  243. lppsda = (LPPAGESETUPDLGA)lParam;
  244. psdw.lStructSize = sizeof(PAGESETUPDLGW);
  245. // Copy some stuff over now
  246. psdw.hwndOwner = lppsda->hwndOwner;
  247. psdw.ptPaperSize = lppsda->ptPaperSize;
  248. psdw.rtMinMargin = lppsda->rtMinMargin;
  249. psdw.rtMargin = lppsda->rtMargin;
  250. psdw.hInstance = lppsda->hInstance;
  251. psdw.lCustData = lppsda->lCustData;
  252. // Do NOT deallocate the HGLOBALS here, someone else might
  253. // need them! Passing FALSE for the fFree param accomplishes
  254. // this feat.
  255. psdw.hDevMode = HDevModeWfromA(&(lppsda->hDevMode), FALSE);
  256. psdw.hDevNames = HDevNamesWfromA(&(lppsda->hDevNames), FALSE);
  257. // Hide the details of our hook from them (by munging flags as
  258. // necessary)
  259. psdw.Flags = lppsda->Flags;
  260. if(lpfnP)
  261. psdw.Flags &= ~PSD_ENABLEPAGEPAINTHOOK;
  262. psdw.lpfnPagePaintHook = lpfnP;
  263. if(lpfnS)
  264. psdw.Flags &= ~PSD_ENABLEPAGESETUPHOOK;
  265. psdw.lpfnPageSetupHook = lpfnS;
  266. if(lppsda->Flags & PSD_ENABLEPAGESETUPTEMPLATE)
  267. {
  268. psdw.hPageSetupTemplate = lppsda->hPageSetupTemplate;
  269. ar = GodotToUnicodeOnHeap(lppsda->lpPageSetupTemplateName,
  270. &(LPWSTR)psdw.lpPageSetupTemplateName);
  271. }
  272. RetVal = ((* lpfnP)(hdlg, WM_PSD_PAGESETUPDLG, wParam, (LPARAM)&psdw));
  273. if(ar==arAlloc)
  274. GodotHeapFree((LPWSTR)psdw.lpPageSetupTemplateName);
  275. lppsda->hDevMode = HDevModeAfromW(&(psdw.hDevMode), TRUE);
  276. lppsda->hDevNames = HDevNamesAfromW(&(psdw.hDevNames), TRUE);
  277. return(RetVal);
  278. }
  279. else
  280. return(FALSE);
  281. }
  282. }
  283. //
  284. // Ok, here are all our almost but not quite pointless hooks that we have.
  285. // These hooks allow us to make sure that the dialog and all the controls in
  286. // it are tagged appropriately. See GenericHookHelper for details.
  287. //
  288. UINT_PTR CALLBACK CCHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  289. {
  290. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  291. return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnChooseColor : NULL)));
  292. }
  293. UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  294. {
  295. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  296. if(uiMsg==WM_INITDIALOG)
  297. SetFontProp(hdlg);
  298. return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnChooseFont : NULL)));
  299. }
  300. UINT_PTR CALLBACK PageSetupHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  301. {
  302. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  303. return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPageSetup : NULL)));
  304. }
  305. UINT_PTR CALLBACK PrintHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  306. {
  307. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  308. return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPrintDlg : NULL)));
  309. }
  310. UINT_PTR CALLBACK SetupHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  311. {
  312. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  313. return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPrintDlgSetup : NULL)));
  314. }
  315. UINT_PTR CALLBACK OFNHookProcOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  316. {
  317. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  318. WNDPROC lpfn = (WNDPROC)(lpgti ? lpgti->pfnGetOpenFileNameOldStyle : NULL);
  319. return(OFNHookHelperOldStyle(hdlg, uiMsg, wParam, lParam, lpfn));
  320. }
  321. UINT_PTR CALLBACK OFNHookProcOldStyleSave(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  322. {
  323. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  324. WNDPROC lpfn = (WNDPROC)(lpgti ? lpgti->pfnGetSaveFileNameOldStyle : NULL);
  325. return(OFNHookHelperOldStyle(hdlg, uiMsg, wParam, lParam, lpfn));
  326. }
  327. /*-------------------------------
  328. // FRHelper
  329. //
  330. // Used to handle lots of the shared code between the find and replace hook functions. Note
  331. // that some of it does not always apply: certain controls are hidden from the FIND dialog, etc.
  332. // But since all of the code other than the actual callbacks themselves is shared, this keeps
  333. // us from a lot of duplication.
  334. -------------------------------*/
  335. UINT FRHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fFindText)
  336. {
  337. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  338. LPFRHOOKPROC lpfn = 0;
  339. UINT RetVal;
  340. if(!lpgti)
  341. {
  342. if(lpfn)
  343. return((* lpfn)(hdlg, uiMsg, wParam, lParam));
  344. else
  345. return(FALSE);
  346. }
  347. if(fFindText)
  348. lpfn = lpgti->pfnFindText;
  349. else
  350. lpfn = lpgti->pfnReplaceText;
  351. switch(uiMsg)
  352. {
  353. case WM_COMMAND:
  354. {
  355. LPFINDREPLACEW lpfr;
  356. if(fFindText)
  357. lpfr = lpgti->lpfrwFind;
  358. else
  359. lpfr = lpgti->lpfrwReplace;
  360. switch(GET_WM_COMMAND_ID(wParam, lParam))
  361. {
  362. case IDOK: // FIND
  363. UpdateTextAndFlags(hdlg, lpfr, FR_FINDNEXT, fFindText);
  364. NotifyFindReplaceParent(lpfr, lpfn, msgFINDMSGSTRING, fFindText);
  365. RetVal = TRUE;
  366. break;
  367. case psh1: // REPLACE
  368. case psh2: // REPLACE ALL
  369. UpdateTextAndFlags(hdlg, lpfr, (psh1 ? FR_REPLACE : FR_REPLACEALL), fFindText);
  370. if (NotifyFindReplaceParent(lpfr, lpfn, msgFINDMSGSTRING, fFindText) == TRUE)
  371. {
  372. // Change <Cancel> button to <Close> if function
  373. // returns TRUE (IDCANCEL instead of psh1).
  374. // First load the string if we never have before
  375. if(m_szClose[0] == 0)
  376. GodotLoadStringW(GetComDlgHandle(), iszClose, m_szClose, CCHCLOSE);
  377. GodotSetWindowTextW(GetDlgItem(hdlg, IDCANCEL), (LPWSTR)m_szClose);
  378. }
  379. RetVal = TRUE;
  380. break;
  381. case pshHelp: // HELP
  382. if (msgHELPMSGSTRING && lpfr->hwndOwner)
  383. {
  384. UpdateTextAndFlags(hdlg, lpfr, 0, fFindText);
  385. NotifyFindReplaceParent(lpfr, lpfn, msgHELPMSGSTRING, fFindText);
  386. }
  387. RetVal = TRUE;
  388. break;
  389. case IDCANCEL: // CANCEL, both types
  390. case IDABORT:
  391. // They are destroying the dialog, so unhook ourselves
  392. // and clean up the dialog. Usually the caller will keep
  393. // a FINDREPLACEW lying around, so if we do not clean up
  394. // we might have some re-entrancy issues here.
  395. if(lpfn)
  396. lpfr->lpfnHook = lpfn;
  397. else
  398. {
  399. lpfr->lpfnHook = NULL;
  400. lpfr->Flags &= ~FR_ENABLEHOOK;
  401. }
  402. if(fFindText)
  403. {
  404. lpgti->lpfrwFind = NULL;
  405. lpgti->pfnFindText = NULL;
  406. }
  407. else
  408. {
  409. lpgti->lpfrwReplace = NULL;
  410. lpgti->pfnReplaceText = NULL;
  411. }
  412. // Fall through. We do not set the RetVal to TRUE, since
  413. // we need to make sure that comdlg32.dll cleans up
  414. default: // Everything else
  415. RetVal = FALSE;
  416. break;
  417. }
  418. break;
  419. }
  420. case WM_INITDIALOG:
  421. // Mark all the children now, before the user's proc gets to them
  422. EnumChildWindows(hdlg, &EnumChildToTagProc, 0);
  423. // Perhaps the caller's hook will override this, but we need to cover
  424. // our bases in case there the caller has no hook. The caller expects
  425. // us to return TRUE if they are drawing the dlg, FALSE if we are.
  426. RetVal = TRUE;
  427. break;
  428. default:
  429. RetVal = FALSE;
  430. break;
  431. }
  432. if(lpfn)
  433. RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
  434. return(RetVal);
  435. }
  436. /*-------------------------------
  437. // OFNHookHelperOldStyle
  438. //
  439. // Almost all of the old style fileopen/save code is shared, and this function shares it!
  440. -------------------------------*/
  441. UINT_PTR OFNHookHelperOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn)
  442. {
  443. UINT_PTR RetVal = FALSE;
  444. if(!msgSHAREVISTRING)
  445. msgSHAREVISTRING = RegisterWindowMessage(SHAREVISTRINGA);
  446. if(!msgFILEOKSTRING)
  447. msgFILEOKSTRING = RegisterWindowMessage(FILEOKSTRINGA);
  448. if(uiMsg==WM_INITDIALOG)
  449. {
  450. // Mark all the children now, before the user's proc gets to them
  451. EnumChildWindows(hdlg, &EnumChildToTagProc, 0);
  452. if(lpfn)
  453. RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
  454. else
  455. RetVal = TRUE;
  456. }
  457. else if(((uiMsg == msgFILEOKSTRING) || (uiMsg == msgSHAREVISTRING)))
  458. {
  459. WCHAR drive[_MAX_DRIVE + 1];
  460. WCHAR dir[_MAX_DIR +1];
  461. WCHAR file[_MAX_FNAME +1];
  462. LPOPENFILENAMEA lpofnA = (LPOPENFILENAMEA)lParam;
  463. OPENFILENAMEW ofn;
  464. LPARAM lParamW;
  465. ALLOCRETURN arCustomFilter = arNoAlloc;
  466. ALLOCRETURN arFile = arNoAlloc;
  467. ALLOCRETURN arFileTitle = arNoAlloc;
  468. // lParam is an LPOPENFILENAMEA to be converted. Copy all the
  469. // members that the user might expect.
  470. ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
  471. arCustomFilter = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrCustomFilter,
  472. lpofnA->nMaxCustFilter,
  473. &ofn.lpstrCustomFilter,
  474. g_acp);
  475. ofn.nMaxCustFilter = gwcslen(ofn.lpstrCustomFilter);
  476. ofn.nFilterIndex = lpofnA->nFilterIndex;
  477. ofn.nMaxFile = lpofnA->nMaxFile * sizeof(WCHAR);
  478. arFile = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFile,
  479. lpofnA->nMaxFile,
  480. &ofn.lpstrFile,
  481. g_acp);
  482. ofn.nMaxFile = gwcslen(ofn.lpstrFile);
  483. arFileTitle = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFileTitle,
  484. lpofnA->nMaxFileTitle,
  485. &ofn.lpstrFileTitle,
  486. g_acp);
  487. ofn.nMaxFileTitle = gwcslen(ofn.lpstrFileTitle);
  488. ofn.Flags = lpofnA->Flags;
  489. // nFileOffset and nFileExtension are to provide info about the
  490. // file name and extension location in lpstrFile, but there is
  491. // no reasonable way to get it from the return so we just recalc
  492. gwsplitpath(ofn.lpstrFile, drive, dir, file, NULL);
  493. ofn.nFileOffset = (gwcslen(drive) + gwcslen(dir));
  494. ofn.nFileExtension = ofn.nFileOffset + gwcslen(file);
  495. lParamW = (LPARAM)&ofn;
  496. if(lpfn)
  497. RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParamW);
  498. // Free up some memory if we allocated any
  499. if(arCustomFilter==arAlloc)
  500. GodotHeapFree(ofn.lpstrCustomFilter);
  501. if(arFile==arAlloc)
  502. GodotHeapFree(ofn.lpstrFile);
  503. if(arFileTitle==arAlloc)
  504. GodotHeapFree(ofn.lpstrFileTitle);
  505. }
  506. else
  507. {
  508. if(lpfn)
  509. RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
  510. }
  511. return(RetVal);
  512. }
  513. /*-------------------------------
  514. // OFNHookHelper
  515. //
  516. // Almost all of the fileopen/save code is shared, and this function shares it!
  517. -------------------------------*/
  518. UINT_PTR OFNHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fOpenFile)
  519. {
  520. UINT_PTR RetVal = 0;
  521. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
  522. LPOFNHOOKPROC lpfn;
  523. if(fOpenFile)
  524. lpfn = (lpgti ? lpgti->pfnGetOpenFileName : NULL);
  525. else
  526. lpfn = (lpgti ? lpgti->pfnGetSaveFileName : NULL);
  527. if(uiMsg==WM_INITDIALOG)
  528. {
  529. HWND hwndParent;
  530. // Tag the window as a new style file open dlg
  531. SetNewFileOpenProp(hdlg);
  532. if(hwndParent = GetParent(hdlg))
  533. SetNewFileOpenProp(hwndParent);
  534. // Mark all the children now, before the user's proc gets to them
  535. EnumChildWindows(hdlg, &EnumChildToTagProc, 0);
  536. RetVal = TRUE;
  537. }
  538. if(lpfn)
  539. {
  540. switch(uiMsg)
  541. {
  542. case WM_NOTIFY:
  543. RetVal = OFNotifyHelper(hdlg, lpfn, wParam, lParam);
  544. break;
  545. default:
  546. RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
  547. break;
  548. }
  549. }
  550. return(RetVal);
  551. }
  552. /*-------------------------------
  553. // OSNotifyHelper
  554. -------------------------------*/
  555. UINT_PTR OFNotifyHelper(HWND hdlg, WNDPROC lpfn, WPARAM wParam, LPARAM lParam)
  556. {
  557. UINT_PTR RetVal;
  558. LPOFNOTIFYA lpofnfyA = (LPOFNOTIFYA)lParam;
  559. switch(lpofnfyA->hdr.code)
  560. {
  561. case CDN_FILEOK:
  562. case CDN_FOLDERCHANGE:
  563. case CDN_HELP:
  564. case CDN_INCLUDEITEM:
  565. case CDN_INITDONE:
  566. case CDN_SELCHANGE:
  567. case CDN_SHAREVIOLATION:
  568. case CDN_TYPECHANGE:
  569. {
  570. LPOPENFILENAMEA lpofnA = lpofnfyA->lpOFN;
  571. OFNOTIFYW ofnfy;
  572. OPENFILENAMEW ofn;
  573. WCHAR drive[_MAX_DRIVE];
  574. WCHAR dir[_MAX_DIR];
  575. WCHAR file[_MAX_FNAME];
  576. ALLOCRETURN arCustomFilter = arNoAlloc;
  577. ALLOCRETURN arFile = arNoAlloc;
  578. ALLOCRETURN arFileTitle = arNoAlloc;
  579. ZeroMemory(&ofnfy, sizeof(OFNOTIFYW));
  580. ofnfy.hdr = lpofnfyA->hdr;
  581. ofnfy.lpOFN = &ofn;
  582. ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
  583. ofn.Flags = lpofnA->Flags;
  584. ofn.hwndOwner = lpofnA->hwndOwner;
  585. ofn.hInstance = lpofnA->hInstance;
  586. ofn.lpfnHook = lpfn;
  587. arCustomFilter = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrCustomFilter,
  588. lpofnA->nMaxCustFilter,
  589. &ofn.lpstrCustomFilter,
  590. g_acp);
  591. ofn.nMaxCustFilter = gwcslen(ofn.lpstrCustomFilter);
  592. ofn.nFilterIndex = lpofnA->nFilterIndex;
  593. ofn.nMaxFile = lpofnA->nMaxFile * sizeof(WCHAR);
  594. arFile = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFile,
  595. lpofnA->nMaxFile,
  596. &ofn.lpstrFile,
  597. g_acp);
  598. ofn.nMaxFile = gwcslen(ofn.lpstrFile);
  599. arFileTitle = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFileTitle,
  600. lpofnA->nMaxFileTitle,
  601. &ofn.lpstrFileTitle,
  602. g_acp);
  603. ofn.nMaxFileTitle = gwcslen(ofn.lpstrFileTitle);
  604. ofn.Flags = lpofnA->Flags;
  605. // nFileOffset and nFileExtension are to provide info about the
  606. // file name and extension location in lpstrFile, but there is
  607. // no reasonable way to get it from the return so we just recalc
  608. gwsplitpath(ofn.lpstrFile, drive, dir, file, NULL);
  609. ofn.nFileOffset = (gwcslen(drive) + gwcslen(dir));
  610. ofn.nFileExtension = ofn.nFileOffset + gwcslen(file);
  611. if(ofnfy.hdr.code == CDN_SHAREVIOLATION)
  612. {
  613. WCHAR lpwzFile[MAX_PATH];
  614. if(FSTRING_VALID(lpofnfyA->pszFile))
  615. {
  616. MultiByteToWideChar(g_acp, 0,
  617. lpofnfyA->pszFile, -1,
  618. lpwzFile, MAX_PATH);
  619. ofnfy.pszFile = lpwzFile;
  620. }
  621. RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, (LPARAM)&ofnfy);
  622. }
  623. else
  624. {
  625. RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, (LPARAM)&ofnfy);
  626. }
  627. // Free up some memory if we allocated any
  628. if(arCustomFilter==arAlloc)
  629. GodotHeapFree(ofn.lpstrCustomFilter);
  630. if(arFile==arAlloc)
  631. GodotHeapFree(ofn.lpstrFile);
  632. if(arFileTitle==arAlloc)
  633. GodotHeapFree(ofn.lpstrFileTitle);
  634. break;
  635. }
  636. default:
  637. RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, lParam);
  638. break;
  639. }
  640. return(RetVal);
  641. }
  642. /*-------------------------------
  643. // GenericHookHelper
  644. //
  645. // This function is to assist all the comdlg32 hook functions that serve
  646. // no real purpose that we know of, other than to make sure all the
  647. // child controls get marked as "Unicode" controls, etc.
  648. -------------------------------*/
  649. UINT_PTR GenericHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn)
  650. {
  651. UINT_PTR RetVal = FALSE;
  652. switch(uiMsg)
  653. {
  654. case WM_INITDIALOG:
  655. // Mark all the children now, before the user's proc gets to them
  656. EnumChildWindows(hdlg, &EnumChildToTagProc, 0);
  657. if(lpfn)
  658. RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
  659. else
  660. RetVal = TRUE;
  661. break;
  662. case WM_DESTROY:
  663. case WM_NCDESTROY:
  664. default:
  665. if(lpfn)
  666. RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
  667. break;
  668. }
  669. return(RetVal);
  670. }
  671. /*-------------------------------
  672. // UpdateTextAndFlags
  673. //
  674. // Get the text from the control, and update the flags. Use in
  675. // preparation for notifying the owner window that the user has
  676. // done something interesting.
  677. //
  678. // Modified from find.c from the comdlg32 project in the Shell
  679. // depot. It definitely needed its own special spin, though!
  680. //
  681. // chx1 is whether or not to match entire words
  682. // chx2 is whether or not case is relevant
  683. // chx3 is whether or not to wrap scans
  684. -------------------------------*/
  685. VOID UpdateTextAndFlags(HWND hDlg, LPFINDREPLACEW lpfrw, DWORD dwActionFlag, BOOL fFindText)
  686. {
  687. // Only clear flags that this routine sets. The hook and template
  688. // flags should not be anded off here.
  689. lpfrw->Flags &= ~((DWORD)(FR_WHOLEWORD | FR_MATCHCASE | FR_REPLACE |
  690. FR_FINDNEXT | FR_REPLACEALL | FR_DOWN));
  691. if (IsDlgButtonChecked(hDlg, chx1))
  692. lpfrw->Flags |= FR_WHOLEWORD;
  693. if (IsDlgButtonChecked(hDlg, chx2))
  694. lpfrw->Flags |= FR_MATCHCASE;
  695. // Set ACTION flag FR_{REPLACE,FINDNEXT,REPLACEALL}.
  696. lpfrw->Flags |= dwActionFlag;
  697. GodotGetDlgItemTextW(hDlg, edt1, lpfrw->lpstrFindWhat, lpfrw->wFindWhatLen);
  698. if (fFindText)
  699. {
  700. // Assume searching down. Check if UP button is NOT pressed, rather
  701. // than if DOWN button IS. So, if buttons have been hidden or
  702. // disabled, FR_DOWN flag will be set correctly.
  703. if (!IsDlgButtonChecked(hDlg, rad1))
  704. lpfrw->Flags |= FR_DOWN;
  705. }
  706. else
  707. {
  708. GodotGetDlgItemTextW(hDlg, edt2, lpfrw->lpstrReplaceWith, lpfrw->wReplaceWithLen);
  709. lpfrw->Flags |= FR_DOWN;
  710. }
  711. }
  712. /*-------------------------------
  713. // NotifyFindReplaceParent
  714. //
  715. // Let hwndOwner know what is happening, since the user has
  716. // performed some kind of action that they might want to
  717. // know about.
  718. //
  719. // Modified from find.c from the comdlg32 project in the Shell
  720. // depot. It definitely needed its own special spin, though!
  721. -------------------------------*/
  722. LRESULT NotifyFindReplaceParent(LPFINDREPLACEW lpfr, LPFRHOOKPROC lpfrhp, UINT uMsg, BOOL fFindText)
  723. {
  724. LRESULT RetVal;
  725. DWORD Flags;
  726. // Cache the flags setting since we may be mucking with it
  727. Flags = lpfr->Flags;
  728. // Now, muck with the structure a bit
  729. // to hide our hook from the user
  730. if(lpfrhp)
  731. lpfr->lpfnHook = lpfrhp;
  732. else
  733. {
  734. lpfr->lpfnHook = NULL;
  735. Flags &= ~FR_ENABLEHOOK;
  736. }
  737. RetVal = GodotSendMessageW(lpfr->hwndOwner, uMsg, 0, (DWORD_PTR)lpfr);
  738. // Restore the structure to what it was
  739. lpfr->Flags = Flags;
  740. if(fFindText)
  741. lpfr->lpfnHook = &FRHookProcFind;
  742. else
  743. lpfr->lpfnHook = &FRHookProcReplace;
  744. return(RetVal);
  745. }
  746. /*-------------------------------
  747. // IsFontDialog
  748. -------------------------------*/
  749. BOOL IsFontDialog(HWND hdlg)
  750. {
  751. return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == CHOOSEFONT_DIALOG));
  752. }
  753. /*-------------------------------
  754. // IsNewFileOpenDialog
  755. -------------------------------*/
  756. BOOL IsNewFileOpenDialog(HWND hdlg)
  757. {
  758. return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == NEWFILEOPEN_DIALOG));
  759. }
  760. /*-------------------------------
  761. // IsCaptureWindow
  762. -------------------------------*/
  763. BOOL IsCaptureWindow(HWND hdlg)
  764. {
  765. return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == CAPTURE_WINDOW));
  766. }
  767. /*-------------------------------
  768. // RemoveComdlgPropIfPresent
  769. -------------------------------*/
  770. void RemoveComdlgPropIfPresent(HWND hdlg)
  771. {
  772. if(GetPropA(hdlg, m_szComdlgClass) != 0)
  773. RemovePropA(hdlg, m_szComdlgClass);
  774. return;
  775. }
  776. /*-------------------------------
  777. // SetNewFileOpenProp
  778. -------------------------------*/
  779. void SetNewFileOpenProp(HWND hdlg)
  780. {
  781. // We have to aggressively refcount our prop to keep anyone from
  782. // losing it. Thus we do an initial GlobalAddAtom on it and then
  783. // subsequently call SetPropA on it with the same string.
  784. if(!m_aComdlgClass)
  785. m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass);
  786. SetPropA(hdlg, m_szComdlgClass, (HANDLE)NEWFILEOPEN_DIALOG);
  787. return;
  788. }
  789. /*-------------------------------
  790. // SetFontProp
  791. -------------------------------*/
  792. void SetFontProp(HWND hdlg)
  793. {
  794. // We have to aggressively refcount our prop to keep anyone from
  795. // losing it. Thus we do an initial GlobalAddAtom on it and then
  796. // subsequently call SetPropA on it with the same string.
  797. if(!m_aComdlgClass)
  798. m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass);
  799. SetPropA(hdlg, m_szComdlgClass, (HANDLE)CHOOSEFONT_DIALOG);
  800. return;
  801. }
  802. /*-------------------------------
  803. // SetCaptureWindowProp
  804. -------------------------------*/
  805. void SetCaptureWindowProp(HWND hdlg)
  806. {
  807. // We have to aggressively refcount our prop to keep anyone from
  808. // losing it. Thus we do an initial GlobalAddAtom on it and then
  809. // subsequently call SetPropA on it with the same string.
  810. if(!m_aComdlgClass)
  811. m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass);
  812. SetPropA(hdlg, m_szComdlgClass, (HANDLE)CAPTURE_WINDOW);
  813. return;
  814. }