/* * PROJECT: ReactOS VGA Font Editor * LICENSE: GNU General Public License Version 2.0 or any later version * FILE: devutils/vgafontedit/fontboxeswnd.c * PURPOSE: Implements the window showing the character boxes for a font * COPYRIGHT: Copyright 2008 Colin Finck */ #include "precomp.h" static const WCHAR szFontBoxesWndClass[] = L"VGAFontEditFontBoxesWndClass"; static VOID DrawCharacterPixel(IN PAINTSTRUCT *ps, IN UINT uCharacter, IN UCHAR uRow, IN UCHAR uColumn, IN UCHAR uBit, IN COLORREF clBackground) { INT x; INT y; x = (uCharacter % 16) * (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING) + 24 + uColumn; y = (uCharacter / 16) * (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING)+ 1 + CHARACTER_INFO_BOX_HEIGHT + 2 + uRow; SetPixel( ps->hdc, x, y, (uBit ? 0 : clBackground) ); } VOID GetCharacterRect(IN UINT uFontRow, IN UINT uFontColumn, OUT LPRECT CharacterRect) { CharacterRect->left = uFontColumn * (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING); CharacterRect->top = uFontRow * (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING); CharacterRect->right = CharacterRect->left + CHARACTER_BOX_WIDTH; CharacterRect->bottom = CharacterRect->top + CHARACTER_BOX_HEIGHT; } __inline VOID GetCharacterPosition(IN UINT uCharacter, OUT PUINT uFontRow, OUT PUINT uFontColumn) { *uFontRow = uCharacter / 16; *uFontColumn = uCharacter % 16; } static INT FontBoxesHitTest(IN UINT xPos, IN UINT yPos, OUT LPRECT CharacterRect) { UINT uFontColumn; UINT uFontRow; uFontColumn = xPos / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING); uFontRow = yPos / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING); GetCharacterRect(uFontRow, uFontColumn, CharacterRect); if(xPos > (UINT)CharacterRect->right || yPos > (UINT)CharacterRect->bottom) // The user clicked on separator space, so return HITTEST_SEPARATOR return HITTEST_SEPARATOR; else // Return the character number return (uFontRow * 16 + uFontColumn); } static VOID SetSelectedCharacter(IN PFONT_WND_INFO Info, IN UINT uNewCharacter, OPTIONAL IN LPRECT NewCharacterRect) { LPRECT pCharacterRect; RECT OldCharacterRect; UINT uFontColumn; UINT uFontRow; // Remove the selection of the old character GetCharacterPosition(Info->uSelectedCharacter, &uFontRow, &uFontColumn); GetCharacterRect(uFontRow, uFontColumn, &OldCharacterRect); InvalidateRect(Info->hFontBoxesWnd, &OldCharacterRect, FALSE); // You may pass the RECT of the new character, otherwise we'll allocate memory for one and get it ourselves if(NewCharacterRect) pCharacterRect = NewCharacterRect; else { GetCharacterPosition(uNewCharacter, &uFontRow, &uFontColumn); pCharacterRect = (LPRECT) HeapAlloc( hProcessHeap, 0, sizeof(RECT) ); GetCharacterRect(uFontRow, uFontColumn, pCharacterRect); } // Select the new character Info->uSelectedCharacter = uNewCharacter; InvalidateRect(Info->hFontBoxesWnd, pCharacterRect, FALSE); if(!NewCharacterRect) HeapFree(hProcessHeap, 0, pCharacterRect); } static VOID DrawProc(IN PFONT_WND_INFO Info, IN PAINTSTRUCT* ps) { COLORREF clBackground; HBRUSH hBrush = 0; HBRUSH hOldBrush = 0; HDC hBoxDC; HFONT hFont; HFONT hOldFont; RECT CharacterRect; UINT uFontColumn; UINT uStartColumn; UINT uEndColumn; UINT uFontRow; UINT uStartRow; UINT uEndRow; UINT uCharacter; UCHAR uCharacterColumn; UCHAR uCharacterRow; UCHAR uBit; WCHAR szInfoText[9]; HBITMAP hBitmapOld; // Preparations hBoxDC = CreateCompatibleDC(NULL); hBitmapOld = SelectObject(hBoxDC, Info->MainWndInfo->hBoxBmp); hFont = CreateFontW(13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Tahoma"); hOldFont = SelectObject(ps->hdc, hFont); SetBkMode( ps->hdc, TRANSPARENT ); // What ranges do we have to draw? uStartRow = ps->rcPaint.top / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING); uEndRow = ps->rcPaint.bottom / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING); uStartColumn = ps->rcPaint.left / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING); uEndColumn = ps->rcPaint.right / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING); for(uFontRow = uStartRow; uFontRow <= uEndRow; uFontRow++) { for(uFontColumn = uStartColumn; uFontColumn <= uEndColumn; uFontColumn++) { GetCharacterRect(uFontRow, uFontColumn, &CharacterRect); uCharacter = uFontRow * 16 + uFontColumn; // Draw the Character Info Box (header) BitBlt(ps->hdc, CharacterRect.left, CharacterRect.top, CHARACTER_BOX_WIDTH, CHARACTER_INFO_BOX_HEIGHT, hBoxDC, 0, 0, SRCCOPY); // Draw the header text wsprintfW(szInfoText, L"%02u = %02X", uCharacter, uCharacter); DrawTextW( ps->hdc, szInfoText, -1, &CharacterRect, DT_CENTER ); // Draw the Character Bitmap Box (rectangle with the actual character) if(Info->uSelectedCharacter == uCharacter) { clBackground = RGB(255, 255, 0); hBrush = CreateSolidBrush(clBackground); hOldBrush = SelectObject(ps->hdc, hBrush); } else { clBackground = RGB(255, 255, 255); SelectObject( ps->hdc, GetStockObject(WHITE_BRUSH) ); } Rectangle(ps->hdc, CharacterRect.left, CharacterRect.top + CHARACTER_INFO_BOX_HEIGHT, CharacterRect.right, CharacterRect.bottom); // Draw the actual character into the box for(uCharacterRow = 0; uCharacterRow < 8; uCharacterRow++) { for(uCharacterColumn = 0; uCharacterColumn < 8; uCharacterColumn++) { uBit = Info->Font->Bits[uCharacter * 8 + uCharacterRow] << uCharacterColumn & 0x80; DrawCharacterPixel(ps, uCharacter, uCharacterRow, uCharacterColumn, uBit, clBackground); } } } } SelectObject(hBoxDC, hBitmapOld); SelectObject(ps->hdc, hOldFont); DeleteObject(hFont); SelectObject(ps->hdc, hOldBrush); DeleteObject(hBrush); DeleteDC(hBoxDC); } VOID EditCurrentGlyph(PFONT_WND_INFO FontWndInfo) { PEDIT_GLYPH_INFO EditGlyphInfo; // Has the window for this character already been opened? EditGlyphInfo = FontWndInfo->FirstEditGlyphWnd; while(EditGlyphInfo) { if(EditGlyphInfo->uCharacter == FontWndInfo->uSelectedCharacter) { // Yes, it has. Bring it to the front. SetFocus(EditGlyphInfo->hSelf); return; } EditGlyphInfo = EditGlyphInfo->NextEditGlyphWnd; } // No. Then create a new one EditGlyphInfo = (PEDIT_GLYPH_INFO) HeapAlloc( hProcessHeap, 0, sizeof(EDIT_GLYPH_INFO) ); EditGlyphInfo->FontWndInfo = FontWndInfo; EditGlyphInfo->uCharacter = FontWndInfo->uSelectedCharacter; RtlCopyMemory( EditGlyphInfo->CharacterBits, FontWndInfo->Font->Bits + FontWndInfo->uSelectedCharacter * 8, sizeof(EditGlyphInfo->CharacterBits) ); // Add the new window to the linked list EditGlyphInfo->PrevEditGlyphWnd = FontWndInfo->LastEditGlyphWnd; EditGlyphInfo->NextEditGlyphWnd = NULL; if(FontWndInfo->LastEditGlyphWnd) FontWndInfo->LastEditGlyphWnd->NextEditGlyphWnd = EditGlyphInfo; else FontWndInfo->FirstEditGlyphWnd = EditGlyphInfo; FontWndInfo->LastEditGlyphWnd = EditGlyphInfo; // Open the window as a modeless dialog, so people can edit several characters at the same time. EditGlyphInfo->hSelf = CreateDialogParamW(hInstance, MAKEINTRESOURCEW(IDD_EDITGLYPH), FontWndInfo->hSelf, EditGlyphDlgProc, (LPARAM)EditGlyphInfo); ShowWindow(EditGlyphInfo->hSelf, SW_SHOW); } VOID CreateFontBoxesWindow(IN PFONT_WND_INFO FontWndInfo) { FontWndInfo->hFontBoxesWnd = CreateWindowExW(0, szFontBoxesWndClass, 0, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, FontWndInfo->hSelf, NULL, hInstance, FontWndInfo); } static LRESULT CALLBACK FontBoxesWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { PFONT_WND_INFO Info; Info = (PFONT_WND_INFO) GetWindowLongW(hwnd, GWLP_USERDATA); if(Info || uMsg == WM_CREATE) { switch(uMsg) { case WM_CREATE: Info = (PFONT_WND_INFO)( ( (LPCREATESTRUCT)lParam )->lpCreateParams ); SetWindowLongW(hwnd, GWLP_USERDATA, (LONG)Info); // Set a fixed window size SetWindowPos(hwnd, NULL, 0, 0, FONT_BOXES_WND_WIDTH, FONT_BOXES_WND_HEIGHT, SWP_NOZORDER | SWP_NOMOVE); return 0; case WM_DESTROY: SetWindowLongW(hwnd, GWLP_USERDATA, 0); return 0; case WM_KEYDOWN: switch(wParam) { case VK_DOWN: if(Info->uSelectedCharacter < 239) SetSelectedCharacter(Info, Info->uSelectedCharacter + 16, NULL); return 0; case VK_LEFT: if(Info->uSelectedCharacter) SetSelectedCharacter(Info, Info->uSelectedCharacter - 1, NULL); return 0; case VK_RETURN: EditCurrentGlyph(Info); return 0; case VK_RIGHT: if(Info->uSelectedCharacter < 255) SetSelectedCharacter(Info, Info->uSelectedCharacter + 1, NULL); return 0; case VK_UP: if(Info->uSelectedCharacter > 15) SetSelectedCharacter(Info, Info->uSelectedCharacter - 16, NULL); return 0; } break; case WM_LBUTTONDBLCLK: { EditCurrentGlyph(Info); return 0; } case WM_LBUTTONDOWN: { RECT CharacterRect; INT iRet; iRet = FontBoxesHitTest( GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &CharacterRect ); if(iRet >= 0) SetSelectedCharacter( Info, (UINT)iRet, &CharacterRect ); return 0; } case WM_PAINT: { PAINTSTRUCT ps; BeginPaint(hwnd, &ps); DrawProc(Info, &ps); EndPaint(hwnd, &ps); return 0; } } } return DefWindowProcW(hwnd, uMsg, wParam, lParam); } BOOL InitFontBoxesWndClass(VOID) { WNDCLASSW wc = {0,}; wc.lpfnWndProc = FontBoxesWndProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor( NULL, IDC_ARROW ); wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 ); wc.lpszClassName = szFontBoxesWndClass; wc.style = CS_DBLCLKS; return RegisterClassW(&wc) != 0; } VOID UnInitFontBoxesWndClass(VOID) { UnregisterClassW(szFontBoxesWndClass, hInstance); }