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) GetWindowLongPtrW(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 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)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 SetWindowLongPtrW(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 } 586