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