1 /* 2 * PROJECT: ReactOS VGA Font Editor 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Implements the window showing the character boxes for a font 5 * COPYRIGHT: Copyright 2008 Colin Finck (colin@reactos.org) 6 */ 7 8 #include "precomp.h" 9 10 static const WCHAR szFontBoxesWndClass[] = L"VGAFontEditFontBoxesWndClass"; 11 12 static VOID 13 DrawCharacterPixel(IN PAINTSTRUCT *ps, IN UINT uCharacter, IN UCHAR uRow, IN UCHAR uColumn, IN UCHAR uBit, IN COLORREF clBackground) 14 { 15 INT x; 16 INT y; 17 18 x = (uCharacter % 16) * (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING) + 24 + uColumn; 19 y = (uCharacter / 16) * (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING)+ 1 + CHARACTER_INFO_BOX_HEIGHT + 2 + uRow; 20 21 SetPixel( ps->hdc, x, y, (uBit ? 0 : clBackground) ); 22 } 23 24 VOID 25 GetCharacterRect(IN UINT uFontRow, IN UINT uFontColumn, OUT LPRECT CharacterRect) 26 { 27 CharacterRect->left = uFontColumn * (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING); 28 CharacterRect->top = uFontRow * (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING); 29 CharacterRect->right = CharacterRect->left + CHARACTER_BOX_WIDTH; 30 CharacterRect->bottom = CharacterRect->top + CHARACTER_BOX_HEIGHT; 31 } 32 33 static INT 34 FontBoxesHitTest(IN UINT xPos, IN UINT yPos, OUT LPRECT CharacterRect) 35 { 36 UINT uFontColumn; 37 UINT uFontRow; 38 39 uFontColumn = xPos / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING); 40 uFontRow = yPos / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING); 41 GetCharacterRect(uFontRow, uFontColumn, CharacterRect); 42 43 if(xPos > (UINT)CharacterRect->right || yPos > (UINT)CharacterRect->bottom) 44 // The user clicked on separator space, so return HITTEST_SEPARATOR 45 return HITTEST_SEPARATOR; 46 else 47 // Return the character number 48 return (uFontRow * 16 + uFontColumn); 49 } 50 51 static VOID 52 SetSelectedCharacter(IN PFONT_WND_INFO Info, IN UINT uNewCharacter, OPTIONAL IN LPRECT NewCharacterRect) 53 { 54 LPRECT pCharacterRect; 55 RECT OldCharacterRect; 56 UINT uFontColumn; 57 UINT uFontRow; 58 59 // Remove the selection of the old character 60 GetCharacterPosition(Info->uSelectedCharacter, &uFontRow, &uFontColumn); 61 GetCharacterRect(uFontRow, uFontColumn, &OldCharacterRect); 62 InvalidateRect(Info->hFontBoxesWnd, &OldCharacterRect, FALSE); 63 64 // You may pass the RECT of the new character, otherwise we'll allocate memory for one and get it ourselves 65 if(NewCharacterRect) 66 pCharacterRect = NewCharacterRect; 67 else 68 { 69 GetCharacterPosition(uNewCharacter, &uFontRow, &uFontColumn); 70 pCharacterRect = (LPRECT) HeapAlloc( hProcessHeap, 0, sizeof(RECT) ); 71 GetCharacterRect(uFontRow, uFontColumn, pCharacterRect); 72 } 73 74 // Select the new character 75 Info->uSelectedCharacter = uNewCharacter; 76 InvalidateRect(Info->hFontBoxesWnd, pCharacterRect, FALSE); 77 78 if(!NewCharacterRect) 79 HeapFree(hProcessHeap, 0, pCharacterRect); 80 } 81 82 static VOID 83 DrawProc(IN PFONT_WND_INFO Info, IN PAINTSTRUCT* ps) 84 { 85 COLORREF clBackground; 86 HBRUSH hBrush = 0; 87 HBRUSH hOldBrush = 0; 88 HDC hBoxDC; 89 HFONT hFont; 90 HFONT hOldFont; 91 RECT CharacterRect; 92 UINT uFontColumn; 93 UINT uStartColumn; 94 UINT uEndColumn; 95 UINT uFontRow; 96 UINT uStartRow; 97 UINT uEndRow; 98 UINT uCharacter; 99 UCHAR uCharacterColumn; 100 UCHAR uCharacterRow; 101 UCHAR uBit; 102 WCHAR szInfoText[9]; 103 HBITMAP hBitmapOld; 104 105 // Preparations 106 hBoxDC = CreateCompatibleDC(NULL); 107 hBitmapOld = SelectObject(hBoxDC, Info->MainWndInfo->hBoxBmp); 108 109 hFont = CreateFontW(13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Tahoma"); 110 hOldFont = SelectObject(ps->hdc, hFont); 111 112 SetBkMode( ps->hdc, TRANSPARENT ); 113 114 // What ranges do we have to draw? 115 uStartRow = ps->rcPaint.top / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING); 116 uEndRow = ps->rcPaint.bottom / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING); 117 uStartColumn = ps->rcPaint.left / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING); 118 uEndColumn = ps->rcPaint.right / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING); 119 120 for(uFontRow = uStartRow; uFontRow <= uEndRow; uFontRow++) 121 { 122 for(uFontColumn = uStartColumn; uFontColumn <= uEndColumn; uFontColumn++) 123 { 124 GetCharacterRect(uFontRow, uFontColumn, &CharacterRect); 125 uCharacter = uFontRow * 16 + uFontColumn; 126 127 // Draw the Character Info Box (header) 128 BitBlt(ps->hdc, 129 CharacterRect.left, 130 CharacterRect.top, 131 CHARACTER_BOX_WIDTH, 132 CHARACTER_INFO_BOX_HEIGHT, 133 hBoxDC, 134 0, 135 0, 136 SRCCOPY); 137 138 // Draw the header text 139 wsprintfW(szInfoText, L"%02u = %02X", uCharacter, uCharacter); 140 DrawTextW( ps->hdc, szInfoText, -1, &CharacterRect, DT_CENTER ); 141 142 // Draw the Character Bitmap Box (rectangle with the actual character) 143 if(Info->uSelectedCharacter == uCharacter) 144 { 145 clBackground = RGB(255, 255, 0); 146 hBrush = CreateSolidBrush(clBackground); 147 hOldBrush = SelectObject(ps->hdc, hBrush); 148 } 149 else 150 { 151 clBackground = RGB(255, 255, 255); 152 SelectObject( ps->hdc, GetStockObject(WHITE_BRUSH) ); 153 } 154 155 Rectangle(ps->hdc, 156 CharacterRect.left, 157 CharacterRect.top + CHARACTER_INFO_BOX_HEIGHT, 158 CharacterRect.right, 159 CharacterRect.bottom); 160 161 // Draw the actual character into the box 162 for(uCharacterRow = 0; uCharacterRow < 8; uCharacterRow++) 163 { 164 for(uCharacterColumn = 0; uCharacterColumn < 8; uCharacterColumn++) 165 { 166 uBit = Info->Font->Bits[uCharacter * 8 + uCharacterRow] << uCharacterColumn & 0x80; 167 DrawCharacterPixel(ps, uCharacter, uCharacterRow, uCharacterColumn, uBit, clBackground); 168 } 169 } 170 } 171 } 172 173 SelectObject(hBoxDC, hBitmapOld); 174 SelectObject(ps->hdc, hOldFont); 175 DeleteObject(hFont); 176 SelectObject(ps->hdc, hOldBrush); 177 DeleteObject(hBrush); 178 DeleteDC(hBoxDC); 179 } 180 181 VOID 182 EditCurrentGlyph(PFONT_WND_INFO FontWndInfo) 183 { 184 PEDIT_GLYPH_INFO EditGlyphInfo; 185 186 // Has the window for this character already been opened? 187 EditGlyphInfo = FontWndInfo->FirstEditGlyphWnd; 188 189 while(EditGlyphInfo) 190 { 191 if(EditGlyphInfo->uCharacter == FontWndInfo->uSelectedCharacter) 192 { 193 // Yes, it has. Bring it to the front. 194 SetFocus(EditGlyphInfo->hSelf); 195 return; 196 } 197 198 EditGlyphInfo = EditGlyphInfo->NextEditGlyphWnd; 199 } 200 201 // No. Then create a new one 202 EditGlyphInfo = (PEDIT_GLYPH_INFO) HeapAlloc( hProcessHeap, 0, sizeof(EDIT_GLYPH_INFO) ); 203 EditGlyphInfo->FontWndInfo = FontWndInfo; 204 EditGlyphInfo->uCharacter = FontWndInfo->uSelectedCharacter; 205 RtlCopyMemory( EditGlyphInfo->CharacterBits, FontWndInfo->Font->Bits + FontWndInfo->uSelectedCharacter * 8, sizeof(EditGlyphInfo->CharacterBits) ); 206 207 // Add the new window to the linked list 208 EditGlyphInfo->PrevEditGlyphWnd = FontWndInfo->LastEditGlyphWnd; 209 EditGlyphInfo->NextEditGlyphWnd = NULL; 210 211 if(FontWndInfo->LastEditGlyphWnd) 212 FontWndInfo->LastEditGlyphWnd->NextEditGlyphWnd = EditGlyphInfo; 213 else 214 FontWndInfo->FirstEditGlyphWnd = EditGlyphInfo; 215 216 FontWndInfo->LastEditGlyphWnd = EditGlyphInfo; 217 218 // Open the window as a modeless dialog, so people can edit several characters at the same time. 219 EditGlyphInfo->hSelf = CreateDialogParamW(hInstance, MAKEINTRESOURCEW(IDD_EDITGLYPH), FontWndInfo->hSelf, EditGlyphDlgProc, (LPARAM)EditGlyphInfo); 220 ShowWindow(EditGlyphInfo->hSelf, SW_SHOW); 221 } 222 223 VOID 224 CreateFontBoxesWindow(IN PFONT_WND_INFO FontWndInfo) 225 { 226 FontWndInfo->hFontBoxesWnd = CreateWindowExW(0, 227 szFontBoxesWndClass, 228 0, 229 WS_CHILD | WS_VISIBLE, 230 0, 231 0, 232 0, 233 0, 234 FontWndInfo->hSelf, 235 NULL, 236 hInstance, 237 FontWndInfo); 238 } 239 240 static LRESULT CALLBACK 241 FontBoxesWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 242 { 243 PFONT_WND_INFO Info; 244 245 Info = (PFONT_WND_INFO) GetWindowLongPtrW(hwnd, GWLP_USERDATA); 246 247 if(Info || uMsg == WM_CREATE) 248 { 249 switch(uMsg) 250 { 251 case WM_CREATE: 252 Info = (PFONT_WND_INFO)( ( (LPCREATESTRUCT)lParam )->lpCreateParams ); 253 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)Info); 254 255 // Set a fixed window size 256 SetWindowPos(hwnd, NULL, 0, 0, FONT_BOXES_WND_WIDTH, FONT_BOXES_WND_HEIGHT, SWP_NOZORDER | SWP_NOMOVE); 257 258 return 0; 259 260 case WM_DESTROY: 261 SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); 262 return 0; 263 264 case WM_KEYDOWN: 265 switch(wParam) 266 { 267 case VK_DOWN: 268 if(Info->uSelectedCharacter < 239) 269 SetSelectedCharacter(Info, Info->uSelectedCharacter + 16, NULL); 270 return 0; 271 272 case VK_LEFT: 273 if(Info->uSelectedCharacter) 274 SetSelectedCharacter(Info, Info->uSelectedCharacter - 1, NULL); 275 return 0; 276 277 case VK_RETURN: 278 EditCurrentGlyph(Info); 279 return 0; 280 281 case VK_RIGHT: 282 if(Info->uSelectedCharacter < 255) 283 SetSelectedCharacter(Info, Info->uSelectedCharacter + 1, NULL); 284 return 0; 285 286 case VK_UP: 287 if(Info->uSelectedCharacter > 15) 288 SetSelectedCharacter(Info, Info->uSelectedCharacter - 16, NULL); 289 return 0; 290 } 291 292 break; 293 294 case WM_LBUTTONDBLCLK: 295 { 296 EditCurrentGlyph(Info); 297 return 0; 298 } 299 300 case WM_LBUTTONDOWN: 301 { 302 RECT CharacterRect; 303 INT iRet; 304 305 iRet = FontBoxesHitTest( GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &CharacterRect ); 306 307 if(iRet >= 0) 308 SetSelectedCharacter( Info, (UINT)iRet, &CharacterRect ); 309 310 return 0; 311 } 312 313 case WM_PAINT: 314 { 315 PAINTSTRUCT ps; 316 317 BeginPaint(hwnd, &ps); 318 DrawProc(Info, &ps); 319 EndPaint(hwnd, &ps); 320 321 return 0; 322 } 323 } 324 } 325 326 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 327 } 328 329 BOOL 330 InitFontBoxesWndClass(VOID) 331 { 332 WNDCLASSW wc = {0,}; 333 334 wc.lpfnWndProc = FontBoxesWndProc; 335 wc.hInstance = hInstance; 336 wc.hCursor = LoadCursor( NULL, IDC_ARROW ); 337 wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 ); 338 wc.lpszClassName = szFontBoxesWndClass; 339 wc.style = CS_DBLCLKS; 340 341 return RegisterClassW(&wc) != 0; 342 } 343 344 VOID 345 UnInitFontBoxesWndClass(VOID) 346 { 347 UnregisterClassW(szFontBoxesWndClass, hInstance); 348 } 349