/* * 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 * */ #include "precomp.h" #include 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 MapOnCreate(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 (!MapOnCreate(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); }