/* 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 #include #include #define _WIN32_WINNT 0x0500 #define __RPCASYNC_H__ /* header shows warnings in VS6 */ #include #include #define MIRANDA_VER 0x0600 #include #include #include #include #include "help.h" #pragma warning(disable:4201) /* nonstandard extension used : nameless struct/union */ #include #pragma warning(default:4201) /* nonstandard extension used : nameless struct/union */ #include #include #include #define CHARBUFFER_ALLOCSTEP 512 // allocated buffer increase in bytes #ifdef EDITOR extern const TCHAR *szControlTypeNames[]={ _T("Unknown"),_T("Dialog Box"),_T("Button"),_T("Check Box"),_T("Radio Button"), _T("Text"),_T("Image"),_T("Edit Box"),_T("Group Box"),_T("Combo Box"), _T("List Box"),_T("Spin Edit Box"),_T("Progress Bar"),_T("Slider"),_T("List View"), _T("Tree View"),_T("Date/Time Picker"),_T("IP Address"),_T("Status Bar"),_T("Hyperlink"), _T("Contact List"),_T("Scroll Bar"),_T("Animation"),_T("Hotkey"),_T("Tabs"), _T("Colour Picker"),_T("Tool Bar"),_T("Combo Edit Box"),_T("Size Grip")}; #endif int GetControlType(HWND hwndCtl) { TCHAR szClassName[32]; DWORD style; if(GetClassLong(hwndCtl,GCW_ATOM)==32770) return CTLTYPE_DIALOG; if(!GetClassName(hwndCtl,szClassName,SIZEOF(szClassName))) return CTLTYPE_UNKNOWN; if(!lstrcmpi(szClassName,_T("MDIClient"))) return CTLTYPE_DIALOG; else if(!lstrcmpi(szClassName,_T("Static"))) { if(GetClassName(GetParent(hwndCtl),szClassName,SIZEOF(szClassName)) && !lstrcmpi(szClassName,_T("ComboBox")) || !lstrcmpi(szClassName,WC_COMBOBOXEX)) return CTLTYPE_COMBO; style=GetWindowLong(hwndCtl,GWL_STYLE); switch(style&SS_TYPEMASK) { case SS_BITMAP: case SS_BLACKFRAME: case SS_BLACKRECT: case SS_ENHMETAFILE: case SS_ETCHEDFRAME: case SS_ETCHEDHORZ: case SS_ETCHEDVERT: case SS_WHITEFRAME: case SS_WHITERECT: case SS_GRAYFRAME: case SS_GRAYRECT: case SS_ICON: case SS_OWNERDRAW: return CTLTYPE_IMAGE; } return CTLTYPE_TEXT; } else if(GetClassLong(hwndCtl,GCW_ATOM)==32772) // WinNT/2000/XP: icon titles return CTLTYPE_IMAGE; // class="#32772" else if(!lstrcmpi(szClassName,_T("Button"))) { style=GetWindowLong(hwndCtl,GWL_STYLE); switch(style&0x24) { case BS_CHECKBOX: case BS_AUTOCHECKBOX: case BS_3STATE: case BS_AUTO3STATE: if(style&BS_PUSHLIKE) break; return CTLTYPE_CHECKBOX; case BS_RADIOBUTTON: case BS_AUTORADIOBUTTON: if(style&BS_PUSHLIKE) break; return CTLTYPE_RADIO; case BS_GROUPBOX: return CTLTYPE_GROUP; } return CTLTYPE_BUTTON; } else if(!lstrcmpi(szClassName,MIRANDABUTTONCLASS)) return CTLTYPE_BUTTON; else if(!lstrcmpi(szClassName,_T("Edit"))) { if(GetClassName(GetParent(hwndCtl),szClassName,SIZEOF(szClassName)) && !lstrcmpi(szClassName,_T("ComboBox"))) return CTLTYPE_COMBO; if(GetClassName(GetWindow(hwndCtl,GW_HWNDNEXT),szClassName,SIZEOF(szClassName)) && !lstrcmpi(szClassName,UPDOWN_CLASS)) if((HWND)SendMessage(GetWindow(hwndCtl,GW_HWNDNEXT),UDM_GETBUDDY,0,0)==hwndCtl) return CTLTYPE_SPINEDIT; return CTLTYPE_EDIT; } else if(!_tcsnicmp(szClassName,_T("RichEdit"),8)) return CTLTYPE_EDIT; // RICHEDIT,RichEdit20A,RichEdit20W,RichEdit50W and future versions else if(!lstrcmpi(szClassName,_T("ListBox"))) { style=GetWindowLong(hwndCtl,GWL_STYLE); if(style&LBS_COMBOBOX) return CTLTYPE_COMBO; return CTLTYPE_LIST; } else if(!lstrcmpi(szClassName,_T("ComboLBox")) || !lstrcmpi(szClassName,_T("ComboBox")) || !lstrcmpi(szClassName,WC_COMBOBOXEX)) return CTLTYPE_COMBO; else if(!lstrcmpi(szClassName,_T("ScrollBar"))) { style=GetWindowLong(hwndCtl,GWL_STYLE); if(style&SBS_SIZEBOX) return CTLTYPE_SIZEGRIP; return CTLTYPE_SCROLL; } else if(!lstrcmpi(szClassName,WC_PAGESCROLLER)) return CTLTYPE_SCROLL; else if(!lstrcmpi(szClassName,UPDOWN_CLASS)) { if(GetClassName((HWND)SendMessage(hwndCtl,UDM_GETBUDDY,0,0),szClassName,SIZEOF(szClassName)) && !lstrcmpi(szClassName,_T("Edit"))) return CTLTYPE_SPINEDIT; return CTLTYPE_SCROLL; } else if(!lstrcmpi(szClassName,PROGRESS_CLASS)) return CTLTYPE_PROGRESS; else if(!lstrcmpi(szClassName,TRACKBAR_CLASS)) return CTLTYPE_SLIDER; else if(!lstrcmpi(szClassName,WC_LISTVIEW) || !lstrcmpi(szClassName,WC_HEADER)) return CTLTYPE_LISTVIEW; else if(!lstrcmpi(szClassName,WC_TREEVIEW)) return CTLTYPE_TREEVIEW; else if(!lstrcmpi(szClassName,DATETIMEPICK_CLASS) || !lstrcmpi(szClassName,MONTHCAL_CLASS)) return CTLTYPE_DATETIME; else if(!lstrcmpi(szClassName,WC_IPADDRESS)) return CTLTYPE_IP; else if(!lstrcmpi(szClassName,STATUSCLASSNAME)) return CTLTYPE_STATUSBAR; else if(!lstrcmpi(szClassName,CLISTCONTROL_CLASS)) return CTLTYPE_CLC; else if(!lstrcmpi(szClassName,WNDCLASS_HYPERLINK) || !lstrcmpi(szClassName,_T("SysLink"))) return CTLTYPE_HYPERLINK; else if(!lstrcmpi(szClassName,ANIMATE_CLASS)) return CTLTYPE_ANIMATION; else if(!lstrcmpi(szClassName,HOTKEY_CLASS)) return CTLTYPE_HOTKEY; else if(!lstrcmpi(szClassName,WC_TABCONTROL)) return CTLTYPE_TABS; else if(!lstrcmpi(szClassName,WNDCLASS_COLOURPICKER)) return CTLTYPE_COLOUR; else if(!lstrcmpi(szClassName,TOOLBARCLASSNAME) || !lstrcmpi(szClassName,REBARCLASSNAME)) return CTLTYPE_TOOLBAR; switch(SendMessage(hwndCtl,WM_GETDLGCODE,0,(LPARAM)NULL)&0x2070) { case DLGC_RADIOBUTTON: return CTLTYPE_RADIO; case DLGC_DEFPUSHBUTTON: case DLGC_UNDEFPUSHBUTTON: case DLGC_BUTTON: return CTLTYPE_BUTTON; } hwndCtl=GetWindow(hwndCtl,GW_CHILD); // check for owner-extended control if(hwndCtl!=NULL) return GetControlType(hwndCtl); return CTLTYPE_UNKNOWN; } HWND GetControlDialog(HWND hwndCtl) { TCHAR szClassName[32]; while(hwndCtl!=NULL) { if(GetClassLong(hwndCtl,GCW_ATOM)==32770) return hwndCtl; if(GetClassName(hwndCtl,szClassName,SIZEOF(szClassName))) if(!lstrcmpi(szClassName,_T("MDIClient"))) return hwndCtl; hwndCtl=GetParent(hwndCtl); } return hwndCtl; } // never fails int GetControlTitle(HWND hwndCtl,TCHAR *pszTitle,int cchTitle) { TCHAR *p; int res=0; if(cchTitle) pszTitle[0]=_T('\0'); switch(GetControlType(hwndCtl)) { case CTLTYPE_DIALOG: case CTLTYPE_BUTTON: case CTLTYPE_CHECKBOX: case CTLTYPE_RADIO: case CTLTYPE_GROUP: case CTLTYPE_TEXT: case CTLTYPE_HYPERLINK: res=GetWindowText(hwndCtl,pszTitle,cchTitle); } hwndCtl=GetWindow(hwndCtl,GW_HWNDPREV); if(hwndCtl) switch(GetControlType(hwndCtl)) { case CTLTYPE_TEXT: case CTLTYPE_GROUP: res=GetWindowText(hwndCtl,pszTitle,cchTitle); } if(res) for(p=pszTitle;*p!=0;p++) { // strip-off ampersand (&) prefix character if(*p==_T('&') && *(p+1)!=_T('&')) { MoveMemory(p,p+1,(lstrlen(p+1)+1)*sizeof(TCHAR)); res--; if(*(p+1)==0) break; } // strip-off last ':' if(*p==_T(':') && *(p+1)==0) { *p=0; res--; break; } } return res; } // mir_free() the return value char *GetControlModuleName(HWND hwndCtl) { char szModule[512],szMainModule[512]; char *pszFile,*buf; if(!GetModuleFileNameA(NULL,szMainModule,sizeof(szMainModule))) return NULL; buf=strrchr(szMainModule,'\\'); if(buf!=NULL) *buf='\0'; else buf=szMainModule; do { if(!GetModuleFileNameA((HINSTANCE)GetWindowLong(hwndCtl,GWL_HINSTANCE),szModule,sizeof(szModule))) return NULL; pszFile=strrchr(szModule,'\\'); if(pszFile!=NULL) { *pszFile='\0'; pszFile++; } else pszFile=szModule; if(lstrlenA(szModule)>lstrlenA(szMainModule)) szModule[lstrlenA(szMainModule)]='\0'; if(!lstrcmpiA(szModule,szMainModule)) break; // found miranda module hwndCtl=GetParent(hwndCtl); } while(hwndCtl!=NULL); buf=strrchr(pszFile,'.'); if(buf!=NULL) *buf++='\0'; return mir_strdup(pszFile); } struct CreateControlIdData { int id; HWND hwndCtl; }; static BOOL CALLBACK CreateCtlIdEnumProc(HWND hwnd,LPARAM lParam) { struct CreateControlIdData* ccid=(struct CreateControlIdData*)lParam; TCHAR szClassName[32]; if(GetClassLong(hwnd,GCW_ATOM)==32770) // class="#32770" return TRUE; if(GetClassName(hwnd,szClassName,SIZEOF(szClassName))) if(!lstrcmpi(szClassName,_T("MDIClient"))) return TRUE; if(GetWindowLong(hwnd,GWL_ID)<=0 || GetWindowLong(hwnd,GWL_ID)==0xFFFF) ccid->id--; if(hwnd==ccid->hwndCtl) ccid->hwndCtl=NULL; return ccid->hwndCtl!=NULL; } int GetControlID(HWND hwndCtl) { struct CreateControlIdData ccid; TCHAR szClassName[32]; // obey context ID when set (rarely) ccid.id=GetWindowContextHelpId(hwndCtl); if(ccid.id!=0) return ccid.id; if(GetClassName(hwndCtl,szClassName,SIZEOF(szClassName))) { if(!lstrcmpi(szClassName,UPDOWN_CLASS)) { // handle spinner controls as a whole DWORD style; HWND hwndBuddy; style=GetWindowLong(hwndCtl,GWL_STYLE); if(style&UDS_ALIGNRIGHT || style&UDS_ALIGNLEFT) { hwndBuddy=(HWND)SendMessage(hwndCtl,UDM_GETBUDDY,0,0); if(hwndBuddy!=NULL) hwndCtl=hwndBuddy; } } else if(GetClassLong(hwndCtl,GCW_ATOM)==32770 || !lstrcmpi(szClassName,_T("MDIClient"))) return 0; // ensure this is always unset } ccid.id=GetWindowLong(hwndCtl,GWL_ID); if(ccid.id<=0 || ccid.id==0xFFFF) { ccid.id=-1; ccid.hwndCtl=hwndCtl; EnumChildWindows(GetParent(hwndCtl),CreateCtlIdEnumProc,(LPARAM)&ccid); if(ccid.hwndCtl!=NULL) return -1; } return ccid.id; } // mir_free() the return value static char *Base64Encode(PBYTE pBuf,int cbBuf) { NETLIBBASE64 nlb64; nlb64.pbDecoded=pBuf; nlb64.cbDecoded=cbBuf; nlb64.cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded); nlb64.pszEncoded=(char*)mir_alloc(nlb64.cchEncoded); if(nlb64.pszEncoded==NULL || !CallService(MS_NETLIB_BASE64ENCODE,0,(LPARAM)&nlb64)) { mir_free(nlb64.pszEncoded); /* does NULL check */ return NULL; } return nlb64.pszEncoded; } struct CreateDialogIdBinaryData { int alloced,count; PBYTE buf; HWND hwndParent; }; static BOOL CALLBACK CreateDlgIdBinEnumProc(HWND hwnd,LPARAM lParam) { struct CreateDialogIdBinaryData *cdib=(struct CreateDialogIdBinaryData*)lParam; int type; if(GetParent(hwnd)!=cdib->hwndParent) return TRUE; type=GetControlType(hwnd); if(type==CTLTYPE_DIALOG || type==CTLTYPE_TEXT || type==CTLTYPE_GROUP) return TRUE; if(cdib->count+3>cdib->alloced) { PBYTE buf2; buf2=(PBYTE)mir_realloc(cdib->buf,cdib->alloced+32); if(buf2==NULL) return FALSE; cdib->alloced+=32; cdib->buf=buf2; } cdib->buf[cdib->count]=(BYTE)type; *(PWORD)(cdib->buf+cdib->count+1)=(WORD)GetWindowLong(hwnd,GWL_ID); cdib->count+=3; return TRUE; } // mir_free() the return value char *CreateDialogIdString(HWND hwndDlg) { struct CreateDialogIdBinaryData cdib; char *szRet; ZeroMemory(&cdib, sizeof(cdib)); if(hwndDlg==NULL) return NULL; cdib.hwndParent=hwndDlg; EnumChildWindows(hwndDlg,CreateDlgIdBinEnumProc,(LPARAM)&cdib); if(cdib.buf==NULL) return NULL; szRet=Base64Encode(cdib.buf,cdib.count); mir_free(cdib.buf); return szRet; } void AppendCharToCharBuffer(struct ResizableCharBuffer *rcb,char c) { if(rcb->cbAlloced<=rcb->iEnd+1) { char* buf=(char*)mir_realloc(rcb->sz,(rcb->cbAlloced+CHARBUFFER_ALLOCSTEP)); if(buf==NULL) return; rcb->sz=buf; rcb->cbAlloced+=CHARBUFFER_ALLOCSTEP; } rcb->sz[rcb->iEnd++]=c; rcb->sz[rcb->iEnd]='\0'; } void AppendToCharBuffer(struct ResizableCharBuffer *rcb,const char *fmt,...) { va_list va; int charsDone; char *buf; if(rcb->cbAlloced==0) { buf=(char*)mir_alloc(CHARBUFFER_ALLOCSTEP); if(buf==NULL) return; rcb->sz=buf; rcb->cbAlloced=CHARBUFFER_ALLOCSTEP; } va_start(va,fmt); for(;;) { charsDone=mir_vsnprintf(rcb->sz+rcb->iEnd,rcb->cbAlloced-rcb->iEnd,fmt,va); if(charsDone>=0) break; // returns -1 when buffer not large enough buf=(char*)mir_realloc(rcb->sz,rcb->cbAlloced+CHARBUFFER_ALLOCSTEP); if(buf==NULL) { charsDone=0; break; } rcb->sz=buf; rcb->cbAlloced+=CHARBUFFER_ALLOCSTEP; } va_end(va); rcb->iEnd+=charsDone; }