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
DrawCharacterPixel(IN PAINTSTRUCT * ps,IN UINT uCharacter,IN UCHAR uRow,IN UCHAR uColumn,IN UCHAR uBit,IN COLORREF clBackground)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
GetCharacterRect(IN UINT uFontRow,IN UINT uFontColumn,OUT LPRECT CharacterRect)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
FontBoxesHitTest(IN UINT xPos,IN UINT yPos,OUT LPRECT CharacterRect)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
SetSelectedCharacter(IN PFONT_WND_INFO Info,IN UINT uNewCharacter,OPTIONAL IN LPRECT NewCharacterRect)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
DrawProc(IN PFONT_WND_INFO Info,IN PAINTSTRUCT * ps)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
EditCurrentGlyph(PFONT_WND_INFO FontWndInfo)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
CreateFontBoxesWindow(IN PFONT_WND_INFO FontWndInfo)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
FontBoxesWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)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
InitFontBoxesWndClass(VOID)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
UnInitFontBoxesWndClass(VOID)345 UnInitFontBoxesWndClass(VOID)
346 {
347 UnregisterClassW(szFontBoxesWndClass, hInstance);
348 }
349