[DRWTSN32][SHIMDBG] Add missing va_end.
[reactos.git] / modules / rosapps / applications / devutils / vgafontedit / fontboxeswnd.c
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) GetWindowLongW(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 SetWindowLongW(hwnd, GWLP_USERDATA, (LONG)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 SetWindowLongW(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 }