956 lines
32 KiB
C
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;
|
|
}
|
|
|