--- /dev/null
+/*
+ * PROJECT: ReactOS Character Map
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: base/applications/charmap/map.c
+ * PURPOSE: class implementation for painting glyph region
+ * COPYRIGHT: Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
+ *
+ */
+
+#include "precomp.h"
+
+#include <stdlib.h>
+
+static const WCHAR szMapWndClass[] = L"FontMapWnd";
+static const WCHAR szLrgCellWndClass[] = L"LrgCellWnd";
+
+#define MAX_ROWS (0xFFFF / XCELLS) + 1 - YCELLS
+
+
+static
+VOID
+SetGrid(PMAP infoPtr)
+{
+ INT x, y;
+
+ for (y = 0; y < YCELLS; y++)
+ for (x = 0; x < XCELLS; x++)
+ {
+ infoPtr->Cells[y][x].CellExt.left = x * infoPtr->CellSize.cx + 1;
+ infoPtr->Cells[y][x].CellExt.top = y * infoPtr->CellSize.cy + 1;
+ infoPtr->Cells[y][x].CellExt.right = (x + 1) * infoPtr->CellSize.cx + 2;
+ infoPtr->Cells[y][x].CellExt.bottom = (y + 1) * infoPtr->CellSize.cy + 2;
+
+ CopyRect(&infoPtr->Cells[y][x].CellInt,
+ &infoPtr->Cells[y][x].CellExt);
+
+ InflateRect(&infoPtr->Cells[y][x].CellInt,
+ -1,
+ -1);
+ }
+}
+
+static
+VOID
+DrawActiveCell(PMAP infoPtr,
+ HDC hdc)
+{
+ Rectangle(hdc,
+ infoPtr->pActiveCell->CellInt.left,
+ infoPtr->pActiveCell->CellInt.top,
+ infoPtr->pActiveCell->CellInt.right,
+ infoPtr->pActiveCell->CellInt.bottom);
+
+}
+
+
+static
+VOID
+DrawGrid(PMAP infoPtr,
+ PAINTSTRUCT *ps)
+{
+ INT x, y;
+ RECT rc;
+ PCELL Cell;
+
+ for (y = 0; y < YCELLS; y++)
+ for (x = 0; x < XCELLS; x++)
+ {
+ Cell = &infoPtr->Cells[y][x];
+
+ if (!IntersectRect(&rc,
+ &ps->rcPaint,
+ &Cell->CellExt))
+ {
+ continue;
+ }
+
+ Rectangle(ps->hdc,
+ Cell->CellExt.left,
+ Cell->CellExt.top,
+ Cell->CellExt.right,
+ Cell->CellExt.bottom);
+
+ if (infoPtr->pActiveCell == Cell)
+ {
+ DrawActiveCell(infoPtr, ps->hdc);
+ }
+ }
+}
+
+
+static
+VOID
+FillGrid(PMAP infoPtr,
+ PAINTSTRUCT *ps)
+{
+ HFONT hOldFont;
+ WCHAR ch;
+ INT x, y;
+ RECT rc;
+ PCELL Cell;
+ INT i, added;
+
+ hOldFont = SelectObject(ps->hdc,
+ infoPtr->hFont);
+
+ i = XCELLS * infoPtr->iYStart;
+
+ added = 0;
+
+ for (y = 0; y < YCELLS; y++)
+ for (x = 0; x < XCELLS; x++)
+ {
+ ch = (WCHAR)infoPtr->ValidGlyphs[i];
+
+ Cell = &infoPtr->Cells[y][x];
+
+ if (IntersectRect(&rc,
+ &ps->rcPaint,
+ &Cell->CellExt))
+ {
+ Cell->ch = ch;
+
+ DrawTextW(ps->hdc,
+ &ch,
+ 1,
+ &Cell->CellInt,
+ DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+
+ added++;
+ }
+
+ i++;
+ ch = (WCHAR)i;
+ }
+ SelectObject(ps->hdc,
+ hOldFont);
+}
+
+
+static
+BOOL
+CreateLargeCell(PMAP infoPtr)
+{
+ RECT rLarge;
+
+ CopyRect(&rLarge,
+ &infoPtr->pActiveCell->CellExt);
+
+ MapWindowPoints(infoPtr->hMapWnd,
+ infoPtr->hParent,
+ (VOID*)&rLarge,
+ 2);
+
+ InflateRect(&rLarge,
+ XLARGE - XCELLS,
+ YLARGE - YCELLS);
+
+ infoPtr->hLrgWnd = CreateWindowExW(0,
+ szLrgCellWndClass,
+ NULL,
+ WS_CHILDWINDOW | WS_VISIBLE,
+ rLarge.left,
+ rLarge.top,
+ rLarge.right - rLarge.left,
+ rLarge.bottom - rLarge.top,
+ infoPtr->hParent,
+ NULL,
+ hInstance,
+ infoPtr);
+ if (!infoPtr->hLrgWnd)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static
+VOID
+MoveLargeCell(PMAP infoPtr)
+{
+ RECT rLarge;
+
+ CopyRect(&rLarge,
+ &infoPtr->pActiveCell->CellExt);
+
+ MapWindowPoints(infoPtr->hMapWnd,
+ infoPtr->hParent,
+ (VOID*)&rLarge,
+ 2);
+
+ InflateRect(&rLarge,
+ XLARGE - XCELLS,
+ YLARGE - YCELLS);
+
+ MoveWindow(infoPtr->hLrgWnd,
+ rLarge.left,
+ rLarge.top,
+ rLarge.right - rLarge.left,
+ rLarge.bottom - rLarge.top,
+ TRUE);
+
+ InvalidateRect(infoPtr->hLrgWnd,
+ NULL,
+ TRUE);
+}
+
+
+static
+VOID
+SetFont(PMAP infoPtr,
+ LPWSTR lpFontName)
+{
+ HDC hdc;
+ WCHAR ch[MAX_GLYPHS];
+ WORD out[MAX_GLYPHS];
+ DWORD i, j;
+
+ /* Destroy Zoom window, since it was created with older font */
+ DestroyWindow(infoPtr->hLrgWnd);
+ infoPtr->hLrgWnd = NULL;
+
+ if (infoPtr->hFont)
+ DeleteObject(infoPtr->hFont);
+
+ ZeroMemory(&infoPtr->CurrentFont,
+ sizeof(LOGFONTW));
+
+ hdc = GetDC(infoPtr->hMapWnd);
+ infoPtr->CurrentFont.lfHeight = GetDeviceCaps(hdc, LOGPIXELSY) / 5;
+
+ infoPtr->CurrentFont.lfCharSet = DEFAULT_CHARSET;
+ wcsncpy(infoPtr->CurrentFont.lfFaceName,
+ lpFontName,
+ sizeof(infoPtr->CurrentFont.lfFaceName) / sizeof(infoPtr->CurrentFont.lfFaceName[0]));
+
+ infoPtr->hFont = CreateFontIndirectW(&infoPtr->CurrentFont);
+
+ InvalidateRect(infoPtr->hMapWnd,
+ NULL,
+ TRUE);
+
+ infoPtr->pActiveCell = &infoPtr->Cells[0][0];
+
+ // Get all the valid glyphs in this font
+
+ SelectObject(hdc, infoPtr->hFont);
+
+ for (i = 0; i < MAX_GLYPHS; i++)
+ ch[i] = (WCHAR)i;
+
+ if (GetGlyphIndicesW(hdc,
+ ch,
+ MAX_GLYPHS,
+ out,
+ GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
+ {
+ j = 0;
+ for (i = 0; i < MAX_GLYPHS; i++)
+ {
+ if (out[i] != 0xffff)
+ {
+ infoPtr->ValidGlyphs[j] = ch[i];
+ j++;
+ }
+ }
+ infoPtr->NumValidGlyphs = j;
+ }
+
+ ReleaseDC(infoPtr->hMapWnd, hdc);
+
+ infoPtr->NumRows = infoPtr->NumValidGlyphs / XCELLS;
+ if (infoPtr->NumValidGlyphs % XCELLS)
+ infoPtr->NumRows += 1;
+ infoPtr->NumRows = (infoPtr->NumRows > YCELLS) ? infoPtr->NumRows - YCELLS : 0;
+
+ SetScrollRange(infoPtr->hMapWnd, SB_VERT, 0, infoPtr->NumRows, FALSE);
+ SetScrollPos(infoPtr->hMapWnd, SB_VERT, 0, TRUE);
+ infoPtr->iYStart = 0;
+}
+
+
+static
+LRESULT
+NotifyParentOfSelection(PMAP infoPtr,
+ UINT code,
+ WCHAR ch)
+{
+ LRESULT Ret = 0;
+
+ if (infoPtr->hParent != NULL)
+ {
+ DWORD dwIdc = GetWindowLongPtr(infoPtr->hMapWnd, GWLP_ID);
+ /*
+ * Push directly into the event queue instead of waiting
+ * the parent to be unlocked.
+ * High word of LPARAM is still available for future needs...
+ */
+ Ret = PostMessage(infoPtr->hParent,
+ WM_COMMAND,
+ MAKELPARAM((WORD)dwIdc, (WORD)code),
+ (LPARAM)LOWORD(ch));
+ }
+
+ return Ret;
+}
+
+
+static
+VOID
+OnClick(PMAP infoPtr,
+ WORD ptx,
+ WORD pty)
+{
+ POINT pt;
+ INT x, y;
+
+ pt.x = ptx;
+ pt.y = pty;
+
+ for (x = 0; x < XCELLS; x++)
+ for (y = 0; y < YCELLS; y++)
+ {
+ if (PtInRect(&infoPtr->Cells[y][x].CellInt,
+ pt))
+ {
+ /* if the cell is not already active */
+ if (!infoPtr->Cells[y][x].bActive)
+ {
+ /* set previous active cell to inactive */
+ if (infoPtr->pActiveCell)
+ {
+ /* invalidate normal cells, required when
+ * moving a small active cell via keyboard */
+ if (!infoPtr->pActiveCell->bLarge)
+ {
+ InvalidateRect(infoPtr->hMapWnd,
+ &infoPtr->pActiveCell->CellInt,
+ TRUE);
+ }
+
+ infoPtr->pActiveCell->bActive = FALSE;
+ infoPtr->pActiveCell->bLarge = FALSE;
+ }
+
+ /* set new cell to active */
+ infoPtr->pActiveCell = &infoPtr->Cells[y][x];
+ infoPtr->pActiveCell->bActive = TRUE;
+ infoPtr->pActiveCell->bLarge = TRUE;
+ if (infoPtr->hLrgWnd)
+ MoveLargeCell(infoPtr);
+ else
+ CreateLargeCell(infoPtr);
+ }
+ else
+ {
+ /* flick between large and small */
+ if (infoPtr->pActiveCell->bLarge)
+ {
+ DestroyWindow(infoPtr->hLrgWnd);
+ infoPtr->hLrgWnd = NULL;
+ }
+ else
+ {
+ CreateLargeCell(infoPtr);
+ }
+
+ infoPtr->pActiveCell->bLarge = (infoPtr->pActiveCell->bLarge) ? FALSE : TRUE;
+ }
+
+ break;
+ }
+ }
+}
+
+
+static
+BOOL
+OnCreate(PMAP infoPtr,
+ HWND hwnd,
+ HWND hParent)
+{
+ RECT rc;
+ BOOL Ret = FALSE;
+
+ infoPtr = HeapAlloc(GetProcessHeap(),
+ 0,
+ sizeof(MAP));
+ if (infoPtr)
+ {
+ SetLastError(0);
+ SetWindowLongPtrW(hwnd,
+ 0,
+ (DWORD_PTR)infoPtr);
+ if (GetLastError() == 0)
+ {
+ ZeroMemory(infoPtr,
+ sizeof(MAP));
+
+ infoPtr->hMapWnd = hwnd;
+ infoPtr->hParent = hParent;
+
+ GetClientRect(hwnd, &rc);
+ infoPtr->ClientSize.cx = rc.right;
+ infoPtr->ClientSize.cy = rc.bottom;
+ infoPtr->CellSize.cx = infoPtr->ClientSize.cx / XCELLS;
+ infoPtr->CellSize.cy = infoPtr->ClientSize.cy / YCELLS;
+
+ infoPtr->pActiveCell = NULL;
+
+ SetGrid(infoPtr);
+
+ SetScrollPos(infoPtr->hParent, SB_VERT, 0, TRUE);
+
+ Ret = TRUE;
+ }
+ }
+
+ return Ret;
+}
+
+
+static
+VOID
+OnVScroll(PMAP infoPtr,
+ INT Value,
+ INT Pos)
+{
+ INT iYDiff, iOldYStart = infoPtr->iYStart;
+
+ switch (Value)
+ {
+ case SB_LINEUP:
+ infoPtr->iYStart -= 1;
+ break;
+
+ case SB_LINEDOWN:
+ infoPtr->iYStart += 1;
+ break;
+
+ case SB_PAGEUP:
+ infoPtr->iYStart -= YCELLS;
+ break;
+
+ case SB_PAGEDOWN:
+ infoPtr->iYStart += YCELLS;
+ break;
+
+ case SB_THUMBTRACK:
+ infoPtr->iYStart = Pos;
+ break;
+
+ default:
+ break;
+ }
+
+ infoPtr->iYStart = max(0, infoPtr->iYStart);
+ infoPtr->iYStart = min(infoPtr->iYStart, infoPtr->NumRows);
+
+ iYDiff = iOldYStart - infoPtr->iYStart;
+ if (iYDiff)
+ {
+ if (infoPtr->hLrgWnd != NULL)
+ {
+ ShowWindow(infoPtr->hLrgWnd, SW_HIDE);
+ }
+
+ SetScrollPos(infoPtr->hMapWnd,
+ SB_VERT,
+ infoPtr->iYStart,
+ TRUE);
+
+ if (abs(iYDiff) < YCELLS)
+ {
+ RECT rect;
+ GetClientRect(infoPtr->hMapWnd, &rect);
+ rect.top += 2;
+ rect.bottom -= 2;
+ ScrollWindowEx(infoPtr->hMapWnd,
+ 0,
+ iYDiff * infoPtr->CellSize.cy,
+ &rect,
+ &rect,
+ NULL,
+ NULL,
+ SW_INVALIDATE);
+ }
+ else
+ {
+ InvalidateRect(infoPtr->hMapWnd,
+ NULL,
+ TRUE);
+ }
+
+ if (infoPtr->hLrgWnd != NULL)
+ {
+ ShowWindow(infoPtr->hLrgWnd, SW_SHOW);
+ }
+ }
+}
+
+
+static
+VOID
+OnPaint(PMAP infoPtr,
+ WPARAM wParam)
+{
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+
+ if (wParam != 0)
+ {
+ if (!GetUpdateRect(infoPtr->hMapWnd,
+ &ps.rcPaint,
+ TRUE))
+ {
+ return;
+ }
+ ps.hdc = (HDC)wParam;
+ }
+ else
+ {
+ hdc = BeginPaint(infoPtr->hMapWnd,
+ &ps);
+ if (hdc == NULL)
+ {
+ return;
+ }
+ }
+
+ DrawGrid(infoPtr, &ps);
+
+ FillGrid(infoPtr, &ps);
+
+ if (wParam == 0)
+ {
+ EndPaint(infoPtr->hMapWnd,
+ &ps);
+ }
+}
+
+
+LRESULT
+CALLBACK
+MapWndProc(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ PMAP infoPtr;
+ LRESULT Ret = 0;
+
+ infoPtr = (PMAP)GetWindowLongPtrW(hwnd,
+ 0);
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ {
+ if (!OnCreate(infoPtr,
+ hwnd,
+ ((LPCREATESTRUCTW)lParam)->hwndParent))
+ {
+ return (LRESULT)-1;
+ }
+
+ break;
+ }
+
+ case WM_LBUTTONDOWN:
+ {
+ OnClick(infoPtr,
+ LOWORD(lParam),
+ HIWORD(lParam));
+
+ break;
+ }
+
+ case WM_LBUTTONDBLCLK:
+ {
+ NotifyParentOfSelection(infoPtr,
+ FM_SETCHAR,
+ infoPtr->pActiveCell->ch);
+
+
+ break;
+ }
+
+ case WM_VSCROLL:
+ {
+ OnVScroll(infoPtr,
+ LOWORD(wParam),
+ HIWORD(wParam));
+
+ break;
+ }
+
+ case FM_SETFONT:
+ SetFont(infoPtr, (LPWSTR)lParam);
+ break;
+
+ case FM_GETCHAR:
+ {
+ if (!infoPtr->pActiveCell) return 0;
+ return infoPtr->pActiveCell->ch;
+ }
+
+ case FM_GETHFONT:
+ return (LRESULT)infoPtr->hFont;
+
+ case WM_PAINT:
+ {
+ OnPaint(infoPtr,
+ wParam);
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ DeleteObject(infoPtr->hFont);
+ HeapFree(GetProcessHeap(),
+ 0,
+ infoPtr);
+ SetWindowLongPtrW(hwnd,
+ 0,
+ (DWORD_PTR)NULL);
+ break;
+ }
+
+ default:
+ {
+ Ret = DefWindowProcW(hwnd,
+ uMsg,
+ wParam,
+ lParam);
+ break;
+ }
+ }
+
+ return Ret;
+}
+
+
+BOOL
+RegisterMapClasses(HINSTANCE hInstance)
+{
+ WNDCLASSW wc = {0};
+
+ wc.style = CS_DBLCLKS;
+ wc.lpfnWndProc = MapWndProc;
+ wc.cbWndExtra = sizeof(PMAP);
+ wc.hInstance = hInstance;
+ wc.hCursor = LoadCursorW(NULL,
+ (LPWSTR)IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wc.lpszClassName = szMapWndClass;
+
+ if (RegisterClassW(&wc))
+ {
+ wc.lpfnWndProc = LrgCellWndProc;
+ wc.cbWndExtra = 0;
+ wc.lpszClassName = szLrgCellWndClass;
+
+ return RegisterClassW(&wc) != 0;
+ }
+
+ return FALSE;
+}
+
+VOID
+UnregisterMapClasses(HINSTANCE hInstance)
+{
+ UnregisterClassW(szMapWndClass,
+ hInstance);
+
+ UnregisterClassW(szLrgCellWndClass,
+ hInstance);
+}