[DRWTSN32][SHIMDBG] Add missing va_end.
[reactos.git] / modules / rosapps / applications / devutils / vgafontedit / mainwnd.c
1 /*
2 * PROJECT: ReactOS VGA Font Editor
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Implements the main window of the application
5 * COPYRIGHT: Copyright 2008 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 static const WCHAR szMainWndClass[] = L"VGAFontEditMainWndClass";
11
12 static VOID
13 InitResources(IN PMAIN_WND_INFO Info)
14 {
15 HDC hMemDC;
16 HDC hMainDC;
17 HPEN hPen, hPenOld;
18 RECT rect;
19 HBITMAP hBitmapOld;
20
21 hMemDC = CreateCompatibleDC(NULL);
22 hMainDC = GetDC(Info->hMainWnd);
23
24 // Create the "Box" bitmap
25 Info->hBoxBmp = CreateCompatibleBitmap(hMainDC, CHARACTER_BOX_WIDTH, CHARACTER_BOX_HEIGHT);
26 hBitmapOld = SelectObject(hMemDC, Info->hBoxBmp);
27
28 rect.left = 0;
29 rect.top = 0;
30 rect.right = CHARACTER_INFO_BOX_WIDTH;
31 rect.bottom = CHARACTER_INFO_BOX_HEIGHT;
32 FillRect( hMemDC, &rect, (HBRUSH)(COLOR_BTNFACE + 1) );
33
34 hPenOld = SelectObject( hMemDC, GetStockObject(WHITE_PEN) );
35 Rectangle(hMemDC, 0, 0, CHARACTER_INFO_BOX_WIDTH - 1, 2);
36 Rectangle(hMemDC, 0, 2, 2, CHARACTER_INFO_BOX_HEIGHT - 1);
37 hPen = SelectObject(hMemDC, hPenOld);
38
39 hPen = CreatePen( PS_SOLID, 1, RGB(128, 128, 128) );
40 hPenOld = SelectObject(hMemDC, hPen);
41 Rectangle(hMemDC, 1, CHARACTER_INFO_BOX_HEIGHT - 2, CHARACTER_INFO_BOX_WIDTH, CHARACTER_INFO_BOX_HEIGHT);
42 Rectangle(hMemDC, CHARACTER_INFO_BOX_WIDTH - 2, 1, CHARACTER_INFO_BOX_WIDTH, CHARACTER_INFO_BOX_HEIGHT - 2);
43
44 SetPixel( hMemDC, CHARACTER_INFO_BOX_WIDTH - 1, 0, RGB(128, 128, 128) );
45 SetPixel( hMemDC, 0, CHARACTER_INFO_BOX_HEIGHT - 1, RGB(128, 128, 128) );
46 SelectObject(hMemDC, hBitmapOld);
47
48 hPen = SelectObject(hMemDC, hPenOld);
49 DeleteObject(hPen);
50 DeleteDC(hMemDC);
51 ReleaseDC(Info->hMainWnd, hMainDC);
52 }
53
54 static VOID
55 UnInitResources(IN PMAIN_WND_INFO Info)
56 {
57 DeleteObject(Info->hBoxBmp);
58 }
59
60 static VOID
61 AddToolbarButton(IN PMAIN_WND_INFO Info, IN INT iBitmap, IN INT idCommand, IN UINT uID)
62 {
63 PWSTR pszTooltip;
64 TBBUTTON tbb = {0,};
65
66 if( AllocAndLoadString(&pszTooltip, uID) )
67 {
68 tbb.fsState = TBSTATE_ENABLED;
69 tbb.iBitmap = iBitmap;
70 tbb.idCommand = idCommand;
71 tbb.iString = (INT_PTR)pszTooltip;
72
73 SendMessageW( Info->hToolbar, TB_ADDBUTTONSW, 1, (LPARAM)&tbb );
74 HeapFree(hProcessHeap, 0, pszTooltip);
75 }
76 }
77
78 static VOID
79 SetToolbarButtonState(IN PMAIN_WND_INFO Info, INT idCommand, BOOL bEnabled)
80 {
81 TBBUTTONINFOW tbbi = {0,};
82
83 tbbi.cbSize = sizeof(tbbi);
84 tbbi.dwMask = TBIF_STATE;
85 tbbi.fsState = (bEnabled ? TBSTATE_ENABLED : 0);
86
87 SendMessageW(Info->hToolbar, TB_SETBUTTONINFOW, idCommand, (LPARAM)&tbbi);
88 }
89
90 VOID
91 SetToolbarFileButtonState(IN PMAIN_WND_INFO Info, BOOL bEnabled)
92 {
93 SetToolbarButtonState(Info, ID_FILE_SAVE, bEnabled);
94 SetToolbarButtonState(Info, ID_EDIT_GLYPH, bEnabled);
95 SetToolbarButtonState(Info, ID_EDIT_COPY, bEnabled);
96 }
97
98 static VOID
99 AddToolbarSeparator(IN PMAIN_WND_INFO Info)
100 {
101 TBBUTTON tbb = {0,};
102
103 tbb.fsStyle = BTNS_SEP;
104
105 SendMessageW( Info->hToolbar, TB_ADDBUTTONSW, 1, (LPARAM)&tbb );
106 }
107
108 static VOID
109 InitMainWnd(IN PMAIN_WND_INFO Info)
110 {
111 CLIENTCREATESTRUCT ccs;
112 INT iCustomBitmaps;
113 INT iStandardBitmaps;
114 TBADDBITMAP tbab;
115
116 // Add the toolbar
117 Info->hToolbar = CreateWindowExW(0,
118 TOOLBARCLASSNAMEW,
119 NULL,
120 WS_VISIBLE | WS_CHILD | TBSTYLE_TOOLTIPS,
121 0,
122 0,
123 0,
124 0,
125 Info->hMainWnd,
126 NULL,
127 hInstance,
128 NULL);
129
130 // Identify the used Common Controls version
131 SendMessageW(Info->hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
132
133 // Enable Tooltips
134 SendMessageW(Info->hToolbar, TB_SETMAXTEXTROWS, 0, 0);
135
136 // Add the toolbar bitmaps
137 tbab.hInst = HINST_COMMCTRL;
138 tbab.nID = IDB_STD_SMALL_COLOR;
139 iStandardBitmaps = (INT)SendMessageW(Info->hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
140
141 tbab.hInst = hInstance;
142 tbab.nID = IDB_MAIN_TOOLBAR;
143 iCustomBitmaps = (INT)SendMessageW(Info->hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
144
145 // Add the toolbar buttons
146 AddToolbarButton(Info, iStandardBitmaps + STD_FILENEW, ID_FILE_NEW, IDS_TOOLTIP_NEW);
147 AddToolbarButton(Info, iStandardBitmaps + STD_FILEOPEN, ID_FILE_OPEN, IDS_TOOLTIP_OPEN);
148 AddToolbarButton(Info, iStandardBitmaps + STD_FILESAVE, ID_FILE_SAVE, IDS_TOOLTIP_SAVE);
149 AddToolbarSeparator(Info);
150 AddToolbarButton(Info, iCustomBitmaps + TOOLBAR_EDIT_GLYPH, ID_EDIT_GLYPH, IDS_TOOLTIP_EDIT_GLYPH);
151 AddToolbarSeparator(Info);
152 AddToolbarButton(Info, iStandardBitmaps + STD_COPY, ID_EDIT_COPY, IDS_TOOLTIP_COPY);
153 AddToolbarButton(Info, iStandardBitmaps + STD_PASTE, ID_EDIT_PASTE, IDS_TOOLTIP_PASTE);
154
155 SetToolbarFileButtonState(Info, FALSE);
156 SetPasteButtonState(Info);
157
158 // Add the MDI client area
159 ccs.hWindowMenu = GetSubMenu(Info->hMenu, 2);
160 ccs.idFirstChild = ID_MDI_FIRSTCHILD;
161
162 Info->hMdiClient = CreateWindowExW(WS_EX_CLIENTEDGE,
163 L"MDICLIENT",
164 NULL,
165 WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL,
166 0,
167 0,
168 0,
169 0,
170 Info->hMainWnd,
171 NULL,
172 hInstance,
173 &ccs);
174
175 // Initialize the file handling
176 FileInitialize(Info->hMainWnd);
177 }
178
179 static VOID
180 InitMenuPopup(IN PMAIN_WND_INFO Info)
181 {
182 UINT uState;
183
184 uState = MF_BYCOMMAND | !(Info->CurrentFontWnd);
185
186 EnableMenuItem(Info->hMenu, ID_FILE_CLOSE, uState);
187 EnableMenuItem(Info->hMenu, ID_FILE_SAVE, uState);
188 EnableMenuItem(Info->hMenu, ID_FILE_SAVE_AS, uState);
189
190 EnableMenuItem(Info->hMenu, ID_EDIT_COPY, uState);
191 EnableMenuItem(Info->hMenu, ID_EDIT_GLYPH, uState);
192
193 uState = MF_BYCOMMAND | !(Info->CurrentFontWnd && IsClipboardFormatAvailable(uCharacterClipboardFormat));
194 EnableMenuItem(Info->hMenu, ID_EDIT_PASTE, uState);
195 }
196
197 static VOID
198 DoFileNew(IN PMAIN_WND_INFO Info)
199 {
200 PFONT_OPEN_INFO OpenInfo;
201
202 OpenInfo = (PFONT_OPEN_INFO) HeapAlloc( hProcessHeap, HEAP_ZERO_MEMORY, sizeof(FONT_OPEN_INFO) );
203 OpenInfo->bCreateNew = TRUE;
204
205 CreateFontWindow(Info, OpenInfo);
206 }
207
208 static VOID
209 DoFileOpen(IN PMAIN_WND_INFO Info)
210 {
211 PFONT_OPEN_INFO OpenInfo;
212
213 OpenInfo = (PFONT_OPEN_INFO) HeapAlloc( hProcessHeap, HEAP_ZERO_MEMORY, sizeof(FONT_OPEN_INFO) );
214 OpenInfo->pszFileName = HeapAlloc(hProcessHeap, 0, MAX_PATH);
215 OpenInfo->pszFileName[0] = 0;
216
217 if( DoOpenFile(OpenInfo->pszFileName) )
218 {
219 OpenInfo->bCreateNew = FALSE;
220 CreateFontWindow(Info, OpenInfo);
221 }
222 }
223
224 VOID
225 DoFileSave(IN PMAIN_WND_INFO Info, IN BOOL bSaveAs)
226 {
227 DWORD dwBytesWritten;
228 HANDLE hFile;
229
230 // Show the "Save" dialog
231 // - if "Save As" was clicked
232 // - if the file was not yet saved
233 // - if another format than the binary format was opened
234 if(bSaveAs || !Info->CurrentFontWnd->OpenInfo->bBinaryFileOpened)
235 {
236 if(!Info->CurrentFontWnd->OpenInfo->pszFileName)
237 {
238 Info->CurrentFontWnd->OpenInfo->pszFileName = (PWSTR) HeapAlloc(hProcessHeap, 0, MAX_PATH);
239 Info->CurrentFontWnd->OpenInfo->pszFileName[0] = 0;
240 }
241 else if(!Info->CurrentFontWnd->OpenInfo->bBinaryFileOpened)
242 {
243 // For a file in another format, the user has to enter a new file name as well
244 Info->CurrentFontWnd->OpenInfo->pszFileName[0] = 0;
245 }
246
247 if( !DoSaveFile(Info->CurrentFontWnd->OpenInfo->pszFileName) )
248 return;
249 }
250
251 // Save the binary font
252 hFile = CreateFileW(Info->CurrentFontWnd->OpenInfo->pszFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
253
254 if(hFile == INVALID_HANDLE_VALUE)
255 {
256 LocalizedError( IDS_OPENERROR, GetLastError() );
257 return;
258 }
259
260 if( !WriteFile(hFile, Info->CurrentFontWnd->Font, sizeof(BITMAP_FONT), &dwBytesWritten, NULL) )
261 LocalizedError( IDS_WRITEERROR, GetLastError() );
262
263 CloseHandle(hFile);
264 }
265
266 static VOID
267 CopyCurrentGlyph(IN PFONT_WND_INFO FontWndInfo)
268 {
269 HGLOBAL hMem;
270 PUCHAR pCharacterBits;
271
272 if(!OpenClipboard(NULL))
273 return;
274
275 EmptyClipboard();
276
277 hMem = GlobalAlloc(GMEM_MOVEABLE, 8);
278 pCharacterBits = GlobalLock(hMem);
279 RtlCopyMemory(pCharacterBits, FontWndInfo->Font->Bits + FontWndInfo->uSelectedCharacter * 8, 8);
280 GlobalUnlock(hMem);
281
282 SetClipboardData(uCharacterClipboardFormat, hMem);
283
284 CloseClipboard();
285 }
286
287 static VOID
288 PasteIntoCurrentGlyph(IN PFONT_WND_INFO FontWndInfo)
289 {
290 HGLOBAL hMem;
291
292 if(!IsClipboardFormatAvailable(uCharacterClipboardFormat))
293 return;
294
295 if(!OpenClipboard(NULL))
296 return;
297
298 hMem = GetClipboardData(uCharacterClipboardFormat);
299 if(hMem)
300 {
301 PUCHAR pCharacterBits;
302
303 pCharacterBits = GlobalLock(hMem);
304 if(pCharacterBits)
305 {
306 RECT CharacterRect;
307 UINT uFontRow;
308 UINT uFontColumn;
309
310 RtlCopyMemory(FontWndInfo->Font->Bits + FontWndInfo->uSelectedCharacter * 8, pCharacterBits, 8);
311 GlobalUnlock(hMem);
312
313 FontWndInfo->OpenInfo->bModified = TRUE;
314
315 GetCharacterPosition(FontWndInfo->uSelectedCharacter, &uFontRow, &uFontColumn);
316 GetCharacterRect(uFontRow, uFontColumn, &CharacterRect);
317 InvalidateRect(FontWndInfo->hFontBoxesWnd, &CharacterRect, FALSE);
318 }
319 }
320
321 CloseClipboard();
322 }
323
324 VOID
325 SetPasteButtonState(IN PMAIN_WND_INFO Info)
326 {
327 SetToolbarButtonState(Info,
328 ID_EDIT_PASTE,
329 (Info->CurrentFontWnd && IsClipboardFormatAvailable(uCharacterClipboardFormat)));
330 }
331
332 static BOOL
333 MenuCommand(IN INT nMenuItemID, IN PMAIN_WND_INFO Info)
334 {
335 switch(nMenuItemID)
336 {
337 // File Menu
338 case ID_FILE_NEW:
339 DoFileNew(Info);
340 return TRUE;
341
342 case ID_FILE_OPEN:
343 DoFileOpen(Info);
344 return TRUE;
345
346 case ID_FILE_CLOSE:
347 SendMessageW(Info->CurrentFontWnd->hSelf, WM_CLOSE, 0, 0);
348 return TRUE;
349
350 case ID_FILE_SAVE:
351 DoFileSave(Info, FALSE);
352 return TRUE;
353
354 case ID_FILE_SAVE_AS:
355 DoFileSave(Info, TRUE);
356 return TRUE;
357
358 case ID_FILE_EXIT:
359 PostMessage(Info->hMainWnd, WM_CLOSE, 0, 0);
360 return TRUE;
361
362 // Edit Menu
363 case ID_EDIT_GLYPH:
364 EditCurrentGlyph(Info->CurrentFontWnd);
365 return TRUE;
366
367 case ID_EDIT_COPY:
368 CopyCurrentGlyph(Info->CurrentFontWnd);
369 return TRUE;
370
371 case ID_EDIT_PASTE:
372 PasteIntoCurrentGlyph(Info->CurrentFontWnd);
373 return TRUE;
374
375 // Window Menu
376 case ID_WINDOW_TILE_HORZ:
377 SendMessageW(Info->hMdiClient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
378 return TRUE;
379
380 case ID_WINDOW_TILE_VERT:
381 SendMessageW(Info->hMdiClient, WM_MDITILE, MDITILE_VERTICAL, 0);
382 return TRUE;
383
384 case ID_WINDOW_CASCADE:
385 SendMessageW(Info->hMdiClient, WM_MDICASCADE, 0, 0);
386 return TRUE;
387
388 case ID_WINDOW_ARRANGE:
389 SendMessageW(Info->hMdiClient, WM_MDIICONARRANGE, 0, 0);
390 return TRUE;
391
392 case ID_WINDOW_NEXT:
393 SendMessageW(Info->hMdiClient, WM_MDINEXT, 0, 0);
394 return TRUE;
395
396 // Help Menu
397 case ID_HELP_ABOUT:
398 DialogBoxW( hInstance, MAKEINTRESOURCEW(IDD_ABOUT), Info->hMainWnd, AboutDlgProc );
399 return TRUE;
400 }
401
402 return FALSE;
403 }
404
405 static VOID
406 MainWndSize(PMAIN_WND_INFO Info, INT cx, INT cy)
407 {
408 HDWP dwp;
409 INT iMdiTop;
410 RECT ToolbarRect;
411
412 iMdiTop = 0;
413
414 dwp = BeginDeferWindowPos(2);
415 if(!dwp)
416 return;
417
418 if(Info->hToolbar)
419 {
420 GetWindowRect(Info->hToolbar, &ToolbarRect);
421 iMdiTop += ToolbarRect.bottom - ToolbarRect.top;
422
423 dwp = DeferWindowPos(dwp, Info->hToolbar, NULL, 0, 0, cx, ToolbarRect.bottom - ToolbarRect.top, SWP_NOZORDER);
424 if(!dwp)
425 return;
426 }
427
428 if(Info->hMdiClient)
429 {
430 dwp = DeferWindowPos(dwp, Info->hMdiClient, NULL, 0, iMdiTop, cx, cy - iMdiTop, SWP_NOZORDER);
431 if(!dwp)
432 return;
433 }
434
435 EndDeferWindowPos(dwp);
436 }
437
438 static LRESULT CALLBACK
439 MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
440 {
441 static HWND hNextClipboardViewer;
442
443 PMAIN_WND_INFO Info;
444
445 Info = (PMAIN_WND_INFO) GetWindowLongW(hwnd, GWLP_USERDATA);
446
447 if(Info || uMsg == WM_CREATE)
448 {
449 switch(uMsg)
450 {
451 case WM_COMMAND:
452 if( MenuCommand( LOWORD(wParam), Info ) )
453 return 0;
454
455 break;
456
457 case WM_CHANGECBCHAIN:
458 if((HWND)wParam == hNextClipboardViewer)
459 hNextClipboardViewer = (HWND)lParam;
460 else
461 SendMessage(hNextClipboardViewer, uMsg, wParam, lParam);
462
463 return 0;
464
465 case WM_CLOSE:
466 if(Info->FirstFontWnd)
467 {
468 // Send WM_CLOSE to all subwindows, so they can prompt for saving unsaved files
469 PFONT_WND_INFO pNextWnd;
470 PFONT_WND_INFO pWnd;
471
472 pWnd = Info->FirstFontWnd;
473
474 do
475 {
476 // The pWnd structure might already be destroyed after the WM_CLOSE, so we have to preserve the address of the next window here
477 pNextWnd = pWnd->NextFontWnd;
478
479 // Send WM_USER_APPCLOSE, so we can check for a custom return value
480 // In this case, we check if the user clicked the "Cancel" button in one of the prompts and if so, we don't close the app
481 if( !SendMessage(pWnd->hSelf, WM_USER_APPCLOSE, 0, 0) )
482 return 0;
483 }
484 while( (pWnd = pNextWnd) );
485 }
486 break;
487
488 case WM_CREATE:
489 Info = (PMAIN_WND_INFO)( ( (LPCREATESTRUCT)lParam )->lpCreateParams );
490 Info->hMainWnd = hwnd;
491 Info->hMenu = GetMenu(hwnd);
492 SetWindowLongW(hwnd, GWLP_USERDATA, (LONG)Info);
493
494 hNextClipboardViewer = SetClipboardViewer(hwnd);
495
496 InitMainWnd(Info);
497 InitResources(Info);
498
499 ShowWindow(hwnd, Info->nCmdShow);
500 return 0;
501
502 case WM_DESTROY:
503 UnInitResources(Info);
504
505 HeapFree(hProcessHeap, 0, Info);
506 SetWindowLongW(hwnd, GWLP_USERDATA, 0);
507 PostQuitMessage(0);
508 return 0;
509
510 case WM_DRAWCLIPBOARD:
511 SetPasteButtonState(Info);
512
513 // Pass the message to the next clipboard window in the chain
514 SendMessage(hNextClipboardViewer, uMsg, wParam, lParam);
515 return 0;
516
517 case WM_INITMENUPOPUP:
518 InitMenuPopup(Info);
519 break;
520
521 case WM_SIZE:
522 MainWndSize( Info, LOWORD(lParam), HIWORD(lParam) );
523 return 0;
524 }
525 }
526
527 if(Info && Info->hMdiClient)
528 return DefFrameProcW(hwnd, Info->hMdiClient, uMsg, wParam, lParam);
529 else
530 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
531 }
532
533 BOOL
534 CreateMainWindow(IN INT nCmdShow, OUT PMAIN_WND_INFO* Info)
535 {
536 HWND hMainWnd;
537
538 *Info = (PMAIN_WND_INFO) HeapAlloc( hProcessHeap, HEAP_ZERO_MEMORY, sizeof(MAIN_WND_INFO) );
539
540 if(*Info)
541 {
542 (*Info)->nCmdShow = nCmdShow;
543
544 hMainWnd = CreateWindowExW(0,
545 szMainWndClass,
546 szAppName,
547 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
548 CW_USEDEFAULT,
549 CW_USEDEFAULT,
550 CW_USEDEFAULT,
551 CW_USEDEFAULT,
552 NULL,
553 LoadMenuW(hInstance, MAKEINTRESOURCEW(IDM_MAINMENU)),
554 hInstance,
555 *Info);
556
557 if(hMainWnd)
558 return TRUE;
559 else
560 HeapFree(hProcessHeap, 0, *Info);
561 }
562
563 return FALSE;
564 }
565
566 BOOL
567 InitMainWndClass(VOID)
568 {
569 WNDCLASSW wc = {0,};
570
571 wc.lpfnWndProc = MainWndProc;
572 wc.hInstance = hInstance;
573 wc.hCursor = LoadCursor( NULL, IDC_ARROW );
574 wc.hIcon = LoadIconW( hInstance, MAKEINTRESOURCEW(IDI_MAIN) );
575 wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 );
576 wc.lpszClassName = szMainWndClass;
577
578 return RegisterClassW(&wc) != 0;
579 }
580
581 VOID
582 UnInitMainWndClass(VOID)
583 {
584 UnregisterClassW(szMainWndClass, hInstance);
585 }