[VGAFONTEDIT] Use the new header with SPDX license identifier and make the entire...
[reactos.git] / rosapps / applications / devutils / vgafontedit / fontwnd.c
1 /*
2 * PROJECT: ReactOS VGA Font Editor
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Implements the MDI child window for a font
5 * COPYRIGHT: Copyright 2008 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 static const WCHAR szFontWndClass[] = L"VGAFontEditFontWndClass";
11
12 static BOOL
13 InitFont(IN PFONT_WND_INFO Info)
14 {
15 Info->Font = (PBITMAP_FONT) HeapAlloc( hProcessHeap, 0, sizeof(BITMAP_FONT) );
16
17 if(Info->OpenInfo->bCreateNew)
18 {
19 ZeroMemory( Info->Font, sizeof(BITMAP_FONT) );
20 return TRUE;
21 }
22 else
23 {
24 // Load a font
25 BOOL bRet = FALSE;
26 DWORD dwTemp;
27 HANDLE hFile;
28
29 hFile = CreateFileW(Info->OpenInfo->pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
30
31 if(hFile == INVALID_HANDLE_VALUE)
32 {
33 LocalizedError( IDS_OPENERROR, GetLastError() );
34 return FALSE;
35 }
36
37 // Let's first check the file size to determine the file type
38 dwTemp = GetFileSize(hFile, NULL);
39
40 switch(dwTemp)
41 {
42 case 2048:
43 // It should be a binary font file
44 Info->OpenInfo->bBinaryFileOpened = TRUE;
45
46 if( ReadFile(hFile, Info->Font, sizeof(BITMAP_FONT), &dwTemp, NULL) )
47 bRet = TRUE;
48 else
49 LocalizedError( IDS_READERROR, GetLastError() );
50
51 break;
52
53 case 2052:
54 {
55 PSF1_HEADER Header;
56
57 // Probably it's a PSFv1 file, check the header to make sure
58 if( !ReadFile(hFile, &Header, sizeof(PSF1_HEADER) , &dwTemp, NULL) )
59 {
60 LocalizedError( IDS_READERROR, GetLastError() );
61 break;
62 }
63 else
64 {
65 if(Header.uMagic[0] == PSF1_MAGIC0 && Header.uMagic[1] == PSF1_MAGIC1)
66 {
67 // Yes, it is a PSFv1 file.
68 // Check the mode and character size. We only support 8x8 fonts with no special mode.
69 if(Header.uCharSize == 8 && Header.uMode == 0)
70 {
71 // Perfect! The file pointer is already set correctly, so we can just read the font bitmap now.
72 if( ReadFile(hFile, Info->Font, sizeof(BITMAP_FONT), &dwTemp, NULL) )
73 bRet = TRUE;
74 else
75 LocalizedError( IDS_READERROR, GetLastError() );
76 }
77 else
78 LocalizedError(IDS_UNSUPPORTEDPSF);
79
80 break;
81 }
82
83 // Fall through if the magic numbers aren't there
84 }
85 }
86
87 default:
88 LocalizedError(IDS_UNSUPPORTEDFORMAT);
89 }
90
91 CloseHandle(hFile);
92 return bRet;
93 }
94 }
95
96 static LRESULT CALLBACK
97 FontWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
98 {
99 PFONT_WND_INFO Info;
100
101 Info = (PFONT_WND_INFO) GetWindowLongW(hwnd, GWLP_USERDATA);
102
103 if(Info || uMsg == WM_CREATE)
104 {
105 switch(uMsg)
106 {
107 case WM_CHILDACTIVATE:
108 Info->MainWndInfo->CurrentFontWnd = Info;
109 SetToolbarFileButtonState(Info->MainWndInfo, TRUE);
110 SetPasteButtonState(Info->MainWndInfo);
111 break;
112
113 case WM_CREATE:
114 Info = (PFONT_WND_INFO)( ( (LPMDICREATESTRUCT) ( (LPCREATESTRUCT)lParam )->lpCreateParams )->lParam );
115 Info->hSelf = hwnd;
116
117 SetWindowLongW(hwnd, GWLP_USERDATA, (LONG)Info);
118
119 CreateFontBoxesWindow(Info);
120
121 return 0;
122
123 case WM_USER_APPCLOSE:
124 case WM_CLOSE:
125 // The user has to close all open edit dialogs first
126 if(Info->FirstEditGlyphWnd)
127 {
128 PWSTR pszMessage;
129
130 AllocAndLoadString(&pszMessage, IDS_CLOSEEDIT);
131 MessageBoxW(hwnd, pszMessage, szAppName, MB_OK | MB_ICONEXCLAMATION);
132 HeapFree(hProcessHeap, 0, pszMessage);
133
134 return 0;
135 }
136
137 // Prompt if the current file has been modified
138 if(Info->OpenInfo->bModified)
139 {
140 INT nMsgBoxResult;
141 PWSTR pszPrompt;
142 WCHAR szFile[MAX_PATH];
143
144 GetWindowTextW(hwnd, szFile, MAX_PATH);
145 LoadAndFormatString(IDS_SAVEPROMPT, &pszPrompt, szFile);
146
147 nMsgBoxResult = MessageBoxW(hwnd, pszPrompt, szAppName, MB_YESNOCANCEL | MB_ICONQUESTION);
148 LocalFree(pszPrompt);
149
150 switch(nMsgBoxResult)
151 {
152 case IDYES:
153 DoFileSave(Info->MainWndInfo, FALSE);
154 break;
155
156 case IDCANCEL:
157 // 0 = Stop the process of closing the windows (same value for both WM_CLOSE and WM_USER_APPCLOSE)
158 return 0;
159
160 // IDNO is handled automatically
161 }
162 }
163
164 // If there is another child, it will undo the following actions through its WM_CHILDACTIVATE handler.
165 // Otherwise CurrentFontWnd will stay NULL, so the main window knows that no more childs are opened.
166 Info->MainWndInfo->CurrentFontWnd = NULL;
167 SetToolbarFileButtonState(Info->MainWndInfo, FALSE);
168 SetPasteButtonState(Info->MainWndInfo);
169
170 if(uMsg == WM_USER_APPCLOSE)
171 {
172 // First do the tasks we would do for a normal WM_CLOSE message, then return the value for WM_USER_APPCLOSE
173 // Anything other than 0 indicates that the application shall continue closing the windows
174 DefMDIChildProcW(hwnd, WM_CLOSE, 0, 0);
175 return 1;
176 }
177 break;
178
179 case WM_DESTROY:
180 // Remove the window from the linked list
181 if(Info->PrevFontWnd)
182 Info->PrevFontWnd->NextFontWnd = Info->NextFontWnd;
183 else
184 Info->MainWndInfo->FirstFontWnd = Info->NextFontWnd;
185
186 if(Info->NextFontWnd)
187 Info->NextFontWnd->PrevFontWnd = Info->PrevFontWnd;
188 else
189 Info->MainWndInfo->LastFontWnd = Info->PrevFontWnd;
190
191 // Free memory
192 if(Info->Font)
193 HeapFree(hProcessHeap, 0, Info->Font);
194
195 if(Info->OpenInfo->pszFileName)
196 HeapFree(hProcessHeap, 0, Info->OpenInfo->pszFileName);
197
198 HeapFree(hProcessHeap, 0, Info->OpenInfo);
199 HeapFree(hProcessHeap, 0, Info);
200
201 SetWindowLongW(hwnd, GWLP_USERDATA, 0);
202 return 0;
203
204 case WM_SETFOCUS:
205 // Set the keyboard focus to the FontBoxes window every time the Font window gets the focus
206 SetFocus(Info->hFontBoxesWnd);
207 break;
208
209 case WM_SIZE:
210 {
211 INT nHeight = HIWORD(lParam);
212 INT nWidth = LOWORD(lParam);
213 POINT pt;
214 RECT WndRect;
215
216 // This ugly workaround is necessary for not setting either the Height or the Width of the window with SetWindowPos
217 GetWindowRect(Info->hFontBoxesWnd, &WndRect);
218 pt.x = WndRect.left;
219 pt.y = WndRect.top;
220 ScreenToClient(hwnd, &pt);
221
222 if(nHeight < FONT_BOXES_WND_HEIGHT)
223 {
224 SCROLLINFO si;
225
226 // Set the vertical scroll bar
227 si.cbSize = sizeof(si);
228 si.fMask = SIF_RANGE | SIF_PAGE;
229 si.nMin = 0;
230 si.nMax = FONT_BOXES_WND_HEIGHT;
231 si.nPage = nHeight;
232 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
233 }
234 else
235 {
236 ShowScrollBar(hwnd, SB_VERT, FALSE);
237
238 // Store the new y coordinate in pt.y as well (needed for the SetWindowPos call for setting a new x coordinate)
239 pt.y = nHeight / 2 - FONT_BOXES_WND_HEIGHT / 2;
240 SetWindowPos(Info->hFontBoxesWnd,
241 NULL,
242 pt.x,
243 pt.y,
244 0,
245 0,
246 SWP_NOSIZE | SWP_NOZORDER);
247 }
248
249 if(nWidth < FONT_BOXES_WND_WIDTH)
250 {
251 SCROLLINFO si;
252
253 // Set the horizontal scroll bar
254 si.cbSize = sizeof(si);
255 si.fMask = SIF_RANGE | SIF_PAGE;
256 si.nMin = 0;
257 si.nMax = FONT_BOXES_WND_WIDTH;
258 si.nPage = nWidth;
259 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
260 }
261 else
262 {
263 ShowScrollBar(hwnd, SB_HORZ, FALSE);
264
265 SetWindowPos(Info->hFontBoxesWnd,
266 NULL,
267 nWidth / 2 - FONT_BOXES_WND_WIDTH / 2,
268 pt.y,
269 0,
270 0,
271 SWP_NOSIZE | SWP_NOZORDER);
272 }
273
274 // We have to call DefMDIChildProcW here as well, otherwise we won't get the Minimize/Maximize/Close buttons for a maximized MDI child.
275 break;
276 }
277
278 case WM_HSCROLL:
279 case WM_VSCROLL:
280 {
281 INT nBar;
282 INT nOrgPos;
283 SCROLLINFO si;
284
285 if(uMsg == WM_HSCROLL)
286 nBar = SB_HORZ;
287 else
288 nBar = SB_VERT;
289
290 si.cbSize = sizeof(si);
291 si.fMask = SIF_ALL;
292 GetScrollInfo(hwnd, nBar, &si);
293
294 nOrgPos = si.nPos;
295
296 switch( LOWORD(wParam) )
297 {
298 // Constant is the same as SB_LEFT for WM_HSCROLL
299 case SB_TOP:
300 si.nPos = si.nMin;
301 break;
302
303 // Constant is the same as SB_RIGHT for WM_HSCROLL
304 case SB_BOTTOM:
305 si.nPos = si.nMax;
306 break;
307
308 // Constant is the same as SB_LINELEFT for WM_HSCROLL
309 case SB_LINEUP:
310 si.nPos -= 20;
311 break;
312
313 // Constant is the same as SB_LINERIGHT for WM_HSCROLL
314 case SB_LINEDOWN:
315 si.nPos += 20;
316 break;
317
318 // Constant is the same as SB_PAGELEFT for WM_HSCROLL
319 case SB_PAGEUP:
320 si.nPos -= si.nPage;
321 break;
322
323 // Constant is the same as SB_PAGERIGHT for WM_HSCROLL
324 case SB_PAGEDOWN:
325 si.nPos += si.nPage;
326 break;
327
328 case SB_THUMBTRACK:
329 si.nPos = si.nTrackPos;
330 break;
331 }
332
333 si.fMask = SIF_POS;
334 SetScrollInfo(hwnd, nBar, &si, TRUE);
335 GetScrollInfo(hwnd, nBar, &si);
336
337 if(si.nPos != nOrgPos)
338 {
339 // This ugly workaround is necessary for not setting the x coordinate
340 POINT pt;
341 RECT WndRect;
342
343 GetWindowRect(Info->hFontBoxesWnd, &WndRect);
344 pt.x = WndRect.left;
345 pt.y = WndRect.top;
346 ScreenToClient(hwnd, &pt);
347
348 if(uMsg == WM_HSCROLL)
349 SetWindowPos(Info->hFontBoxesWnd, NULL, -si.nPos, pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
350 else
351 SetWindowPos(Info->hFontBoxesWnd, NULL, pt.x, -si.nPos, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
352 }
353
354 return 0;
355 }
356 }
357 }
358
359 return DefMDIChildProcW(hwnd, uMsg, wParam, lParam);
360 }
361
362 BOOL
363 CreateFontWindow(IN PMAIN_WND_INFO MainWndInfo, IN PFONT_OPEN_INFO OpenInfo)
364 {
365 HWND hFontWnd;
366 PFONT_WND_INFO Info;
367
368 Info = (PFONT_WND_INFO) HeapAlloc( hProcessHeap, HEAP_ZERO_MEMORY, sizeof(FONT_WND_INFO) );
369
370 if(Info)
371 {
372 Info->MainWndInfo = MainWndInfo;
373 Info->OpenInfo = OpenInfo;
374
375 if( InitFont(Info) )
376 {
377 PWSTR pszWindowTitle;
378
379 if(OpenInfo->pszFileName)
380 pszWindowTitle = wcsrchr(OpenInfo->pszFileName, '\\') + 1;
381 else
382 LoadAndFormatString(IDS_DOCNAME, &pszWindowTitle, ++MainWndInfo->uDocumentCounter);
383
384 hFontWnd = CreateMDIWindowW( szFontWndClass,
385 pszWindowTitle,
386 0,
387 CW_USEDEFAULT,
388 CW_USEDEFAULT,
389 FONT_WND_MIN_WIDTH,
390 FONT_WND_MIN_HEIGHT,
391 MainWndInfo->hMdiClient,
392 hInstance,
393 (LPARAM)Info );
394
395 if(!OpenInfo->pszFileName)
396 LocalFree(pszWindowTitle);
397
398 if(hFontWnd)
399 {
400 // Add the new window to the linked list
401 Info->PrevFontWnd = Info->MainWndInfo->LastFontWnd;
402
403 if(Info->MainWndInfo->LastFontWnd)
404 Info->MainWndInfo->LastFontWnd->NextFontWnd = Info;
405 else
406 Info->MainWndInfo->FirstFontWnd = Info;
407
408 Info->MainWndInfo->LastFontWnd = Info;
409
410 return TRUE;
411 }
412 }
413
414 HeapFree(hProcessHeap, 0, Info);
415 }
416
417 return FALSE;
418 }
419
420 BOOL
421 InitFontWndClass(VOID)
422 {
423 WNDCLASSW wc = {0,};
424
425 wc.lpfnWndProc = FontWndProc;
426 wc.hInstance = hInstance;
427 wc.hCursor = LoadCursor( NULL, IDC_ARROW );
428 wc.hIcon = LoadIconW( hInstance, MAKEINTRESOURCEW(IDI_DOC) );
429 wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 );
430 wc.lpszClassName = szFontWndClass;
431
432 return RegisterClassW(&wc) != 0;
433 }
434
435 VOID
436 UnInitFontWndClass(VOID)
437 {
438 UnregisterClassW(szFontWndClass, hInstance);
439 }