/* 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 "stdafx.h" #include #define CHARBUFFER_ALLOCSTEP 512 // allocated buffer increase in bytes #ifdef EDITOR extern const TCHAR *szControlTypeNames[] = { L"Unknown", L"Dialog Box", L"Button", L"Check Box", L"Radio Button", L"Text", L"Image", L"Edit Box", L"Group Box", L"Combo Box", L"List Box", L"Spin Edit Box", L"Progress Bar", L"Slider", L"List View", L"Tree View", L"Date/Time Picker", L"IP Address", L"Status Bar", L"Hyperlink", L"Contact List", L"Scroll Bar", L"Animation", L"Hotkey", L"Tabs", L"Colour Picker", L"Tool Bar", L"Combo Edit Box", L"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, _countof(szClassName))) return CTLTYPE_UNKNOWN; if (!lstrcmpi(szClassName, L"MDIClient")) return CTLTYPE_DIALOG; else if (!lstrcmpi(szClassName, L"Static")) { if (GetClassName(GetParent(hwndCtl), szClassName, _countof(szClassName)) && !lstrcmpi(szClassName, L"ComboBox") || !lstrcmpi(szClassName, WC_COMBOBOXEX)) return CTLTYPE_COMBO; style = GetWindowLongPtr(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, L"Button")) { style = GetWindowLongPtr(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, L"Edit")) { if (GetClassName(GetParent(hwndCtl), szClassName, _countof(szClassName)) && !lstrcmpi(szClassName, L"ComboBox")) return CTLTYPE_COMBO; if (GetClassName(GetWindow(hwndCtl, GW_HWNDNEXT), szClassName, _countof(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, L"RichEdit", 8)) return CTLTYPE_EDIT; // RICHEDIT,RichEdit20A,RichEdit20W,RichEdit50W and future versions else if (!lstrcmpi(szClassName, L"ListBox")) { style = GetWindowLongPtr(hwndCtl, GWL_STYLE); if (style&LBS_COMBOBOX) return CTLTYPE_COMBO; return CTLTYPE_LIST; } else if (!lstrcmpi(szClassName, L"ComboLBox") || !lstrcmpi(szClassName, L"ComboBox") || !lstrcmpi(szClassName, WC_COMBOBOXEX)) return CTLTYPE_COMBO; else if (!lstrcmpi(szClassName, L"ScrollBar")) { style = GetWindowLongPtr(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, _countof(szClassName)) && !lstrcmpi(szClassName, L"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, (LPCWSTR)CLISTCONTROL_CLASS)) return CTLTYPE_CLC; // look at !! casting else if (!lstrcmpi(szClassName, WNDCLASS_HYPERLINK) || !lstrcmpi(szClassName, L"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, (LPCWSTR)WNDCLASS_COLOURPICKER)) return CTLTYPE_COLOUR; // look at !! casting 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, _countof(szClassName))) if (!lstrcmpi(szClassName, L"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)GetWindowLongPtr(hwndCtl, GWLP_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, _countof(szClassName))) if (!lstrcmpi(szClassName, L"MDIClient")) return TRUE; if (GetWindowLongPtr(hwnd, GWL_ID) <= 0 || GetWindowLongPtr(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, _countof(szClassName))) { if (!lstrcmpi(szClassName, UPDOWN_CLASS)) { // handle spinner controls as a whole DWORD style; HWND hwndBuddy; style = GetWindowLongPtr(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, L"MDIClient")) return 0; // ensure this is always unset } ccid.id = GetWindowLongPtr(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;*/ return (char*)mir_base64_encode(pBuf, cbBuf); } 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)GetWindowLongPtr(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; }