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
InitFont(IN PFONT_WND_INFO Info)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
FontWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)97 FontWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
98 {
99     PFONT_WND_INFO Info;
100 
101     Info = (PFONT_WND_INFO) GetWindowLongPtrW(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                 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)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                 SetWindowLongPtrW(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
CreateFontWindow(IN PMAIN_WND_INFO MainWndInfo,IN PFONT_OPEN_INFO OpenInfo)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 pch, pszWindowTitle;
378 
379             if(OpenInfo->pszFileName)
380             {
381                 pch = wcsrchr(OpenInfo->pszFileName, '\\');
382                 pszWindowTitle = (pch ? (pch + 1) : OpenInfo->pszFileName);
383             }
384             else
385             {
386                 LoadAndFormatString(IDS_DOCNAME, &pszWindowTitle, ++MainWndInfo->uDocumentCounter);
387             }
388 
389             hFontWnd = CreateMDIWindowW( szFontWndClass,
390                                          pszWindowTitle,
391                                          0,
392                                          CW_USEDEFAULT,
393                                          CW_USEDEFAULT,
394                                          FONT_WND_MIN_WIDTH,
395                                          FONT_WND_MIN_HEIGHT,
396                                          MainWndInfo->hMdiClient,
397                                          hInstance,
398                                          (LPARAM)Info );
399 
400             if(!OpenInfo->pszFileName)
401                 LocalFree(pszWindowTitle);
402 
403             if(hFontWnd)
404             {
405                 // Add the new window to the linked list
406                 Info->PrevFontWnd = Info->MainWndInfo->LastFontWnd;
407 
408                 if(Info->MainWndInfo->LastFontWnd)
409                     Info->MainWndInfo->LastFontWnd->NextFontWnd = Info;
410                 else
411                     Info->MainWndInfo->FirstFontWnd = Info;
412 
413                 Info->MainWndInfo->LastFontWnd = Info;
414 
415                 return TRUE;
416             }
417         }
418 
419         HeapFree(hProcessHeap, 0, Info);
420     }
421 
422     return FALSE;
423 }
424 
425 BOOL
InitFontWndClass(VOID)426 InitFontWndClass(VOID)
427 {
428     WNDCLASSW wc = {0,};
429 
430     wc.lpfnWndProc    = FontWndProc;
431     wc.hInstance      = hInstance;
432     wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
433     wc.hIcon          = LoadIconW( hInstance, MAKEINTRESOURCEW(IDI_DOC) );
434     wc.hbrBackground  = (HBRUSH)( COLOR_BTNFACE + 1 );
435     wc.lpszClassName  = szFontWndClass;
436 
437     return RegisterClassW(&wc) != 0;
438 }
439 
440 VOID
UnInitFontWndClass(VOID)441 UnInitFontWndClass(VOID)
442 {
443     UnregisterClassW(szFontWndClass, hInstance);
444 }
445