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