2c50fb526ebb08926ab4168fcc8e1c5ab516a3d6
[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 }
92
93 static VOID
94 AddToolbarSeparator(IN PMAIN_WND_INFO Info)
95 {
96 TBBUTTON tbb = {0,};
97
98 tbb.fsStyle = BTNS_SEP;
99
100 SendMessageW( Info->hToolbar, TB_ADDBUTTONSW, 1, (LPARAM)&tbb );
101 }
102
103 static VOID
104 InitMainWnd(IN PMAIN_WND_INFO Info)
105 {
106 CLIENTCREATESTRUCT ccs;
107 INT iCustomBitmaps;
108 INT iStandardBitmaps;
109 TBADDBITMAP tbab;
110
111 // Add the toolbar
112 Info->hToolbar = CreateWindowExW(0,
113 TOOLBARCLASSNAMEW,
114 NULL,
115 WS_VISIBLE | WS_CHILD | TBSTYLE_TOOLTIPS,
116 0,
117 0,
118 0,
119 0,
120 Info->hMainWnd,
121 NULL,
122 hInstance,
123 NULL);
124
125 // Identify the used Common Controls version
126 SendMessageW(Info->hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
127
128 // Enable Tooltips
129 SendMessageW(Info->hToolbar, TB_SETMAXTEXTROWS, 0, 0);
130
131 // Add the toolbar bitmaps
132 tbab.hInst = HINST_COMMCTRL;
133 tbab.nID = IDB_STD_SMALL_COLOR;
134 iStandardBitmaps = SendMessageW(Info->hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
135
136 tbab.hInst = hInstance;
137 tbab.nID = IDB_MAIN_TOOLBAR;
138 iCustomBitmaps = SendMessageW(Info->hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
139
140 // Add the toolbar buttons
141 AddToolbarButton(Info, iStandardBitmaps + STD_FILENEW, ID_FILE_NEW, IDS_TOOLTIP_NEW);
142 AddToolbarButton(Info, iStandardBitmaps + STD_FILEOPEN, ID_FILE_OPEN, IDS_TOOLTIP_OPEN);
143 AddToolbarButton(Info, iStandardBitmaps + STD_FILESAVE, ID_FILE_SAVE, IDS_TOOLTIP_SAVE);
144 AddToolbarSeparator(Info);
145 AddToolbarButton(Info, iCustomBitmaps + TOOLBAR_EDIT_GLYPH, ID_EDIT_GLYPH, IDS_TOOLTIP_EDIT_GLYPH);
146
147 SetToolbarFileButtonState(Info, FALSE);
148
149 // Add the MDI client area
150 ccs.hWindowMenu = GetSubMenu(Info->hMenu, 1);
151 ccs.idFirstChild = ID_MDI_FIRSTCHILD;
152
153 Info->hMdiClient = CreateWindowExW(WS_EX_CLIENTEDGE,
154 L"MDICLIENT",
155 NULL,
156 WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL,
157 0,
158 0,
159 0,
160 0,
161 Info->hMainWnd,
162 NULL,
163 hInstance,
164 &ccs);
165
166 // Initialize the file handling
167 FileInitialize(Info->hMainWnd);
168 }
169
170 static VOID
171 InitMenuPopup(IN PMAIN_WND_INFO Info)
172 {
173 UINT uState;
174
175 uState = MF_BYCOMMAND | (Info->CurrentFontWnd == NULL);
176
177 EnableMenuItem(Info->hMenu, ID_FILE_CLOSE, uState);
178 EnableMenuItem(Info->hMenu, ID_FILE_SAVE, uState);
179 EnableMenuItem(Info->hMenu, ID_FILE_SAVE_AS, uState);
180 }
181
182 static VOID
183 DoFileNew(IN PMAIN_WND_INFO Info)
184 {
185 PFONT_OPEN_INFO OpenInfo;
186
187 OpenInfo = (PFONT_OPEN_INFO) HeapAlloc( hProcessHeap, HEAP_ZERO_MEMORY, sizeof(FONT_OPEN_INFO) );
188 OpenInfo->bCreateNew = TRUE;
189
190 CreateFontWindow(Info, OpenInfo);
191 }
192
193 static VOID
194 DoFileOpen(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->pszFileName = HeapAlloc(hProcessHeap, 0, MAX_PATH);
200 OpenInfo->pszFileName[0] = 0;
201
202 if( DoOpenFile(OpenInfo->pszFileName) )
203 {
204 OpenInfo->bCreateNew = FALSE;
205 CreateFontWindow(Info, OpenInfo);
206 }
207 }
208
209 VOID
210 DoFileSave(IN PMAIN_WND_INFO Info, IN BOOL bSaveAs)
211 {
212 DWORD dwBytesWritten;
213 HANDLE hFile;
214
215 // Show the "Save" dialog
216 // - if "Save As" was clicked
217 // - if the file was not yet saved
218 // - if another format than the binary format was opened
219 if(bSaveAs || !Info->CurrentFontWnd->OpenInfo->bBinaryFileOpened)
220 {
221 if(!Info->CurrentFontWnd->OpenInfo->pszFileName)
222 {
223 Info->CurrentFontWnd->OpenInfo->pszFileName = (PWSTR) HeapAlloc(hProcessHeap, 0, MAX_PATH);
224 Info->CurrentFontWnd->OpenInfo->pszFileName[0] = 0;
225 }
226 else if(!Info->CurrentFontWnd->OpenInfo->bBinaryFileOpened)
227 {
228 // For a file in another format, the user has to enter a new file name as well
229 Info->CurrentFontWnd->OpenInfo->pszFileName[0] = 0;
230 }
231
232 if( !DoSaveFile(Info->CurrentFontWnd->OpenInfo->pszFileName) )
233 return;
234 }
235
236 // Save the binary font
237 hFile = CreateFileW(Info->CurrentFontWnd->OpenInfo->pszFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
238
239 if(hFile == INVALID_HANDLE_VALUE)
240 {
241 LocalizedError( IDS_OPENERROR, GetLastError() );
242 return;
243 }
244
245 if( !WriteFile(hFile, Info->CurrentFontWnd->Font, sizeof(BITMAP_FONT), &dwBytesWritten, NULL) )
246 LocalizedError( IDS_WRITEERROR, GetLastError() );
247
248 CloseHandle(hFile);
249 }
250
251 static BOOL
252 MenuCommand(IN INT nMenuItemID, IN PMAIN_WND_INFO Info)
253 {
254 switch(nMenuItemID)
255 {
256 // File Menu
257 case ID_FILE_NEW:
258 DoFileNew(Info);
259 return TRUE;
260
261 case ID_FILE_OPEN:
262 DoFileOpen(Info);
263 return TRUE;
264
265 case ID_FILE_CLOSE:
266 SendMessageW(Info->CurrentFontWnd->hSelf, WM_CLOSE, 0, 0);
267 return TRUE;
268
269 case ID_FILE_SAVE:
270 DoFileSave(Info, FALSE);
271 return TRUE;
272
273 case ID_FILE_SAVE_AS:
274 DoFileSave(Info, TRUE);
275 return TRUE;
276
277 case ID_FILE_EXIT:
278 PostMessage(Info->hMainWnd, WM_CLOSE, 0, 0);
279 return TRUE;
280
281 // "Edit Glyph" toolbar button
282 case ID_EDIT_GLYPH:
283 EditCurrentGlyph(Info->CurrentFontWnd);
284 return TRUE;
285
286 // Window Menu
287 case ID_WINDOW_TILE_HORZ:
288 SendMessageW(Info->hMdiClient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
289 return TRUE;
290
291 case ID_WINDOW_TILE_VERT:
292 SendMessageW(Info->hMdiClient, WM_MDITILE, MDITILE_VERTICAL, 0);
293 return TRUE;
294
295 case ID_WINDOW_CASCADE:
296 SendMessageW(Info->hMdiClient, WM_MDICASCADE, 0, 0);
297 return TRUE;
298
299 case ID_WINDOW_ARRANGE:
300 SendMessageW(Info->hMdiClient, WM_MDIICONARRANGE, 0, 0);
301 return TRUE;
302
303 case ID_WINDOW_NEXT:
304 SendMessageW(Info->hMdiClient, WM_MDINEXT, 0, 0);
305 return TRUE;
306
307 // Help Menu
308 case ID_HELP_ABOUT:
309 DialogBoxW( hInstance, MAKEINTRESOURCEW(IDD_ABOUT), Info->hMainWnd, AboutDlgProc );
310 return TRUE;
311 }
312
313 return FALSE;
314 }
315
316 static VOID
317 MainWndSize(PMAIN_WND_INFO Info, INT cx, INT cy)
318 {
319 HDWP dwp;
320 INT iMdiTop;
321 RECT ToolbarRect;
322
323 iMdiTop = 0;
324
325 dwp = BeginDeferWindowPos(2);
326 if(!dwp)
327 return;
328
329 if(Info->hToolbar)
330 {
331 GetWindowRect(Info->hToolbar, &ToolbarRect);
332 iMdiTop += ToolbarRect.bottom - ToolbarRect.top;
333
334 dwp = DeferWindowPos(dwp, Info->hToolbar, NULL, 0, 0, cx, ToolbarRect.bottom - ToolbarRect.top, SWP_NOZORDER);
335 if(!dwp)
336 return;
337 }
338
339 if(Info->hMdiClient)
340 {
341 dwp = DeferWindowPos(dwp, Info->hMdiClient, NULL, 0, iMdiTop, cx, cy - iMdiTop, SWP_NOZORDER);
342 if(!dwp)
343 return;
344 }
345
346 EndDeferWindowPos(dwp);
347 }
348
349 static LRESULT CALLBACK
350 MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
351 {
352 PMAIN_WND_INFO Info;
353
354 Info = (PMAIN_WND_INFO) GetWindowLongW(hwnd, GWLP_USERDATA);
355
356 if(Info || uMsg == WM_CREATE)
357 {
358 switch(uMsg)
359 {
360 case WM_COMMAND:
361 if( MenuCommand( LOWORD(wParam), Info ) )
362 return 0;
363
364 break;
365
366 case WM_CLOSE:
367 if(Info->FirstFontWnd)
368 {
369 // Send WM_CLOSE to all subwindows, so they can prompt for saving unsaved files
370 PFONT_WND_INFO pNextWnd;
371 PFONT_WND_INFO pWnd;
372
373 pWnd = Info->FirstFontWnd;
374
375 do
376 {
377 // The pWnd structure might already be destroyed after the WM_CLOSE, so we have to preserve the address of the next window here
378 pNextWnd = pWnd->NextFontWnd;
379
380 // Send WM_USER_APPCLOSE, so we can check for a custom return value
381 // 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
382 if( !SendMessage(pWnd->hSelf, WM_USER_APPCLOSE, 0, 0) )
383 return 0;
384 }
385 while( (pWnd = pNextWnd) );
386 }
387 break;
388
389 case WM_CREATE:
390 Info = (PMAIN_WND_INFO)( ( (LPCREATESTRUCT)lParam )->lpCreateParams );
391 Info->hMainWnd = hwnd;
392 Info->hMenu = GetMenu(hwnd);
393 SetWindowLongW(hwnd, GWLP_USERDATA, (LONG)Info);
394
395 InitMainWnd(Info);
396 InitResources(Info);
397
398 ShowWindow(hwnd, Info->nCmdShow);
399 return 0;
400
401 case WM_DESTROY:
402 UnInitResources(Info);
403
404 HeapFree(hProcessHeap, 0, Info);
405 SetWindowLongW(hwnd, GWLP_USERDATA, 0);
406 PostQuitMessage(0);
407 return 0;
408
409 case WM_INITMENUPOPUP:
410 InitMenuPopup(Info);
411 break;
412
413 case WM_SIZE:
414 MainWndSize( Info, LOWORD(lParam), HIWORD(lParam) );
415 return 0;
416 }
417 }
418
419 if(Info && Info->hMdiClient)
420 return DefFrameProcW(hwnd, Info->hMdiClient, uMsg, wParam, lParam);
421 else
422 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
423 }
424
425 BOOL
426 CreateMainWindow(IN INT nCmdShow, OUT PMAIN_WND_INFO* Info)
427 {
428 HWND hMainWnd;
429
430 *Info = (PMAIN_WND_INFO) HeapAlloc( hProcessHeap, HEAP_ZERO_MEMORY, sizeof(MAIN_WND_INFO) );
431
432 if(*Info)
433 {
434 (*Info)->nCmdShow = nCmdShow;
435
436 hMainWnd = CreateWindowExW(0,
437 szMainWndClass,
438 szAppName,
439 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
440 CW_USEDEFAULT,
441 CW_USEDEFAULT,
442 CW_USEDEFAULT,
443 CW_USEDEFAULT,
444 NULL,
445 LoadMenuW(hInstance, MAKEINTRESOURCEW(IDM_MAINMENU)),
446 hInstance,
447 *Info);
448
449 if(hMainWnd)
450 return TRUE;
451 else
452 HeapFree(hProcessHeap, 0, *Info);
453 }
454
455 return FALSE;
456 }
457
458 BOOL
459 InitMainWndClass(VOID)
460 {
461 WNDCLASSW wc = {0,};
462
463 wc.lpfnWndProc = MainWndProc;
464 wc.hInstance = hInstance;
465 wc.hCursor = LoadCursor( NULL, IDC_ARROW );
466 wc.hIcon = LoadIconW( hInstance, MAKEINTRESOURCEW(IDI_MAIN) );
467 wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 );
468 wc.lpszClassName = szMainWndClass;
469
470 return RegisterClassW(&wc) != 0;
471 }
472
473 VOID
474 UnInitMainWndClass(VOID)
475 {
476 UnregisterClassW(szMainWndClass, hInstance);
477 }