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