/* 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" extern HINSTANCE hInst; HWND hwndHelpDlg; static int HelpDialogResize(HWND, LPARAM, UTILRESIZECONTROL *urc) { switch (urc->wId) { case IDC_CTLTEXT: #ifdef EDITOR case IDC_DLGID: case IDC_MODULE: #endif return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; case IDC_TEXT: return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; } return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; } #ifndef EDITOR INT_PTR CALLBACK ShadowDlgProc(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { BOOL(WINAPI *pfnSetLayeredWindowAttributes)(HWND, COLORREF, BYTE, DWORD); *(FARPROC*)&pfnSetLayeredWindowAttributes = GetProcAddress(GetModuleHandleA("USER32"), "SetLayeredWindowAttributes"); if (!pfnSetLayeredWindowAttributes) { *(HANDLE*)lParam = NULL; // hwndShadowDlg reset DestroyWindow(hwndDlg); return FALSE; } EnableWindow(hwndDlg, FALSE); SetWindowLongPtr(hwndDlg, GWL_EXSTYLE, GetWindowLongPtr(hwndDlg, GWL_EXSTYLE) | WS_EX_LAYERED); pfnSetLayeredWindowAttributes(hwndDlg, RGB(0, 0, 0), 96, LWA_ALPHA); return FALSE; } case WM_CTLCOLORDLG: return (INT_PTR)GetSysColorBrush(COLOR_WINDOWFRAME); } return FALSE; } // in client coordinates int GetCharRangeRect(HWND hwndEdit, LONG *cpMin, LONG cpMax, RECT *rcRange) { LONG cpLineBreak; LONG nLine, nLinePrev; if (*cpMin>cpMax) return 1; nLine = SendMessage(hwndEdit, EM_EXLINEFROMCHAR, 0, *cpMin); for (cpLineBreak = *cpMin + 1; cpLineBreak <= cpMax; cpLineBreak++) { nLinePrev = nLine; nLine = SendMessage(hwndEdit, EM_EXLINEFROMCHAR, 0, cpLineBreak); if (nLine != nLinePrev) break; } cpMax = cpLineBreak - 1; POINTL pt; if (SendMessage(hwndEdit, EM_SETTYPOGRAPHYOPTIONS, 0, 0)) { // test for richedit v3.0 SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)&pt, *cpMin); rcRange->left = pt.x; rcRange->top = pt.y; SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)&pt, cpMax); rcRange->right = pt.x; rcRange->bottom = pt.y; } else { DWORD pos; pos = SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)*cpMin, 0); POINTSTOPOINT(pt, MAKEPOINTS(pos)); rcRange->left = pt.x; rcRange->top = pt.y; pos = SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)cpMax, 0); POINTSTOPOINT(pt, MAKEPOINTS(pos)); rcRange->right = pt.x; rcRange->bottom = pt.y; } FORMATRANGE fr; ZeroMemory(&fr, sizeof(fr)); fr.chrg.cpMin = *cpMin; fr.chrg.cpMax = cpMax; fr.hdc = fr.hdcTarget = GetDC(hwndEdit); if (fr.hdc == NULL) return 1; SendMessage(hwndEdit, EM_FORMATRANGE, 0, (LPARAM)&fr); PostMessage(hwndEdit, EM_FORMATRANGE, 0, 0); // clear memory rcRange->bottom += MulDiv(fr.rc.bottom, GetDeviceCaps(fr.hdc, LOGPIXELSY), 1440); // twips to pixels ReleaseDC(hwndEdit, fr.hdc); *cpMin = cpLineBreak; return 0; } static HCURSOR hHandCursor; static LRESULT CALLBACK HelpSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_KEYDOWN: if (GetKeyState(VK_CONTROL) & 0x8000 && wParam == 'C') { SendMessage(GetParent(hwnd), M_CLIPBOARDCOPY, 0, 0); return 0; } break; case WM_LBUTTONDBLCLK: DestroyWindow(GetParent(hwnd)); return 0; case WM_CONTEXTMENU: return DefWindowProc(hwnd, msg, wParam, lParam); // redirect to parent case WM_SETCURSOR: // not available via EN_MSGFILTER if (GetDlgCtrlID((HWND)wParam) == IDC_TEXT) { POINT pt; DWORD pos; CHARRANGE rng; pos = GetMessagePos(); POINTSTOPOINT(pt, MAKEPOINTS(pos)); ScreenToClient((HWND)wParam, &pt); pos = SendMessage((HWND)wParam, EM_CHARFROMPOS, 0, (WPARAM)&pt); if (IsHyperlink(pos, &rng.cpMin, &rng.cpMax, NULL)) { RECT rc; while (!GetCharRangeRect((HWND)wParam, &rng.cpMin, rng.cpMax, &rc)) if (PtInRect(&rc, pt)) { SetCursor(hHandCursor); return TRUE; } } } break; } return CallWindowProc((WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA), hwnd, msg, wParam, lParam); } #endif // !defined EDITOR INT_PTR CALLBACK HelpDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { HWND hwndCtl = (HWND)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); static LCID locale; #ifndef EDITOR static HWND hwndShadowDlg; static HWND hwndToolTip; #endif switch (msg) { case WM_INITDIALOG: hwndHelpDlg = hwndDlg; #ifdef EDITOR TranslateDialogDefault(hwndDlg); SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DLGID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DLGID), GWL_STYLE) | SS_ENDELLIPSIS); SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEVENTMASK, 0, ENM_KEYEVENTS); { RECT rcDlg, rcWork; if (SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, FALSE) && GetWindowRect(hwndDlg, &rcDlg)) SetWindowPos(hwndDlg, 0, rcDlg.left, rcWork.bottom - rcDlg.bottom + rcDlg.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } #else { RECT rc, rcBuf; SendDlgItemMessage(hwndDlg, IDC_CTLTEXT, EM_GETRECT, 0, (LPARAM)&rcBuf); SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETRECT, 0, (LPARAM)&rc); rc.left = rcBuf.left; // sync richedit offset with edit rc.right = rcBuf.right; SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETRECTNP, 0, (LPARAM)&rc); } SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWLP_WNDPROC, (LONG_PTR)HelpSubclassProc)); SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CARETSUCKER), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_CARETSUCKER), GWLP_WNDPROC, (LONG_PTR)HelpSubclassProc)); SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_MOUSEEVENTS | ENM_REQUESTRESIZE); SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_INFOBK)); SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR); SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_TEXT), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_TEXT), GWLP_WNDPROC, (LONG_PTR)HelpSubclassProc)); hwndShadowDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SHADOW), hwndDlg, ShadowDlgProc, (LPARAM)&hwndShadowDlg); hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, NULL, hInst, NULL); if (hwndToolTip != NULL) { SetWindowPos(hwndToolTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); SendMessage(hwndToolTip, TTM_SETTIPBKCOLOR, GetSysColor(COLOR_WINDOW), 0); // yelleow on yellow looks silly SendMessage(hwndToolTip, TTM_SETDELAYTIME, TTDT_AUTOMATIC, GetDoubleClickTime() * 3); } hHandCursor = (HCURSOR)LoadImage(NULL, IDC_HAND, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); if (hHandCursor == NULL) // use fallback out of miranda32.exe hHandCursor = (HCURSOR)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(214), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); #endif SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETLANGOPTIONS, 0, SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETLANGOPTIONS, 0, 0) | IMF_UIFONTS); SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); return TRUE; case WM_SIZE: { Utils_ResizeDialog(hwndDlg, hInst, MAKEINTRESOURCEA(IDD_HELP), HelpDialogResize); InvalidateRect(hwndDlg, NULL, TRUE); #ifdef EDITOR break; #endif } #ifndef EDITOR case WM_MOVE: if (IsWindow(hwndShadowDlg)) { RECT rc; HRGN hRgnShadow, hRgnDlg; if (!GetWindowRect(hwndDlg, &rc)) break; hRgnShadow = CreateRectRgnIndirect(&rc); if (hRgnShadow == NULL) break; OffsetRgn(hRgnShadow, 5, 5); hRgnDlg = CreateRectRgnIndirect(&rc); if (hRgnDlg == NULL) break; if (CombineRgn(hRgnShadow, hRgnShadow, hRgnDlg, RGN_DIFF) == ERROR) { DeleteObject(hRgnShadow); DeleteObject(hRgnDlg); break; } DeleteObject(hRgnDlg); OffsetRgn(hRgnShadow, -rc.left - 5, -rc.top - 5); SetWindowRgn(hwndShadowDlg, hRgnShadow, FALSE); // system gets ownership of hRgnShadow SetWindowPos(hwndShadowDlg, HWND_TOPMOST, rc.left + 5, rc.top + 5, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW); SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } break; case WM_KEYDOWN: if (GetKeyState(VK_CONTROL) & 0x8000 && wParam == 'C') { SendMessage(hwndDlg, M_CLIPBOARDCOPY, 0, 0); return TRUE; } break; case WM_CONTEXTMENU: { POINT pt; POINTSTOPOINT(pt, MAKEPOINTS(lParam)); HMENU hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING, WM_COPY, TranslateT("&Copy")); if (TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_HORPOSANIMATION | TPM_VERPOSANIMATION | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, hwndDlg, NULL)) SendMessage(hwndDlg, M_CLIPBOARDCOPY, 0, 0); DestroyMenu(hMenu); } return 0; case WM_LBUTTONDBLCLK: DestroyWindow(hwndDlg); return TRUE; case M_CLIPBOARDCOPY: { HWND hwnd = GetFocus(); if (hwnd == GetDlgItem(hwndDlg, IDC_CTLTEXT) || hwnd == GetDlgItem(hwndDlg, IDC_TEXT)) { CHARRANGE sel; ZeroMemory(&sel, sizeof(sel)); SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel.cpMin, (LPARAM)&sel.cpMax); if (sel.cpMin != sel.cpMax) { SendMessage(hwnd, WM_COPY, 0, 0); return TRUE; } } } if (OpenClipboard(hwndDlg)) { HGLOBAL hglb; int cch, len; EmptyClipboard(); hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(LCID)); if (hglb != NULL) { LCID *plocale = (LCID*)GlobalLock(hglb); // look at !! if (plocale != NULL) { *plocale = locale; GlobalUnlock(hglb); if (!SetClipboardData(CF_LOCALE, hglb)) GlobalFree(hglb); // shell takes ownership } else GlobalFree(hglb); } cch = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_CTLTEXT)) + GetWindowTextLength(GetDlgItem(hwndDlg, IDC_TEXT)) + 3; hglb = GlobalAlloc(GMEM_MOVEABLE, (cch + 1)*sizeof(TCHAR)); if (hglb != NULL) { TCHAR *pszText = (TCHAR*)GlobalLock(hglb); if (pszText != NULL) { if (!GetWindowText(GetDlgItem(hwndDlg, IDC_CTLTEXT), pszText, cch - 2)) pszText[0] = _T('\0'); len = lstrlen(pszText); if (GetWindowText(GetDlgItem(hwndDlg, IDC_TEXT), pszText + len + 2, cch - 2 - len) && len) { pszText[len] = _T('\r'); pszText[len + 1] = _T('\n'); } GlobalUnlock(hglb); if (!SetClipboardData(CF_UNICODETEXT, hglb)) GlobalFree(hglb); // shell takes ownership } else GlobalFree(hglb); } CloseClipboard(); } return TRUE; case WM_CTLCOLORDLG: case WM_CTLCOLORSTATIC: SetTextColor((HDC)wParam, GetSysColor(COLOR_INFOTEXT)); SetBkColor((HDC)wParam, GetSysColor(COLOR_INFOBK)); return (INT_PTR)GetSysColorBrush(COLOR_INFOBK); case WM_ACTIVATE: if (LOWORD(wParam) != WA_INACTIVE) break; if (GetParent((HWND)lParam) == hwndDlg) break; // fall through case WM_SYSCOLORCHANGE: case WM_ACTIVATEAPP: PostMessage(hwndDlg, WM_CLOSE, 0, 0); // no DestroyWindow() here! would cause recursion break; #endif // !defined EDITOR case M_CHANGEHELPCONTROL: if (hwndCtl == (HWND)lParam) break; SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); hwndCtl = (HWND)lParam; SetDlgItemText(hwndDlg, IDC_CTLTEXT, NULL); #ifdef EDITOR { TCHAR text[1024]; char *szModule, *szDlgId; GetControlTitle(hwndCtl, text, _countof(text)); SetDlgItemText(hwndDlg, IDC_CTLTEXT, text); mir_sntprintf(text, sizeof(text), TranslateT("Control ID: %d"), GetControlID(hwndCtl)); SetDlgItemText(hwndDlg, IDC_CTLID, text); szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl)); mir_sntprintf(text, sizeof(text), TranslateT("Dialog ID: %hs"), (szDlgId != NULL) ? szDlgId : Translate("Unknown")); mir_free(szDlgId); // does NULL check SetDlgItemText(hwndDlg, IDC_DLGID, text); mir_sntprintf(text, sizeof(text), TranslateT("Type: %s"), TranslateTS(szControlTypeNames[GetControlType(hwndCtl)])); SetDlgItemText(hwndDlg, IDC_CTLTYPE, text); szModule = GetControlModuleName(hwndCtl); mir_sntprintf(text, sizeof(text), TranslateT("Module: %hs"), szModule ? szModule : Translate("Unknown")); mir_free(szModule); // does NULL check SetDlgItemText(hwndDlg, IDC_MODULE, text); } #endif // defined EDITOR SetDlgItemText(hwndDlg, IDC_TEXT, NULL); SendMessage(hwndDlg, M_LOADHELP, 0, 0); #ifdef EDITOR ShowWindow(hwndDlg, SW_SHOWNORMAL); #else SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_REQUESTRESIZE, 0, 0); return FALSE; #endif return TRUE; case M_HELPLOADFAILED: if (hwndCtl != (HWND)lParam) break; #ifdef EDITOR EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), TRUE); { TCHAR text[2024]; GetControlTitle(hwndCtl, text, _countof(text)); SetDlgItemText(hwndDlg, IDC_CTLTEXT, text); } #else SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No help pack installed!")); #endif SetDlgItemText(hwndDlg, IDC_TEXT, NULL); MessageBeep(MB_ICONERROR); break; #ifdef EDITOR case M_SAVECOMPLETE: #endif case M_HELPLOADED: if (hwndCtl != (HWND)lParam) break; case M_LOADHELP: { TCHAR *szTitle; char *szText; char *szDlgId, *szModule; UINT codepage; BOOL isRTL; int id, loading; #ifdef EDITOR EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), TRUE); #endif szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl)); szModule = GetControlModuleName(hwndCtl); id = GetControlID(hwndCtl); #ifndef EDITOR // show id string instead of help text when 'ctrl' key pressed if (msg == M_LOADHELP && GetAsyncKeyState(VK_CONTROL) & 0x8000) { char *buf = CreateControlIdentifier(szDlgId ? szDlgId : "unknown", szModule ? szModule : "unknown", id, hwndCtl); HWND hwnd = GetDlgItem(hwndDlg, IDC_CTLTEXT); SetWindowTextA(hwnd, buf); // accepts NULL SetDlgItemText(hwndDlg, IDC_TEXT, NULL); mir_free(buf); // does NULL check mir_free(szDlgId); // does NULL check mir_free(szModule); // does NULL check break; } #endif if (szDlgId == NULL || szModule == NULL) { SetDlgItemText(hwndDlg, IDC_TEXT, NULL); #ifndef EDITOR SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No help available for this item.")); #endif mir_free(szDlgId); // does NULL check mir_free(szModule); // does NULL check break; } loading = GetControlHelp(hwndCtl, szDlgId, szModule, id, &szTitle, &szText, NULL, &locale, &codepage, &isRTL, (msg == M_HELPLOADED) ? GCHF_DONTLOAD : 0); if (!loading) { if (szText) StreamInHtml(GetDlgItem(hwndDlg, IDC_TEXT), szText, codepage, RGB(255, 0, 0)); else SetDlgItemText(hwndDlg, IDC_TEXT, NULL); if (szTitle) { TCHAR buf[128]; RECT rc; HFONT hFontPrev; DWORD exStyle; HWND hwndCtlText; HDC hdc; hwndCtlText = GetDlgItem(hwndDlg, IDC_CTLTEXT); exStyle = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWL_EXSTYLE); hdc = GetDC(hwndCtlText); { DWORD(WINAPI *pfnGetLayout)(HDC); // obey right-to-left languages *(FARPROC*)&pfnGetLayout = GetProcAddress(GetModuleHandleA("GDI32"), "GetLayout"); if (pfnGetLayout) isRTL = (isRTL && !pfnGetLayout(hdc)); if (isRTL) exStyle |= WS_EX_RTLREADING | WS_EX_RIGHT; else exStyle &= ~(WS_EX_RTLREADING | WS_EX_RIGHT); } mir_sntprintf(buf, sizeof(buf) - 4, _T("%s"), szTitle); if (hdc != NULL && hwndCtlText != NULL) { SendMessage(hwndCtlText, EM_GETRECT, 0, (LPARAM)&rc); hFontPrev = (HFONT)SelectObject(hdc, (HFONT)SendMessage(hwndCtlText, WM_GETFONT, 0, 0)); // look at !! // doesn't actually draw the string due to DT_CALCRECT DrawTextEx(hdc, buf, -1, &rc, DT_MODIFYSTRING | DT_CALCRECT | DT_EDITCONTROL | DT_END_ELLIPSIS | DT_INTERNAL | (isRTL ? (DT_RTLREADING | DT_RIGHT) : DT_LEFT) | DT_NOPREFIX | DT_SINGLELINE, NULL); SelectObject(hdc, hFontPrev); ReleaseDC(hwndCtlText, hdc); } SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWL_EXSTYLE, exStyle); SetWindowText(hwndCtlText, buf); } else SetDlgItemText(hwndDlg, IDC_CTLTEXT, NULL); } else { if (msg == M_HELPLOADED) { SetDlgItemText(hwndDlg, IDC_TEXT, NULL); #ifndef EDITOR SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No help available for this item.")); #endif } else { #ifdef EDITOR EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), FALSE); SetDlgItemText(hwndDlg, IDC_TEXT, TranslateT("Loading...")); #else SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("Loading...")); SetDlgItemText(hwndDlg, IDC_TEXT, NULL); #endif } } mir_free(szDlgId); mir_free(szModule); break; } case WM_NOTIFY: switch (((NMHDR*)lParam)->idFrom) { case IDC_TEXT: switch (((NMHDR*)lParam)->code) { #ifdef EDITOR case EN_MSGFILTER: switch (((MSGFILTER*)lParam)->msg) { case WM_CHAR: switch (((MSGFILTER*)lParam)->wParam) { case 'B' - 'A' + 1: case 'I' - 'A' + 1: case 'U' - 'A' + 1: case 'H' - 'A' + 1: case 'L' - 'A' + 1: case 'S' - 'A' + 1: case 'G' - 'A' + 1: SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); return TRUE; } break; case WM_KEYDOWN: { CHARFORMAT cf; int changes = 0; ZeroMemory(&cf, sizeof(cf)); if (!(GetKeyState(VK_CONTROL) & 0x8000)) break; cf.cbSize = sizeof(cf); SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETCHARFORMAT, TRUE, (LPARAM)&cf); switch (((MSGFILTER*)lParam)->wParam) { case 'B': cf.dwEffects ^= CFE_BOLD; cf.dwMask = CFM_BOLD; changes = 1; break; case 'I': cf.dwEffects ^= CFE_ITALIC; cf.dwMask = CFM_ITALIC; changes = 1; break; case 'U': cf.dwEffects ^= CFE_UNDERLINE; cf.dwMask = CFM_UNDERLINE; changes = 1; break; case 'L': { CHOOSECOLOR cc; COLORREF custCol[16]; ZeroMemory(&custCol, sizeof(custCol)); ZeroMemory(&cc, sizeof(cc)); cc.lStructSize = sizeof(cc); cc.hwndOwner = hwndDlg; cc.lpCustColors = custCol; cc.rgbResult = cf.crTextColor; cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; if (!ChooseColor(&cc)) break; cf.crTextColor = 0; cf.dwEffects = 0; if (cc.rgbResult) cf.crTextColor = cc.rgbResult; else cf.dwEffects = CFE_AUTOCOLOR; cf.dwMask = CFM_COLOR; changes = 1; break; } case 'H': cf.dwEffects ^= CFE_STRIKEOUT; cf.dwMask = CFM_STRIKEOUT; changes = 1; break; case VK_OEM_PLUS: cf.yHeight = ((GetKeyState(VK_SHIFT) & 0x8000) ? TEXTSIZE_BIG : TEXTSIZE_NORMAL) * 10; cf.dwMask = CFM_SIZE; changes = 1; break; case VK_OEM_MINUS: cf.yHeight = TEXTSIZE_SMALL * 10; cf.dwMask = CFM_SIZE; changes = 1; break; case 'S': { TCHAR szTitle[1024]; char *szText, *szDlgId, *szModule; if (!GetDlgItemText(hwndDlg, IDC_CTLTEXT, szTitle, _countof(szTitle))) break; szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl)); if (szDlgId == NULL) break; szText = StreamOutHtml(GetDlgItem(hwndDlg, IDC_TEXT)); szModule = GetControlModuleName(hwndCtl); if (szModule == NULL) { mir_free(szDlgId); break; } SetControlHelp(szDlgId, szModule, GetControlID(hwndCtl), szTitle, szText, GetControlType(hwndCtl)); mir_free(szText); mir_free(szDlgId); mir_free(szModule); SaveDialogCache(); SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); return TRUE; } case 'G': SendMessage(hwndDlg, M_LOADHELP, 0, 0); SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE); return TRUE; } if (changes) { SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf); SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); return TRUE; } break; } } break; #else // defined EDITOR case EN_MSGFILTER: { MSGFILTER *msgf = (MSGFILTER*)lParam; switch (msgf->msg) { case WM_LBUTTONUP: { POINT pt; DWORD pos; CHARRANGE sel; char *pszLink; HWND hwndEdit = msgf->nmhdr.hwndFrom; ZeroMemory(&sel, sizeof(sel)); SendMessage(msgf->nmhdr.hwndFrom, EM_EXGETSEL, 0, (LPARAM)&sel); if (sel.cpMin != sel.cpMax) break; POINTSTOPOINT(pt, MAKEPOINTS(msgf->lParam)); pos = SendMessage(hwndEdit, EM_CHARFROMPOS, 0, (WPARAM)&pt); if (IsHyperlink(pos, NULL, NULL, &pszLink)) { Utils_OpenUrl(pszLink); // pszLink is MBCS string in CP_ACP SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); return TRUE; } } break; case WM_MOUSEMOVE: // register hyperlink tooltips when current if (hwndToolTip != NULL) { POINTL pt; DWORD pos; CHARRANGE rng; char *pszLink; POINTSTOPOINT(pt, MAKEPOINTS(msgf->lParam)); pos = SendMessage(msgf->nmhdr.hwndFrom, EM_CHARFROMPOS, 0, (WPARAM)&pt); if (IsHyperlink(pos, &rng.cpMin, &rng.cpMax, &pszLink)) { // pszLink is MBCS in CP_ACP TTTOOLINFOA ti = { 0 }; ti.cbSize = sizeof(ti); ti.hwnd = msgf->nmhdr.hwndFrom; ti.uId = (UINT)rng.cpMin; if (!SendMessage(hwndToolTip, TTM_GETTOOLINFOA, 0, (LPARAM)&ti)) { LONG cpRectMin; ti.uFlags = TTF_SUBCLASS; ti.lpszText = pszLink; cpRectMin = rng.cpMin; while (!GetCharRangeRect(ti.hwnd, &rng.cpMin, rng.cpMax, &ti.rect)) { ti.uId = (UINT)cpRectMin; SendMessage(hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti); cpRectMin = rng.cpMin; } } } } break; } } break; case EN_REQUESTRESIZE: { RECT rcDlg, rcEdit, rcCtl, rcNew; REQRESIZE *rr = (REQRESIZE*)lParam; POINT ptScreenBottomRight; if (!GetWindowRect(hwndDlg, &rcDlg)) break; if (!GetWindowRect(hwndCtl, &rcCtl)) break; if (!GetWindowRect(GetDlgItem(hwndDlg, IDC_TEXT), &rcEdit)) break; rcNew.left = rcCtl.left + 30; rcNew.top = rcCtl.bottom + 10; rcNew.right = rcNew.left + (rr->rc.right - rr->rc.left) + (rcDlg.right - rcDlg.left) - (rcEdit.right - rcEdit.left) + (GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_TEXT), GWL_STYLE)&WS_VSCROLL ? GetSystemMetrics(SM_CXVSCROLL) : 0); if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_TEXT))) rcNew.bottom = rcNew.top + min(GetSystemMetrics(SM_CYSCREEN) / 5, (rr->rc.bottom - rr->rc.top) + (rcDlg.bottom - rcDlg.top) - (rcEdit.bottom - rcEdit.top)); else rcNew.bottom = rcNew.top + min(GetSystemMetrics(SM_CYSCREEN) / 5, (rcDlg.bottom - rcDlg.top) - (rcEdit.bottom - rcEdit.top)); if (GetSystemMetrics(SM_CXVIRTUALSCREEN)) { ptScreenBottomRight.x = GetSystemMetrics(SM_CXVIRTUALSCREEN) + GetSystemMetrics(SM_XVIRTUALSCREEN); ptScreenBottomRight.y = GetSystemMetrics(SM_CYVIRTUALSCREEN) + GetSystemMetrics(SM_YVIRTUALSCREEN); } else { ptScreenBottomRight.x = GetSystemMetrics(SM_CXSCREEN); ptScreenBottomRight.y = GetSystemMetrics(SM_CYSCREEN); } if (rcNew.right >= ptScreenBottomRight.x) OffsetRect(&rcNew, ptScreenBottomRight.x - rcNew.right, 0); if (rcNew.bottom >= ptScreenBottomRight.y) OffsetRect(&rcNew, 0, ptScreenBottomRight.y - rcNew.bottom); SetWindowPos(hwndDlg, 0, rcNew.left, rcNew.top, rcNew.right - rcNew.left, rcNew.bottom - rcNew.top, SWP_NOZORDER); ShowWindow(hwndDlg, SW_SHOWNORMAL); break; } #endif // defined EDITOR } } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: // WM_CLOSE DestroyWindow(hwndDlg); return TRUE; } break; case WM_DESTROY: #ifndef EDITOR FreeHyperlinkData(); if (IsWindow(hwndShadowDlg)) DestroyWindow(hwndShadowDlg); if (hwndToolTip != NULL) DestroyWindow(hwndToolTip); #endif hwndHelpDlg = NULL; break; } return FALSE; }