From 59e53cb6cad99051eb3e46b919fd78ab7493061d Mon Sep 17 00:00:00 2001 From: mataes2007 Date: Wed, 23 Nov 2011 18:16:39 +0000 Subject: added Help plugin git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@201 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- Help/dlgboxsubclass.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 Help/dlgboxsubclass.c (limited to 'Help/dlgboxsubclass.c') diff --git a/Help/dlgboxsubclass.c b/Help/dlgboxsubclass.c new file mode 100644 index 0000000..ff0706c --- /dev/null +++ b/Help/dlgboxsubclass.c @@ -0,0 +1,455 @@ +/* +Miranda IM Help Plugin +Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program (Help-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#define _WIN32_WINNT 0x0400 +#define __RPCASYNC_H__ /* header shows warnings in VS6 */ +#include +#include + +#define MIRANDA_VER 0x0600 +#include +#include +#include +#include +#include +#include "help.h" +#include "m_help.h" + +#include "resource.h" + +#define SC_CONTEXTHELP_SEPARATOR SC_SEPARATOR+1 +#define SC_CONTEXTHELP_DIALOG SC_CONTEXTHELP+1 + +#define PROP_CONTEXTSTATE _T("HelpPlugin_ContextState") +#define PROPF_MENUFORCED 0x01 // always show help context menu for ctl (override default) +#define PROPF_MENUDISABLED 0x02 // never show help context menu for ctl +#define PROPF_AUTOTIPFORCED 0x04 // always show autotip for ctl (override default) +#define PROPF_AUTOTIPDISABLED 0x08 // never show autotip for ctl + +extern HINSTANCE hInst; +extern HWND hwndHelpDlg; +extern WORD settingAutoTipDelay; +static HHOOK hMessageHook,hKeyboardHook,hEatNextMouseHook=NULL; +static HANDLE hServiceShowHelp,hServiceSetContext; + +struct DlgBoxSubclassData { + HWND hwndDlg; + WNDPROC pfnOldWndProc; + DWORD flags; +} static *dlgBoxSubclass=NULL; +static int dlgBoxSubclassCount=0; +static CRITICAL_SECTION csDlgBoxSubclass; + +#define DBSDF_MINIMIZABLE 0x01 // WS_MINIMIZEBOX style was set on hooked window +#define DBSDF_MAXIMIZABLE 0x02 // WS_MAXIMIZEBOX style was set on hooked window + +struct FindChildAtPointData { + HWND hwnd; + POINT pt; + int bestArea; +}; + +// ChildWindowFromPoint() messes up with group boxes +static BOOL CALLBACK FindChildAtPointEnumProc(HWND hwnd,LPARAM lParam) +{ + if(IsWindowVisible(hwnd)) { + struct FindChildAtPointData *fcap=(struct FindChildAtPointData*)lParam; + RECT rcVisible,rc,rcParent; + GetWindowRect(hwnd,&rc); + GetWindowRect(GetParent(hwnd),&rcParent); + IntersectRect(&rcVisible,&rcParent,&rc); + if(PtInRect(&rcVisible,fcap->pt)) { + int thisArea=(rc.bottom-rc.top)*(rc.right-rc.left); + if(thisArea && (thisAreabestArea || fcap->bestArea==0)) { + fcap->bestArea=thisArea; + fcap->hwnd=hwnd; + } + } + } + return TRUE; +} + +// IsChild() messes up with owned windows +int IsRealChild(HWND hwndParent,HWND hwnd) +{ + while(hwnd!=NULL) { + if(hwnd==hwndParent) return 1; + if(hwndParent==GetWindow(hwnd,GW_OWNER)) return 0; + hwnd=GetParent(hwnd); + } + return 0; +} + +static BOOL CALLBACK RemovePropForAllChildsEnumProc(HWND hwnd,LPARAM lParam) +{ + RemoveProp(hwnd,(TCHAR*)lParam); + return TRUE; +} + +static HWND hwndMouseMoveDlg=NULL; +static UINT idMouseMoveTimer=0; +static LONG cursorPos=MAKELONG(-1,-1); +static int openedAutoTip=0; +static void CALLBACK NoMouseMoveForDelayTimerProc(HWND hwnd,UINT msg,UINT idTimer,DWORD time) +{ + POINT pt; + HWND hwndCtl; + struct FindChildAtPointData fcap; + UNREFERENCED_PARAMETER(msg); + UNREFERENCED_PARAMETER(time); + + KillTimer(hwnd,idTimer); + if(idMouseMoveTimer!=idTimer) return; + idMouseMoveTimer=0; + if(!settingAutoTipDelay || !IsWindow(hwndMouseMoveDlg)) return; + + ZeroMemory(&fcap,sizeof(fcap)); + if(!GetCursorPos(&pt)) return; + // ChildWindowFromPoint() messes up with group boxes + fcap.hwnd=NULL; + fcap.pt=pt; + EnumChildWindows(hwndMouseMoveDlg,FindChildAtPointEnumProc,(LPARAM)&fcap); + hwndCtl=fcap.hwnd; + if(hwndCtl==NULL) { + ScreenToClient(hwndMouseMoveDlg,&pt); + hwndCtl=ChildWindowFromPointEx(hwndMouseMoveDlg,pt,CWP_SKIPINVISIBLE); + if(hwndCtl==NULL) return; + } + { LONG flags=(LONG)GetProp(hwndCtl,PROP_CONTEXTSTATE); + if(flags&PROPF_AUTOTIPDISABLED) return; + flags=SendMessage(hwndCtl,WM_GETDLGCODE,(WPARAM)VK_RETURN,(LPARAM)NULL); + if(flags&DLGC_HASSETSEL || flags&DLGC_WANTALLKEYS) return; // autotips on edits are annoying + } + { CURSORINFO ci; + BOOL (WINAPI *pfnGetCursorInfo)(CURSORINFO*); + ci.cbSize=sizeof(ci); + *(FARPROC*)&pfnGetCursorInfo=GetProcAddress(GetModuleHandleA("USER32"),"GetCursorInfo"); + if(pfnGetCursorInfo && IsWinVer2000Plus()) // call not safe for WinNT4 + if(pfnGetCursorInfo(&ci) && !(ci.flags&CURSOR_SHOWING)) return; + } + if(IsRealChild(hwndMouseMoveDlg,hwndCtl) && hwndHelpDlg==NULL) { + hwndHelpDlg=CreateDialog(hInst,MAKEINTRESOURCE(IDD_HELP),NULL,HelpDlgProc); + if(hwndHelpDlg==NULL) return; + openedAutoTip=1; + PostMessage(hwndHelpDlg,M_CHANGEHELPCONTROL,0,(LPARAM)hwndCtl); + } +} + +static LRESULT CALLBACK KeyboardInputHookProc(int code,WPARAM wParam,LPARAM lParam) +{ + if(code==HC_ACTION && idMouseMoveTimer!=0) { + KillTimer(NULL,idMouseMoveTimer); + idMouseMoveTimer=0; + } + return CallNextHookEx(hKeyboardHook,code,wParam,lParam); +} + +// workaround for WM_HELP (SC_CONTEXTHELP causes an additional WM_LBUTTONUP when selecting) +static LRESULT CALLBACK EatNextMouseButtonUpHookProc(int code,WPARAM wParam,LPARAM lParam) +{ + if(code>=0 && wParam==WM_LBUTTONUP) { + UnhookWindowsHookEx(hEatNextMouseHook); // unhook ourselves + hEatNextMouseHook=NULL; + return -1; + } + return CallNextHookEx(hEatNextMouseHook,code,wParam,lParam); +} + +static LRESULT CALLBACK DialogBoxSubclassProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam) +{ + WNDPROC pfnWndProc; + DWORD flags; + int i; + + EnterCriticalSection(&csDlgBoxSubclass); + for(i=0;i10) return 0; + + if(idMouseMoveTimer) KillTimer(NULL,idMouseMoveTimer); + idMouseMoveTimer=0; + + ZeroMemory(&fcap,sizeof(fcap)); + POINTSTOPOINT(pt,MAKEPOINTS(lParam)); + // ChildWindowFromPoint() messes up with group boxes + fcap.hwnd=NULL; + fcap.pt=pt; + EnumChildWindows(hwndDlg,FindChildAtPointEnumProc,(LPARAM)&fcap); + hwndCtl=fcap.hwnd; + if(hwndCtl==NULL) { + ScreenToClient(hwndDlg,&pt); + hwndCtl=ChildWindowFromPointEx(hwndDlg,pt,CWP_SKIPINVISIBLE); + if(hwndCtl==NULL) break; + POINTSTOPOINT(pt,MAKEPOINTS(lParam)); + } + { LONG flags=(LONG)GetProp(hwndCtl,PROP_CONTEXTSTATE); + if(flags&PROPF_MENUDISABLED) break; + else if(!(flags&PROPF_MENUFORCED)) { + int type=GetControlType(hwndCtl); + // showing a context menu on these looks silly (multi components) + if(type==CTLTYPE_TOOLBAR || type==CTLTYPE_LISTVIEW || type==CTLTYPE_TREEVIEW || type==CTLTYPE_STATUSBAR || type==CTLTYPE_CLC) + break; + } + } + if(IsRealChild(hwndDlg,hwndCtl)) { + HMENU hMenu=CreatePopupMenu(); + AppendMenu(hMenu,MF_STRING,SC_CONTEXTHELP,(hwndCtl==hwndDlg)?TranslateT("&What's this Dialog?"):TranslateT("&What's this?")); + if(TrackPopupMenuEx(hMenu,TPM_LEFTALIGN|TPM_TOPALIGN|TPM_HORPOSANIMATION|TPM_VERPOSANIMATION|TPM_RIGHTBUTTON|TPM_RETURNCMD|TPM_NONOTIFY,pt.x,pt.y,hwndDlg,NULL)) { + if(hwndHelpDlg==NULL) { + hwndHelpDlg=CreateDialog(hInst,MAKEINTRESOURCE(IDD_HELP),NULL,HelpDlgProc); + if(hwndHelpDlg==NULL) { + DestroyMenu(hMenu); + break; + } + } + SendMessage(hwndHelpDlg,M_CHANGEHELPCONTROL,0,(LPARAM)hwndCtl); + } + DestroyMenu(hMenu); + } + return 0; + } + case WM_HELP: + { HELPINFO *hi=(HELPINFO*)lParam; + if(hi->iContextType!=HELPINFO_WINDOW) break; + // fix for SHBrowseForFolder() dialog, which sends unhandled help to parent + if(!IsRealChild(hwndDlg,hi->hItemHandle)) break; + + if(idMouseMoveTimer) KillTimer(NULL,idMouseMoveTimer); + idMouseMoveTimer=0; + + if(!IsWindow(hwndHelpDlg)) { + hwndHelpDlg=CreateDialog(hInst,MAKEINTRESOURCE(IDD_HELP),NULL,HelpDlgProc); + if(hwndHelpDlg==NULL) break; + } + SendMessage(hwndHelpDlg,M_CHANGEHELPCONTROL,0,(LPARAM)hi->hItemHandle); + // we need to eat the next WM_LBUTTONDOWN (if invoked by mouse) + if(GetKeyState(GetSystemMetrics(SM_SWAPBUTTON)?VK_RBUTTON:VK_LBUTTON)&0x8000 && hEatNextMouseHook==NULL) + hEatNextMouseHook=SetWindowsHookEx(WH_MOUSE,EatNextMouseButtonUpHookProc,NULL,GetCurrentThreadId()); + return TRUE; + } + case WM_NCDESTROY: + if(idMouseMoveTimer) KillTimer(NULL,idMouseMoveTimer); + idMouseMoveTimer=0; + EnumChildWindows(hwndDlg,RemovePropForAllChildsEnumProc,(LPARAM)PROP_CONTEXTSTATE); + { TCHAR text[64]; + mir_sntprintf(text,SIZEOF(text),_T("unhooked window 0x%X for context help\n"),hwndDlg); + OutputDebugString(text); + } + SetWindowLong(hwndDlg,GWL_WNDPROC,(LONG)pfnWndProc); + break; + } + return CallWindowProc(pfnWndProc,hwndDlg,msg,wParam,lParam); +} + +static LRESULT CALLBACK HelpSendMessageHookProc(int code,WPARAM wParam,LPARAM lParam) +{ + if(code>=0) { + CWPSTRUCT *msg=(CWPSTRUCT*)lParam; + switch(msg->message) { + case WM_INITDIALOG: // dialogs and message boxes + if(GetClassLong(msg->hwnd,GCW_ATOM)!=32770) // class="#32770" + break; + if(msg->hwnd==hwndHelpDlg || (DLGPROC)GetWindowLong(msg->hwnd,DWL_DLGPROC)==HelpDlgProc) + break; +#ifndef EDITOR + if((DLGPROC)GetWindowLong(msg->hwnd,DWL_DLGPROC)==ShadowDlgProc) break; +#endif + { DWORD style,exStyle; + struct DlgBoxSubclassData *buf; + + exStyle=GetWindowLong(msg->hwnd,GWL_EXSTYLE); + if(exStyle&WS_EX_CONTEXTHELP) break; + style=GetWindowLong(msg->hwnd,GWL_STYLE); + + EnterCriticalSection(&csDlgBoxSubclass); + buf=(struct DlgBoxSubclassData*)mir_realloc(dlgBoxSubclass,sizeof(struct DlgBoxSubclassData)*(dlgBoxSubclassCount+1)); + if(buf==NULL) { + LeaveCriticalSection(&csDlgBoxSubclass); + break; + } + dlgBoxSubclass=buf; + dlgBoxSubclass[dlgBoxSubclassCount].hwndDlg=msg->hwnd; + dlgBoxSubclass[dlgBoxSubclassCount].pfnOldWndProc=(WNDPROC)SetWindowLong(msg->hwnd,GWL_WNDPROC,(LONG)DialogBoxSubclassProc); + dlgBoxSubclass[dlgBoxSubclassCount].flags=0; + + // WS_EX_CONTEXTHELP cannot be used in conjunction WS_MINIMIZEBOX or WS_MAXIMIZEBOX + // solution: switch off WS_MINIMIZEBOX or WS_MAXIMIZEBOX when only one of them is present + if(!(style&WS_MINIMIZEBOX) || !(style&WS_MAXIMIZEBOX)) { + if(style&WS_MINIMIZEBOX) dlgBoxSubclass[dlgBoxSubclassCount].flags|=DBSDF_MINIMIZABLE; + if(style&WS_MAXIMIZEBOX) dlgBoxSubclass[dlgBoxSubclassCount].flags|=DBSDF_MAXIMIZABLE; + SetWindowLong(msg->hwnd,GWL_STYLE,style&(~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX))); + SetWindowLong(msg->hwnd,GWL_EXSTYLE,exStyle|WS_EX_CONTEXTHELP); + } + dlgBoxSubclassCount++; + LeaveCriticalSection(&csDlgBoxSubclass); + } + { HMENU hMenu; + hMenu=GetSystemMenu(msg->hwnd,FALSE); + if(hMenu!=NULL && AppendMenu(hMenu,MF_SEPARATOR,SC_CONTEXTHELP_SEPARATOR,NULL)) { + AppendMenu(hMenu,MF_STRING,SC_CONTEXTHELP,TranslateT("&What's this?")); + AppendMenu(hMenu,MF_STRING,SC_CONTEXTHELP_DIALOG,TranslateT("&What's this Dialog?")); + } + } + { TCHAR text[64]; + mir_sntprintf(text,SIZEOF(text),_T("hooked window 0x%X for context help\n"),msg->hwnd); + OutputDebugString(text); + } + break; + } + } + return CallNextHookEx(hMessageHook,code,wParam,lParam); +} + +static int ServiceShowHelp(WPARAM wParam,LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + if(!IsWindow((HWND)wParam)) return 1; + if(idMouseMoveTimer) KillTimer(NULL,idMouseMoveTimer); + idMouseMoveTimer=0; + if(hwndHelpDlg==NULL) { + hwndHelpDlg=CreateDialog(hInst,MAKEINTRESOURCE(IDD_HELP),NULL,HelpDlgProc); + if(hwndHelpDlg==NULL) return 2; + } + PostMessage(hwndHelpDlg,M_CHANGEHELPCONTROL,0,(LPARAM)wParam); + return 0; +} + +static int ServiceSetContextState(WPARAM wParam,LPARAM lParam) +{ + int i; + LONG flags; + EnterCriticalSection(&csDlgBoxSubclass); + for(i=0;i