// SimpleRE.cpp : Simple Rich Edit Application
//
// Demonstrates setting CJK Unicode text into a RichEdit20W control
// and displaying it legibly on Windows 9x.
// 
// Compiles and runs with VC++ 6.0.
//
// You can use this code however you like. No warranties are 
// expressed or implied.
//
// 
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "richedit.h"

BOOL InitInstance(HINSTANCE hInst, INT cmdShow);
VOID TermInstance(VOID);

#define SZ_APPNAME  "Simple Richedit Host"    // app name/title
#define SZ_FONT     "SimHei"                  // Facename of font for rich edit control
#define THE_CHARSET GB2312_CHARSET            // charset of the font
#define IDC_RE      100                       // id of richedit window
#define WSZ_SAMPLE  L"\x4e2d\x570b"           // sample Unicode text

// Get the x, y, width, height of a RECT
#define RC_XYWH(rc) (rc).left, (rc).top, (rc).right-(rc).left, (rc).bottom-(rc).top

HMODULE     g_hRiched = NULL; // RichEd20.dll module handle
HFONT       g_hf      = NULL; // Font

//================================================================
// basic WinMain
INT APIENTRY WinMain (HINSTANCE hInst, HINSTANCE, LPSTR, INT nCmdShow)
{
    MSG msg = {0};
    if (InitInstance(hInst, nCmdShow))
    {
      while (GetMessage(&msg, NULL, 0, 0))
      {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
      }
    }
    TermInstance();
    return msg.wParam;
}

//================================================================
// SetRichEditText - add Unicode text to a richedit control

struct StreamInData
{
  PCWSTR pszData;
  LPBYTE pbCur;
  LONG   cbCur;
};

DWORD CALLBACK SimpleEditStreamCallback(DWORD dwCookie, LPBYTE pb, LONG cb, LONG *pcb)
{
  StreamInData * psid = (StreamInData *)dwCookie;
  if (!psid->pbCur)
  {
    psid->pbCur = (LPBYTE)psid->pszData;
    psid->cbCur = wcslen(psid->pszData) * sizeof(WCHAR);
  }
  LONG cbWritten;
  for (cbWritten = 0; (cbWritten < cb) && (cbWritten < psid->cbCur); cbWritten++)
  {
    *pb++ = *psid->pbCur++;
  }
  psid->cbCur -= cbWritten;
  *pcb = cbWritten;
  return 0;
}

LRESULT SetRichEditText (HWND hwndRE, LPCWSTR psz)
{
#if 1
    // EM_STREAMIN works with Richedit 2.0
    StreamInData sid = { psz, NULL, 0 };
    EDITSTREAM   es  = { (DWORD)&sid, 0, SimpleEditStreamCallback };
    return SendMessage(hwndRE, EM_STREAMIN, (WPARAM)(UINT)(SF_TEXT|SF_UNICODE), (LPARAM)&es);
#else
    // EM_SETTEXTEX is Richedit 3.0 only
    SETTEXTEX ste;
    ste.flags    = ST_SELECTION;  // replace everything
    ste.codepage = 1200;          // Unicode is codepage 1200
    return SendMessage(hwndRE, EM_SETTEXTEX, (WPARAM)&ste, (LPARAM)psz);
#endif
}

//================================================================
// AppWndProc - main window procedure
//
LONG APIENTRY AppWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    RECT rc;

    switch (msg)
    {
    case WM_CREATE:
        {
            // calculate rect of the richedit control
            GetClientRect(hwnd, &rc);
            InflateRect(&rc, -1, -1); // 1-pixel border

            // create richedit child window
            HWND hwndRE = CreateWindowEx(0, "RichEdit20W", "",
                WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_SAVESEL|WS_VSCROLL,
                RC_XYWH(rc), hwnd, (HMENU)IDC_RE, g_hRiched, NULL
                );

            // set the font in the richedit control
            SendMessage(hwndRE, WM_SETFONT, (WPARAM)g_hf, FALSE); 

            // initialize the richedit control with some Unicode CJK text
            SetRichEditText(hwndRE, WSZ_SAMPLE);

            // give it focus so the caret appears
            SetFocus(hwndRE);
        }
        break;

    case WM_SIZE:
        {
            HWND hwndRE = GetDlgItem(hwnd, IDC_RE);
            if (hwndRE)
            {
                GetClientRect(hwnd, &rc);
                InflateRect(&rc, -1, -1); // 1-pixel border
                SetWindowPos(hwndRE, NULL, RC_XYWH(rc), SWP_NOZORDER|SWP_SHOWWINDOW);
            }
        }
        break;

    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0L;
}


//
// initialize everything we need for this application
//
BOOL InitInstance(HINSTANCE hInst, INT cmdShow)
{
    // load richedit
    g_hRiched = LoadLibrary("riched20.dll");

    // create an interesting font
    g_hf = CreateFont(20, 0, 0, 0, 0, 0, 0, 0, THE_CHARSET, 0, 0, 0, 0, SZ_FONT);
    
    WNDCLASS  wc;

    wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon          = NULL;
    wc.lpszMenuName   = NULL;
    wc.lpszClassName  = SZ_APPNAME;
    wc.hbrBackground  = (HBRUSH) GetStockObject(GRAY_BRUSH);
    wc.hInstance      = hInst;
    wc.style          = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW;
    wc.lpfnWndProc    = (WNDPROC)AppWndProc;
    wc.cbWndExtra     = 0;
    wc.cbClsExtra     = 0;
    
    if (!RegisterClass(&wc))
        return FALSE;
    
    HWND hwnd = CreateWindowEx (0, SZ_APPNAME, SZ_APPNAME, 
        (WS_CAPTION | WS_OVERLAPPED | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX),
        CW_USEDEFAULT, CW_USEDEFAULT, 300, 100,
        NULL, NULL, hInst, NULL);
    
    if (!hwnd)
        return FALSE;

    ShowWindow(hwnd, cmdShow);
    
    return TRUE;
}


//
// clean up the app
//
VOID TermInstance(VOID)
{
    if (g_hf)
        DeleteObject((HGDIOBJ)g_hf);
    if (g_hRiched)
        FreeLibrary(g_hRiched);
}

