windows-nt/Source/XPSP1/NT/sdktools/unicows/godot/hook.c
2020-09-26 16:20:57 +08:00

956 lines
32 KiB
C

/*++
Copyright (c) 2000-2001, Microsoft Corporation All rights reserved.
Module Name:
hook.c
Abstract:
This file contains functions that wrap various hook procedures,
and the helper functions that support their efforts.
Hooks found in this file:
WindowProc
DialogProc
CBTProc
EnumChildToTagProc
comdlg32.dll hooks:
FRHookProcFind
FRHookProcReplace
OFNHookProc
OFNHookProcSave
PagePaintHook
CCHookProc
CFHookProc
PageSetupHook
PrintHookProc
SetupHookProc
OFNHookProcOldStyle
OFNHookProcOldStyleSave
Helper functions:
FRHelper
UpdateTextAndFlags
NotifyFindReplaceParent
OFNHookHelper
OFNotifyHelper
GenericHookHelper
RemoveFontPropIfPresent
SetNewFileOpenProp
SetFontProp
Externally available helper functions:
IsFontDialog
IsNewFileOpenDialog
IsCaptureWindow
SetCaptureWindowProp
Revision History:
27 Jan 2001 v-michka Created.
--*/
#include "precomp.h"
// Stolen from commdlg.h
#define CCHCLOSE 9
#define iszClose 0x040d // "Close" text for find/replace
static WCHAR m_szClose [CCHCLOSE];
// So we can keep track of font dialogs
const char m_szComdlgClass[] = "GodotComdlgClass";
ATOM m_aComdlgClass;
#define CHOOSEFONT_DIALOG (HANDLE)1
#define NEWFILEOPEN_DIALOG (HANDLE)2
#define CAPTURE_WINDOW (HANDLE)3 // Not really a common dialog, but close enough
// forward declares we will need -- we must have Unicode text, so
// why write new wrappers when we have some lying around already?
UINT __stdcall GodotGetDlgItemTextW(HWND hDlg, int nIDDlgItem, LPWSTR lpString, int nMaxCount);
int __stdcall GodotLoadStringW(HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer, int nBufferMax);
BOOL __stdcall GodotSetWindowTextW(HWND hWnd, LPCWSTR lpString);
LRESULT __stdcall GodotSendMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
// our own forward declares
BOOL CALLBACK EnumChildToTagProc(HWND hwnd, LPARAM lParam);
UINT FRHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fFindText);
VOID UpdateTextAndFlags(HWND hDlg, LPFINDREPLACEW lpfrw, DWORD dwActionFlag, BOOL fFindText);
LRESULT NotifyFindReplaceParent(LPFINDREPLACEW lpfr, LPFRHOOKPROC lpfrhp, UINT uMsg, BOOL fFindText);
UINT_PTR OFNHookHelperOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn);
UINT_PTR OFNHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fOpenFile);
UINT_PTR OFNotifyHelper(HWND hdlg, WNDPROC lpfn, WPARAM wParam, LPARAM lParam);
UINT_PTR GenericHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn);
void SetFontProp(HWND hdlg);
void SetNewFileOpenProp(HWND hdlg);
/*-------------------------------
WindowProc
This is our global wrapper around *all* window
procedrures for windows that we create or subclass.
-------------------------------*/
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT RetVal;
WNDPROC lpfn = WndprocFromFauxWndproc(hwnd, 0, fptWndproc);
// Chain to the user's wndproc
RetVal = GodotDoCallback(hwnd, uMsg, wParam, lParam, lpfn, FALSE, fptWndproc);
// If we get a final destroy message, lets unhook ourselves
if(uMsg==WM_NCDESTROY)
CleanupWindow(hwnd);
return(RetVal);
}
/*-------------------------------
DialogProc
This is our global wrapper around *all* dialog
procedrures for windows that we create or subclass.
-------------------------------*/
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DLGPROC lpfn;
INT_PTR RetVal;
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
// See if we have a DLGPROC waiting to be assigned somewhere.
if(lpgti && lpgti->pfnDlgProc)
{
// Perhaps it is for *this* dialog. We must make sure it is, or else we will be
// assigning the proc to the wrong window! See Windows Bugs # 350862 for details.
if(OUTSIDE_GODOT_RANGE(GetWindowLongInternal(hwndDlg, DWL_DLGPROC, TRUE)))
{
// Set the dlg proc and clear out the dlg proc pointer
lpfn = (DLGPROC)SetWindowLongInternal(hwndDlg, DWL_DLGPROC, (LONG)lpgti->pfnDlgProc, TRUE);
lpgti->pfnDlgProc = NULL;
}
}
// Preprocess: also set some RetVal values, which may
// or may not be overridden by the user's dlgproc.
switch(uMsg)
{
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
case WM_VKEYTOITEM:
RetVal = (INT_PTR)(BOOL)FALSE;
break;
case WM_INITDIALOG:
// Mark all the children now: before the client gets a
// chance to init controls, but afte we are sure the
// controls exist.
EnumChildWindows(hwndDlg, &EnumChildToTagProc, 0);
RetVal = (INT_PTR)(BOOL)FALSE;
break;
case WM_CHARTOITEM:
case WM_COMPAREITEM:
// The user's proc MUST do something for these messages,
// we have no idea what to return here!
RetVal = 0;
break;
case WM_QUERYDRAGICON:
RetVal = (INT_PTR)NULL;
break;
default:
RetVal = (INT_PTR)(BOOL)FALSE;
break;
}
// Chain to the user's dialog procedure. They ought to have one, right?
if(lpfn = WndprocFromFauxWndproc(hwndDlg, 0, fptDlgproc))
RetVal = GodotDoCallback(hwndDlg, uMsg, wParam, lParam, lpfn, FALSE, fptDlgproc);
return(RetVal);
}
/*-------------------------------
CBTProc
This is our CBT hook that we use to get information about a window
about to be created. We also do our window subclassing here so we
can be assured that we are handling the messages properly.
-------------------------------*/
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
HHOOK hh = (lpgti ? lpgti->hHook : NULL);
switch(nCode)
{
// We only care about the window creation notification
case HCBT_CREATEWND:
{
LRESULT RetVal;
InitWindow((HWND)wParam, NULL);
RetVal = CallNextHookEx(hh, nCode, wParam, lParam);
// Make sure no one cancelled window creation; if they did, the
// window will be destroyed without a WM_DESTROY call. Therefore
// we must be sure to clean ourselves up or we will watch Windows
// lose our global atom!
if(RetVal != 0)
CleanupWindow((HWND)wParam);
// Lets unhook here, we have done our duty
TERM_WINDOW_SNIFF(hh);
return(RetVal);
break;
}
default:
// Should be impossible, but just in case,
// we do a nice default sorta thing here.
if(hh)
return(CallNextHookEx(hh, nCode, wParam, lParam));
else
return(0);
break;
}
}
/*-------------------------------
EnumChildToTagProc
Tag some child windows
-------------------------------*/
BOOL CALLBACK EnumChildToTagProc(HWND hwnd, LPARAM lParam)
{
InitWindow(hwnd, NULL);
// Keep on enumerating
return(TRUE);
}
/*-------------------------------
FRHookProcFind/FRHookProcReplace
-------------------------------*/
UINT_PTR CALLBACK FRHookProcFind(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
return(FRHelper(hdlg, uiMsg, wParam, lParam, TRUE));
}
UINT_PTR CALLBACK FRHookProcReplace(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
return(FRHelper(hdlg, uiMsg, wParam, lParam, FALSE));
}
/*-------------------------------
// OFNHookProc/OFNHookProcSave
-------------------------------*/
UINT_PTR CALLBACK OFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
return(OFNHookHelper(hdlg, uiMsg, wParam, lParam, TRUE));
}
UINT_PTR CALLBACK OFNHookProcSave(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
return(OFNHookHelper(hdlg, uiMsg, wParam, lParam, FALSE));
}
/*-------------------------------
// PagePaintHook
//
//
// We use this hook to handle the WM_PSD_PAGESETUPDLG when it comes through
-------------------------------*/
UINT_PTR CALLBACK PagePaintHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
WNDPROC lpfnP = (lpgti ? lpgti->pfnPagePaint : NULL);
if(uiMsg != WM_PSD_PAGESETUPDLG)
return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, lpfnP));
else
{
// Ok, this is the WM_PSD_PAGESETUPDLG event. wParam is some random stuff
// to pass on and lParam is a PAGESETUPDLGA structure that we need to turn
// into a PAGESETUPDLGW.
if(lpfnP)
{
LPPAGESETUPDLGA lppsda;
PAGESETUPDLGW psdw;
UINT_PTR RetVal;
ALLOCRETURN ar = arNoAlloc;
WNDPROC lpfnS = (lpgti ? lpgti->pfnPageSetup : NULL);
lppsda = (LPPAGESETUPDLGA)lParam;
psdw.lStructSize = sizeof(PAGESETUPDLGW);
// Copy some stuff over now
psdw.hwndOwner = lppsda->hwndOwner;
psdw.ptPaperSize = lppsda->ptPaperSize;
psdw.rtMinMargin = lppsda->rtMinMargin;
psdw.rtMargin = lppsda->rtMargin;
psdw.hInstance = lppsda->hInstance;
psdw.lCustData = lppsda->lCustData;
// Do NOT deallocate the HGLOBALS here, someone else might
// need them! Passing FALSE for the fFree param accomplishes
// this feat.
psdw.hDevMode = HDevModeWfromA(&(lppsda->hDevMode), FALSE);
psdw.hDevNames = HDevNamesWfromA(&(lppsda->hDevNames), FALSE);
// Hide the details of our hook from them (by munging flags as
// necessary)
psdw.Flags = lppsda->Flags;
if(lpfnP)
psdw.Flags &= ~PSD_ENABLEPAGEPAINTHOOK;
psdw.lpfnPagePaintHook = lpfnP;
if(lpfnS)
psdw.Flags &= ~PSD_ENABLEPAGESETUPHOOK;
psdw.lpfnPageSetupHook = lpfnS;
if(lppsda->Flags & PSD_ENABLEPAGESETUPTEMPLATE)
{
psdw.hPageSetupTemplate = lppsda->hPageSetupTemplate;
ar = GodotToUnicodeOnHeap(lppsda->lpPageSetupTemplateName,
&(LPWSTR)psdw.lpPageSetupTemplateName);
}
RetVal = ((* lpfnP)(hdlg, WM_PSD_PAGESETUPDLG, wParam, (LPARAM)&psdw));
if(ar==arAlloc)
GodotHeapFree((LPWSTR)psdw.lpPageSetupTemplateName);
lppsda->hDevMode = HDevModeAfromW(&(psdw.hDevMode), TRUE);
lppsda->hDevNames = HDevNamesAfromW(&(psdw.hDevNames), TRUE);
return(RetVal);
}
else
return(FALSE);
}
}
//
// Ok, here are all our almost but not quite pointless hooks that we have.
// These hooks allow us to make sure that the dialog and all the controls in
// it are tagged appropriately. See GenericHookHelper for details.
//
UINT_PTR CALLBACK CCHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnChooseColor : NULL)));
}
UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
if(uiMsg==WM_INITDIALOG)
SetFontProp(hdlg);
return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnChooseFont : NULL)));
}
UINT_PTR CALLBACK PageSetupHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPageSetup : NULL)));
}
UINT_PTR CALLBACK PrintHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPrintDlg : NULL)));
}
UINT_PTR CALLBACK SetupHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPrintDlgSetup : NULL)));
}
UINT_PTR CALLBACK OFNHookProcOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
WNDPROC lpfn = (WNDPROC)(lpgti ? lpgti->pfnGetOpenFileNameOldStyle : NULL);
return(OFNHookHelperOldStyle(hdlg, uiMsg, wParam, lParam, lpfn));
}
UINT_PTR CALLBACK OFNHookProcOldStyleSave(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
WNDPROC lpfn = (WNDPROC)(lpgti ? lpgti->pfnGetSaveFileNameOldStyle : NULL);
return(OFNHookHelperOldStyle(hdlg, uiMsg, wParam, lParam, lpfn));
}
/*-------------------------------
// FRHelper
//
// Used to handle lots of the shared code between the find and replace hook functions. Note
// that some of it does not always apply: certain controls are hidden from the FIND dialog, etc.
// But since all of the code other than the actual callbacks themselves is shared, this keeps
// us from a lot of duplication.
-------------------------------*/
UINT FRHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fFindText)
{
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
LPFRHOOKPROC lpfn = 0;
UINT RetVal;
if(!lpgti)
{
if(lpfn)
return((* lpfn)(hdlg, uiMsg, wParam, lParam));
else
return(FALSE);
}
if(fFindText)
lpfn = lpgti->pfnFindText;
else
lpfn = lpgti->pfnReplaceText;
switch(uiMsg)
{
case WM_COMMAND:
{
LPFINDREPLACEW lpfr;
if(fFindText)
lpfr = lpgti->lpfrwFind;
else
lpfr = lpgti->lpfrwReplace;
switch(GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK: // FIND
UpdateTextAndFlags(hdlg, lpfr, FR_FINDNEXT, fFindText);
NotifyFindReplaceParent(lpfr, lpfn, msgFINDMSGSTRING, fFindText);
RetVal = TRUE;
break;
case psh1: // REPLACE
case psh2: // REPLACE ALL
UpdateTextAndFlags(hdlg, lpfr, (psh1 ? FR_REPLACE : FR_REPLACEALL), fFindText);
if (NotifyFindReplaceParent(lpfr, lpfn, msgFINDMSGSTRING, fFindText) == TRUE)
{
// Change <Cancel> button to <Close> if function
// returns TRUE (IDCANCEL instead of psh1).
// First load the string if we never have before
if(m_szClose[0] == 0)
GodotLoadStringW(GetComDlgHandle(), iszClose, m_szClose, CCHCLOSE);
GodotSetWindowTextW(GetDlgItem(hdlg, IDCANCEL), (LPWSTR)m_szClose);
}
RetVal = TRUE;
break;
case pshHelp: // HELP
if (msgHELPMSGSTRING && lpfr->hwndOwner)
{
UpdateTextAndFlags(hdlg, lpfr, 0, fFindText);
NotifyFindReplaceParent(lpfr, lpfn, msgHELPMSGSTRING, fFindText);
}
RetVal = TRUE;
break;
case IDCANCEL: // CANCEL, both types
case IDABORT:
// They are destroying the dialog, so unhook ourselves
// and clean up the dialog. Usually the caller will keep
// a FINDREPLACEW lying around, so if we do not clean up
// we might have some re-entrancy issues here.
if(lpfn)
lpfr->lpfnHook = lpfn;
else
{
lpfr->lpfnHook = NULL;
lpfr->Flags &= ~FR_ENABLEHOOK;
}
if(fFindText)
{
lpgti->lpfrwFind = NULL;
lpgti->pfnFindText = NULL;
}
else
{
lpgti->lpfrwReplace = NULL;
lpgti->pfnReplaceText = NULL;
}
// Fall through. We do not set the RetVal to TRUE, since
// we need to make sure that comdlg32.dll cleans up
default: // Everything else
RetVal = FALSE;
break;
}
break;
}
case WM_INITDIALOG:
// Mark all the children now, before the user's proc gets to them
EnumChildWindows(hdlg, &EnumChildToTagProc, 0);
// Perhaps the caller's hook will override this, but we need to cover
// our bases in case there the caller has no hook. The caller expects
// us to return TRUE if they are drawing the dlg, FALSE if we are.
RetVal = TRUE;
break;
default:
RetVal = FALSE;
break;
}
if(lpfn)
RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
return(RetVal);
}
/*-------------------------------
// OFNHookHelperOldStyle
//
// Almost all of the old style fileopen/save code is shared, and this function shares it!
-------------------------------*/
UINT_PTR OFNHookHelperOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn)
{
UINT_PTR RetVal = FALSE;
if(!msgSHAREVISTRING)
msgSHAREVISTRING = RegisterWindowMessage(SHAREVISTRINGA);
if(!msgFILEOKSTRING)
msgFILEOKSTRING = RegisterWindowMessage(FILEOKSTRINGA);
if(uiMsg==WM_INITDIALOG)
{
// Mark all the children now, before the user's proc gets to them
EnumChildWindows(hdlg, &EnumChildToTagProc, 0);
if(lpfn)
RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
else
RetVal = TRUE;
}
else if(((uiMsg == msgFILEOKSTRING) || (uiMsg == msgSHAREVISTRING)))
{
WCHAR drive[_MAX_DRIVE + 1];
WCHAR dir[_MAX_DIR +1];
WCHAR file[_MAX_FNAME +1];
LPOPENFILENAMEA lpofnA = (LPOPENFILENAMEA)lParam;
OPENFILENAMEW ofn;
LPARAM lParamW;
ALLOCRETURN arCustomFilter = arNoAlloc;
ALLOCRETURN arFile = arNoAlloc;
ALLOCRETURN arFileTitle = arNoAlloc;
// lParam is an LPOPENFILENAMEA to be converted. Copy all the
// members that the user might expect.
ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
arCustomFilter = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrCustomFilter,
lpofnA->nMaxCustFilter,
&ofn.lpstrCustomFilter,
g_acp);
ofn.nMaxCustFilter = gwcslen(ofn.lpstrCustomFilter);
ofn.nFilterIndex = lpofnA->nFilterIndex;
ofn.nMaxFile = lpofnA->nMaxFile * sizeof(WCHAR);
arFile = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFile,
lpofnA->nMaxFile,
&ofn.lpstrFile,
g_acp);
ofn.nMaxFile = gwcslen(ofn.lpstrFile);
arFileTitle = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFileTitle,
lpofnA->nMaxFileTitle,
&ofn.lpstrFileTitle,
g_acp);
ofn.nMaxFileTitle = gwcslen(ofn.lpstrFileTitle);
ofn.Flags = lpofnA->Flags;
// nFileOffset and nFileExtension are to provide info about the
// file name and extension location in lpstrFile, but there is
// no reasonable way to get it from the return so we just recalc
gwsplitpath(ofn.lpstrFile, drive, dir, file, NULL);
ofn.nFileOffset = (gwcslen(drive) + gwcslen(dir));
ofn.nFileExtension = ofn.nFileOffset + gwcslen(file);
lParamW = (LPARAM)&ofn;
if(lpfn)
RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParamW);
// Free up some memory if we allocated any
if(arCustomFilter==arAlloc)
GodotHeapFree(ofn.lpstrCustomFilter);
if(arFile==arAlloc)
GodotHeapFree(ofn.lpstrFile);
if(arFileTitle==arAlloc)
GodotHeapFree(ofn.lpstrFileTitle);
}
else
{
if(lpfn)
RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
}
return(RetVal);
}
/*-------------------------------
// OFNHookHelper
//
// Almost all of the fileopen/save code is shared, and this function shares it!
-------------------------------*/
UINT_PTR OFNHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fOpenFile)
{
UINT_PTR RetVal = 0;
LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);
LPOFNHOOKPROC lpfn;
if(fOpenFile)
lpfn = (lpgti ? lpgti->pfnGetOpenFileName : NULL);
else
lpfn = (lpgti ? lpgti->pfnGetSaveFileName : NULL);
if(uiMsg==WM_INITDIALOG)
{
HWND hwndParent;
// Tag the window as a new style file open dlg
SetNewFileOpenProp(hdlg);
if(hwndParent = GetParent(hdlg))
SetNewFileOpenProp(hwndParent);
// Mark all the children now, before the user's proc gets to them
EnumChildWindows(hdlg, &EnumChildToTagProc, 0);
RetVal = TRUE;
}
if(lpfn)
{
switch(uiMsg)
{
case WM_NOTIFY:
RetVal = OFNotifyHelper(hdlg, lpfn, wParam, lParam);
break;
default:
RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
break;
}
}
return(RetVal);
}
/*-------------------------------
// OSNotifyHelper
-------------------------------*/
UINT_PTR OFNotifyHelper(HWND hdlg, WNDPROC lpfn, WPARAM wParam, LPARAM lParam)
{
UINT_PTR RetVal;
LPOFNOTIFYA lpofnfyA = (LPOFNOTIFYA)lParam;
switch(lpofnfyA->hdr.code)
{
case CDN_FILEOK:
case CDN_FOLDERCHANGE:
case CDN_HELP:
case CDN_INCLUDEITEM:
case CDN_INITDONE:
case CDN_SELCHANGE:
case CDN_SHAREVIOLATION:
case CDN_TYPECHANGE:
{
LPOPENFILENAMEA lpofnA = lpofnfyA->lpOFN;
OFNOTIFYW ofnfy;
OPENFILENAMEW ofn;
WCHAR drive[_MAX_DRIVE];
WCHAR dir[_MAX_DIR];
WCHAR file[_MAX_FNAME];
ALLOCRETURN arCustomFilter = arNoAlloc;
ALLOCRETURN arFile = arNoAlloc;
ALLOCRETURN arFileTitle = arNoAlloc;
ZeroMemory(&ofnfy, sizeof(OFNOTIFYW));
ofnfy.hdr = lpofnfyA->hdr;
ofnfy.lpOFN = &ofn;
ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
ofn.Flags = lpofnA->Flags;
ofn.hwndOwner = lpofnA->hwndOwner;
ofn.hInstance = lpofnA->hInstance;
ofn.lpfnHook = lpfn;
arCustomFilter = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrCustomFilter,
lpofnA->nMaxCustFilter,
&ofn.lpstrCustomFilter,
g_acp);
ofn.nMaxCustFilter = gwcslen(ofn.lpstrCustomFilter);
ofn.nFilterIndex = lpofnA->nFilterIndex;
ofn.nMaxFile = lpofnA->nMaxFile * sizeof(WCHAR);
arFile = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFile,
lpofnA->nMaxFile,
&ofn.lpstrFile,
g_acp);
ofn.nMaxFile = gwcslen(ofn.lpstrFile);
arFileTitle = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFileTitle,
lpofnA->nMaxFileTitle,
&ofn.lpstrFileTitle,
g_acp);
ofn.nMaxFileTitle = gwcslen(ofn.lpstrFileTitle);
ofn.Flags = lpofnA->Flags;
// nFileOffset and nFileExtension are to provide info about the
// file name and extension location in lpstrFile, but there is
// no reasonable way to get it from the return so we just recalc
gwsplitpath(ofn.lpstrFile, drive, dir, file, NULL);
ofn.nFileOffset = (gwcslen(drive) + gwcslen(dir));
ofn.nFileExtension = ofn.nFileOffset + gwcslen(file);
if(ofnfy.hdr.code == CDN_SHAREVIOLATION)
{
WCHAR lpwzFile[MAX_PATH];
if(FSTRING_VALID(lpofnfyA->pszFile))
{
MultiByteToWideChar(g_acp, 0,
lpofnfyA->pszFile, -1,
lpwzFile, MAX_PATH);
ofnfy.pszFile = lpwzFile;
}
RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, (LPARAM)&ofnfy);
}
else
{
RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, (LPARAM)&ofnfy);
}
// Free up some memory if we allocated any
if(arCustomFilter==arAlloc)
GodotHeapFree(ofn.lpstrCustomFilter);
if(arFile==arAlloc)
GodotHeapFree(ofn.lpstrFile);
if(arFileTitle==arAlloc)
GodotHeapFree(ofn.lpstrFileTitle);
break;
}
default:
RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, lParam);
break;
}
return(RetVal);
}
/*-------------------------------
// GenericHookHelper
//
// This function is to assist all the comdlg32 hook functions that serve
// no real purpose that we know of, other than to make sure all the
// child controls get marked as "Unicode" controls, etc.
-------------------------------*/
UINT_PTR GenericHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn)
{
UINT_PTR RetVal = FALSE;
switch(uiMsg)
{
case WM_INITDIALOG:
// Mark all the children now, before the user's proc gets to them
EnumChildWindows(hdlg, &EnumChildToTagProc, 0);
if(lpfn)
RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
else
RetVal = TRUE;
break;
case WM_DESTROY:
case WM_NCDESTROY:
default:
if(lpfn)
RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam);
break;
}
return(RetVal);
}
/*-------------------------------
// UpdateTextAndFlags
//
// Get the text from the control, and update the flags. Use in
// preparation for notifying the owner window that the user has
// done something interesting.
//
// Modified from find.c from the comdlg32 project in the Shell
// depot. It definitely needed its own special spin, though!
//
// chx1 is whether or not to match entire words
// chx2 is whether or not case is relevant
// chx3 is whether or not to wrap scans
-------------------------------*/
VOID UpdateTextAndFlags(HWND hDlg, LPFINDREPLACEW lpfrw, DWORD dwActionFlag, BOOL fFindText)
{
// Only clear flags that this routine sets. The hook and template
// flags should not be anded off here.
lpfrw->Flags &= ~((DWORD)(FR_WHOLEWORD | FR_MATCHCASE | FR_REPLACE |
FR_FINDNEXT | FR_REPLACEALL | FR_DOWN));
if (IsDlgButtonChecked(hDlg, chx1))
lpfrw->Flags |= FR_WHOLEWORD;
if (IsDlgButtonChecked(hDlg, chx2))
lpfrw->Flags |= FR_MATCHCASE;
// Set ACTION flag FR_{REPLACE,FINDNEXT,REPLACEALL}.
lpfrw->Flags |= dwActionFlag;
GodotGetDlgItemTextW(hDlg, edt1, lpfrw->lpstrFindWhat, lpfrw->wFindWhatLen);
if (fFindText)
{
// Assume searching down. Check if UP button is NOT pressed, rather
// than if DOWN button IS. So, if buttons have been hidden or
// disabled, FR_DOWN flag will be set correctly.
if (!IsDlgButtonChecked(hDlg, rad1))
lpfrw->Flags |= FR_DOWN;
}
else
{
GodotGetDlgItemTextW(hDlg, edt2, lpfrw->lpstrReplaceWith, lpfrw->wReplaceWithLen);
lpfrw->Flags |= FR_DOWN;
}
}
/*-------------------------------
// NotifyFindReplaceParent
//
// Let hwndOwner know what is happening, since the user has
// performed some kind of action that they might want to
// know about.
//
// Modified from find.c from the comdlg32 project in the Shell
// depot. It definitely needed its own special spin, though!
-------------------------------*/
LRESULT NotifyFindReplaceParent(LPFINDREPLACEW lpfr, LPFRHOOKPROC lpfrhp, UINT uMsg, BOOL fFindText)
{
LRESULT RetVal;
DWORD Flags;
// Cache the flags setting since we may be mucking with it
Flags = lpfr->Flags;
// Now, muck with the structure a bit
// to hide our hook from the user
if(lpfrhp)
lpfr->lpfnHook = lpfrhp;
else
{
lpfr->lpfnHook = NULL;
Flags &= ~FR_ENABLEHOOK;
}
RetVal = GodotSendMessageW(lpfr->hwndOwner, uMsg, 0, (DWORD_PTR)lpfr);
// Restore the structure to what it was
lpfr->Flags = Flags;
if(fFindText)
lpfr->lpfnHook = &FRHookProcFind;
else
lpfr->lpfnHook = &FRHookProcReplace;
return(RetVal);
}
/*-------------------------------
// IsFontDialog
-------------------------------*/
BOOL IsFontDialog(HWND hdlg)
{
return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == CHOOSEFONT_DIALOG));
}
/*-------------------------------
// IsNewFileOpenDialog
-------------------------------*/
BOOL IsNewFileOpenDialog(HWND hdlg)
{
return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == NEWFILEOPEN_DIALOG));
}
/*-------------------------------
// IsCaptureWindow
-------------------------------*/
BOOL IsCaptureWindow(HWND hdlg)
{
return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == CAPTURE_WINDOW));
}
/*-------------------------------
// RemoveComdlgPropIfPresent
-------------------------------*/
void RemoveComdlgPropIfPresent(HWND hdlg)
{
if(GetPropA(hdlg, m_szComdlgClass) != 0)
RemovePropA(hdlg, m_szComdlgClass);
return;
}
/*-------------------------------
// SetNewFileOpenProp
-------------------------------*/
void SetNewFileOpenProp(HWND hdlg)
{
// We have to aggressively refcount our prop to keep anyone from
// losing it. Thus we do an initial GlobalAddAtom on it and then
// subsequently call SetPropA on it with the same string.
if(!m_aComdlgClass)
m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass);
SetPropA(hdlg, m_szComdlgClass, (HANDLE)NEWFILEOPEN_DIALOG);
return;
}
/*-------------------------------
// SetFontProp
-------------------------------*/
void SetFontProp(HWND hdlg)
{
// We have to aggressively refcount our prop to keep anyone from
// losing it. Thus we do an initial GlobalAddAtom on it and then
// subsequently call SetPropA on it with the same string.
if(!m_aComdlgClass)
m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass);
SetPropA(hdlg, m_szComdlgClass, (HANDLE)CHOOSEFONT_DIALOG);
return;
}
/*-------------------------------
// SetCaptureWindowProp
-------------------------------*/
void SetCaptureWindowProp(HWND hdlg)
{
// We have to aggressively refcount our prop to keep anyone from
// losing it. Thus we do an initial GlobalAddAtom on it and then
// subsequently call SetPropA on it with the same string.
if(!m_aComdlgClass)
m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass);
SetPropA(hdlg, m_szComdlgClass, (HANDLE)CAPTURE_WINDOW);
return;
}