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