/* File "OVERLAY.C" - Code to overlay a RichText control over a background */ /* Code by Matt Slot and placed into public domain */ #include #include #include #include /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* Preprocessor Definitions */ #define OVERLAY_CLASS "OVERLAY" #define PROGRAM_NAME "RichEdit Overlay" #define STRING "abcde fghij klmno pqrst uvwxyz 01234 56789 " /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* Function Prototypes */ typedef BOOL WINAPI (*MaskBltProc)(IN HDC, IN int, IN int, IN int, IN int, IN HDC, IN int, IN int, IN HBITMAP, IN int, IN int, IN DWORD); BOOL RichEditOverlayHWND(HWND hwnd, HDC hdc, HWND hwndRich); BOOL RichEditOverlayHDC(HDC hdc, DWORD x, DWORD y, HWND hwndRich); /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* Overlay the contents of a HWND with a RichEdit control (its child window) */ BOOL RichEditOverlayHWND(HWND hwnd, HDC hdc, HWND hwndRich) { POINT point = { 0, 0 }; /* Just get the window location from our parent window */ if (IsChild(hwnd, hwndRich)) MapWindowPoints(hwndRich, hwnd, &point, 1); return(RichEditOverlayHDC(hdc, point.x, point.y, hwndRich)); } /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* Overlay the contents of the HDC at the given point with a RichEdit control */ BOOL RichEditOverlayHDC(HDC hdc, DWORD x, DWORD y, HWND hwndRich) { static MaskBltProc gMaskBltProc = (MaskBltProc) -1; BOOL success = TRUE; HDC hdcSave = NULL; HDC hdcRich = NULL, hdcMask = NULL; HBITMAP hbmRich = NULL, hbmSaveRich = NULL; HBITMAP hbmMask = NULL, hbmSaveMask = NULL; COLORREF bkColor; DWORD bits, w, h; RECT rect; /* On our first call, we try to dynamically load MaskBlt() */ if (gMaskBltProc == (MaskBltProc) -1) { HINSTANCE hModule = LoadLibrary("GDI32.DLL"); if (hModule) gMaskBltProc = GetProcAddress(hModule, "MaskBlt"); } /* Establish our coordinates from the start. */ GetClientRect(hwndRich, &rect); w = rect.right - rect.left; h = rect.bottom - rect.top; /* Get the relevant characteristics of the source window */ hdcSave = GetDC(hwndRich); if (!hdcSave) { success = FALSE; goto CLEANUP; } bkColor = GetBkColor(hdcSave); bits = GetDeviceCaps(hdcSave, BITSPIXEL); /* Create offscreen drawing contexts and bitmap for the pixels */ hdcRich = CreateCompatibleDC(hdcSave); if (!hdcRich) { success = FALSE; goto CLEANUP; } hbmRich = CreateBitmap(w, h, 1, bits, NULL); if (!hbmRich) { success = FALSE; goto CLEANUP; } hbmSaveRich = SelectBitmap(hdcRich, hbmRich); /* Create offscreen drawing contexts and bitmap for the mask */ hdcMask = CreateCompatibleDC(hdcSave); if (!hdcMask) { success = FALSE; goto CLEANUP; } hbmMask = CreateBitmap(w, h, 1, 1, NULL); if (!hbmMask) { success = FALSE; goto CLEANUP; } hbmSaveMask = SelectBitmap(hdcMask, hbmMask); /* Draw the control into our offscreen drawing context */ if (1) { /* Windows 2000 doesn't like our calls to WM_PRINT or WM_PRINTCLIENT. Fortunately, it will EM_FORMATRANGE but it insists on overwriting the background -- even ignoring a call to SetBkMode(TRANSPARENT) */ FORMATRANGE fr; COLORREF color; HBRUSH brush; fr.hdc = hdcRich; fr.hdcTarget = hdcRich; fr.rc.left = 0; fr.rc.top = 0; fr.rc.right = w * 1440 / GetDeviceCaps(hdcRich, LOGPIXELSX); fr.rc.bottom = h * 1440 / GetDeviceCaps(hdcRich, LOGPIXELSY); fr.rcPage = fr.rc; fr.chrg.cpMin = 0; fr.chrg.cpMax = -1; /* SetBkMode(hdcRich, TRANSPARENT); -- why doesn't this work? */ SendMessage(hwndRich, EM_FORMATRANGE, 1, (LPARAM) &fr); SendMessage(hwndRich, EM_FORMATRANGE, 0, 0); /* Erase the area below the text with the same color for masking */ color = GetPixel(hdcRich, 0, 0); SetBkColor(hdcRich, color); if (brush = CreateSolidBrush(color)) { SetRect(&rect, 0, 0, w, h); rect.top = fr.rc.bottom * GetDeviceCaps(hdcRich, LOGPIXELSY) / 1440; FillRect(hdcRich, &rect, brush); } } else SendMessage(hwndRich, WM_PRINTCLIENT, (WPARAM) hdcRich, PRF_CLIENT | PRF_ERASEBKGND | PRF_CHILDREN); if (gMaskBltProc) { /* We'll gladly use MaskBlt() if it's available */ BitBlt(hdcMask,0,0,w,h,hdcRich,0,0,SRCCOPY); success = (*gMaskBltProc)(hdc,x,y,w,h,hdcRich,0,0, hbmMask,0,0,MAKEROP4(SRCAND,SRCCOPY)); } else { /* Otherwise manually mask the image as we blit */ BitBlt(hdcMask,0,0,w,h,hdcRich,0,0,SRCCOPY); BitBlt(hdcRich,0,0,w,h,hdcMask,0,0,0x00220326); /* SRCNOTAND */ BitBlt(hdc,x,y,w,h,hdcMask,0,0,SRCAND); success = BitBlt(hdc,x,y,w,h,hdcRich,0,0,SRCPAINT); } CLEANUP: /* Release all of our objects in order */ if (hbmSaveMask) SelectBitmap(hdcMask, hbmSaveMask); if (hbmMask) DeleteObject(hbmMask); if (hbmSaveRich) SelectBitmap(hdcRich, hbmSaveRich); if (hbmRich) DeleteObject(hbmRich); if (hdcSave) ReleaseDC(hwndRich, hdcSave); return(success); } /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* Private utility routines */ #define kFirstChildWindowID 10000 static BOOL CALLBACK CalcChildWindowProc(HWND hwnd, LPARAM param) { HMENU * child = (HMENU *) param; if (*child == (HMENU) GetDlgCtrlID(hwnd)) *child = (HMENU) NULL; return((*child) ? TRUE : FALSE); } /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ static HMENU CalcChildWindowID(HWND hwnd) { HMENU child; UINT i; for(i=kFirstChildWindowID, child=(HMENU) NULL; !child; child = (HMENU) ++i) EnumChildWindows(hwnd, CalcChildWindowProc, (LPARAM) &child); return(child); } /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ static LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { LRESULT result = 0; HWND hwndRich; HDC hdc; PAINTSTRUCT ps; switch(msg) { case WM_PAINT: hdc = BeginPaint(hwnd, &ps); /* Grab the RichEdit handle from the parent window */ hwndRich = (HWND) GetWindowLong(hwnd, GWL_USERDATA); RichEditOverlayHWND(hwnd, hdc, hwndRich); EndPaint(hwnd, &ps); result = 1; break; case WM_DESTROY: PostQuitMessage(0); break; default: result = DefWindowProc(hwnd, msg, wParam, lParam); } return(result); } /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd = (HWND) NULL; HWND hwndRich = (HWND) NULL; HMODULE hModule = NULL; MSG lpMsg; WNDCLASS wc; RECT rect; HDC hdc; UINT i, j; CHARFORMAT cf; /* Create our class and use an interesting background brush */ if (!hPrev) { wc.lpszClassName = OVERLAY_CLASS; wc.hInstance = hInst; wc.lpfnWndProc = MyWndProc; wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION); wc.lpszMenuName = (char *) NULL; wc.hbrBackground = CreateHatchBrush(HS_BDIAGONAL, RGB(255,0,0)); wc.style = 0; wc.cbClsExtra = 0; wc.cbWndExtra = 0; if (!RegisterClass(&wc)) return FALSE; } /* Create the window */ hwnd = CreateWindow(OVERLAY_CLASS, PROGRAM_NAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL, (HINSTANCE) hInst, (LPSTR) NULL); /* Create the RichEdit control -- notice, we never make it visible! */ InitCommonControls(); GetClientRect(hwnd, &rect); if (hModule = LoadLibrary("RICHED32.DLL")) { /* RichEdit 1.0 does not support transparency, so we need to support drawing into the parent HWND or an arbitrary HDC. */ hwndRich = CreateWindowEx(0, "RICHEDIT", "", /* WS_VISIBLE | */ WS_CHILD | ES_LEFT | ES_SAVESEL | ES_MULTILINE | ES_AUTOVSCROLL, rect.left + 50, rect.top + 50, rect.right - rect.left - 100, rect.bottom - rect.top - 100, hwnd, CalcChildWindowID(hwnd), (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL); } Edit_Enable(hwndRich, FALSE); hdc = GetDC(hwndRich); /* Fill the image with some interesting text, 1 run at a time */ ZeroMemory(&cf, sizeof(CHARFORMAT)); cf.cbSize = sizeof(CHARFORMAT); cf.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_ITALIC | CFM_SIZE | CFM_UNDERLINE; cf.dwEffects = 0; cf.yHeight = 120 / GetDeviceCaps(hdc, LOGPIXELSY); cf.yOffset = 0; cf.crTextColor = RGB(0,0,0); cf.bCharSet = ANSI_CHARSET; cf.bPitchAndFamily = DEFAULT_PITCH; strcpy(cf.szFaceName, "System"); for(i=0, j=sizeof(STRING)-sizeof(""); i<27; i++) { Edit_SetSel(hwndRich, j*i, j*i); Edit_ReplaceSel(hwndRich, STRING); Edit_SetSel(hwndRich, j*i, j*(i+1)); /* Format the text */ cf.crTextColor = RGB((i%3)*0x44,((i/3)%3)*0x44,((i/9)%3)*0x44); SendMessage(hwndRich, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf); } Edit_SetSel(hwndRich, 0, 0); ReleaseDC(hwndRich, hdc); /* Store the handle to the child window so we can access it */ SetWindowLong(hwnd, GWL_USERDATA, (LONG) hwndRich); /* Sit and spin */ ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&lpMsg, (HWND) NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } DestroyWindow(hwnd); if (hModule) FreeLibrary(hModule); return(lpMsg.wParam); }