/* File "OVERLAY.C" - Code to overlay a RichText control over a background    */
/* Code by Matt Slot <fprefect@ambrosiasw.com> and placed into public domain  */

#include <COMMCTRL.H>
#include <RICHEDIT.H>
#include <WINDOWSX.H>
#include <WINDOWS.H>

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* 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);
    }



