1 /* 2 * PROJECT: ReactOS Character Map 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: base/applications/charmap/MainWindow.cpp 5 * PURPOSE: Implements the main dialog window 6 * COPYRIGHT: Copyright 2015 Ged Murphy <gedmurphy@reactos.org> 7 */ 8 9 10 #include "precomp.h" 11 #include "MainWindow.h" 12 13 14 /* DATA *****************************************************/ 15 16 #define ID_ABOUT 0x1 17 18 HINSTANCE g_hInstance = NULL; 19 20 21 /* PUBLIC METHODS **********************************************/ 22 23 CCharMapWindow::CCharMapWindow(void) : 24 m_hMainWnd(NULL), 25 m_hStatusBar(NULL), 26 m_CmdShow(0), 27 m_hRichEd(NULL), 28 m_GridView(nullptr) 29 { 30 m_GridView = new CGridView(); 31 } 32 33 CCharMapWindow::~CCharMapWindow(void) 34 { 35 } 36 37 bool 38 CCharMapWindow::Create(_In_ HINSTANCE hInst, 39 _In_ int nCmdShow) 40 { 41 INITCOMMONCONTROLSEX icex; 42 CAtlStringW szAppName; 43 int Ret = 1; 44 45 // Store the instance 46 g_hInstance = hInst; 47 m_CmdShow = nCmdShow; 48 49 // Initialize common controls 50 icex.dwSize = sizeof(INITCOMMONCONTROLSEX); 51 icex.dwICC = ICC_BAR_CLASSES | ICC_COOL_CLASSES; 52 InitCommonControlsEx(&icex); 53 54 // Load the application name 55 if (szAppName.LoadStringW(g_hInstance, IDS_TITLE)) 56 { 57 // Initialize the main window 58 if (Initialize(szAppName, nCmdShow)) 59 { 60 // Run the application 61 Ret = Run(); 62 63 // Uninitialize the main window 64 Uninitialize(); 65 } 66 } 67 68 return (Ret == 0); 69 } 70 71 72 73 /* PRIVATE METHODS **********************************************/ 74 75 bool 76 CCharMapWindow::Initialize(_In_z_ LPCTSTR lpCaption, 77 _In_ int nCmdShow) 78 { 79 // The dialog has a rich edit text box 80 m_hRichEd = LoadLibraryW(L"riched20.DLL"); 81 if (m_hRichEd == NULL) return false; 82 83 return !!(CreateDialogParamW(g_hInstance, 84 MAKEINTRESOURCE(IDD_CHARMAP), 85 NULL, 86 DialogProc, 87 (LPARAM)this)); 88 } 89 90 void 91 CCharMapWindow::Uninitialize(void) 92 { 93 if (m_hRichEd) 94 FreeLibrary(m_hRichEd); 95 } 96 97 int 98 CCharMapWindow::Run(void) 99 { 100 MSG Msg; 101 102 // Pump the message queue 103 while (GetMessageW(&Msg, NULL, 0, 0) != 0) 104 { 105 TranslateMessage(&Msg); 106 DispatchMessageW(&Msg); 107 } 108 109 return 0; 110 } 111 112 void 113 CCharMapWindow::UpdateStatusBar(_In_ bool InMenuLoop) 114 { 115 SendMessageW(m_hStatusBar, 116 SB_SIMPLE, 117 (WPARAM)InMenuLoop, 118 0); 119 } 120 121 bool 122 CCharMapWindow::CreateStatusBar(void) 123 { 124 int StatWidths[] = { 110, -1 }; // widths of status bar 125 bool bRet = FALSE; 126 127 // Create the status bar 128 m_hStatusBar = CreateWindowExW(0, 129 STATUSCLASSNAME, 130 NULL, 131 WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 132 0, 0, 0, 0, 133 m_hMainWnd, 134 (HMENU)IDD_STATUSBAR, 135 g_hInstance, 136 NULL); 137 if (m_hStatusBar) 138 { 139 // Create the sections 140 bRet = (SendMessageW(m_hStatusBar, 141 SB_SETPARTS, 142 sizeof(StatWidths) / sizeof(int), 143 (LPARAM)StatWidths) != 0); 144 145 // Set the status bar for multiple parts output 146 SendMessage(m_hStatusBar, SB_SIMPLE, (WPARAM)FALSE, (LPARAM)0); 147 } 148 149 return bRet; 150 } 151 152 bool 153 CCharMapWindow::StatusBarLoadString(_In_ HWND hStatusBar, 154 _In_ INT PartId, 155 _In_ HINSTANCE hInstance, 156 _In_ UINT uID) 157 { 158 CAtlStringW szMessage; 159 bool bRet = false; 160 161 // Load the string from the resource 162 if (szMessage.LoadStringW(hInstance, uID)) 163 { 164 // Display it on the status bar 165 bRet = (SendMessageW(hStatusBar, 166 SB_SETTEXT, 167 (WPARAM)PartId, 168 (LPARAM)szMessage.GetBuffer()) != 0); 169 } 170 171 return bRet; 172 } 173 174 BOOL 175 CCharMapWindow::OnCreate(_In_ HWND hDlg) 176 { 177 m_hMainWnd = hDlg; 178 179 if (!CreateStatusBar()) 180 return FALSE; 181 182 if (!m_GridView->Create(hDlg)) 183 return FALSE; 184 185 // Load an 'about' option into the system menu 186 HMENU hSysMenu; 187 hSysMenu = GetSystemMenu(m_hMainWnd, FALSE); 188 if (hSysMenu != NULL) 189 { 190 CAtlStringW AboutText; 191 if (AboutText.LoadStringW(IDS_ABOUT)) 192 { 193 AppendMenuW(hSysMenu, MF_SEPARATOR, 0, NULL); 194 AppendMenuW(hSysMenu, MF_STRING, ID_ABOUT, AboutText); 195 } 196 } 197 198 // Add all the fonts to the list 199 if (!CreateFontComboBox()) 200 return FALSE; 201 202 ChangeMapFont(); 203 204 // Configure Richedit control for sending notification changes. 205 DWORD evMask; 206 evMask = SendDlgItemMessage(hDlg, IDC_TEXTBOX, EM_GETEVENTMASK, 0, 0); 207 evMask |= ENM_CHANGE; 208 SendDlgItemMessage(hDlg, IDC_TEXTBOX, EM_SETEVENTMASK, 0, (LPARAM)evMask); 209 210 // Display the window according to the user request 211 ShowWindow(m_hMainWnd, m_CmdShow); 212 213 return TRUE; 214 } 215 216 BOOL 217 CCharMapWindow::OnSize( 218 _In_ WPARAM wParam 219 ) 220 { 221 RECT rcClient, rcStatus; 222 INT lvHeight, iStatusHeight; 223 224 // Resize the status bar 225 SendMessage(m_hStatusBar, WM_SIZE, 0, 0); 226 227 // Get the statusbar rect and save the height 228 GetWindowRect(m_hStatusBar, &rcStatus); 229 iStatusHeight = rcStatus.bottom - rcStatus.top; 230 231 // Get the full client rect 232 GetClientRect(m_hMainWnd, &rcClient); 233 234 // Calculate the remaining height for the gridview 235 lvHeight = rcClient.bottom - iStatusHeight; 236 237 // Resize the grid view 238 SendMessageW(m_GridView->GetHwnd(), WM_SIZE, wParam, 0); 239 240 return TRUE; 241 } 242 243 BOOL 244 CCharMapWindow::OnNotify(_In_ LPARAM lParam) 245 { 246 LPNMHDR NmHdr = (LPNMHDR)lParam; 247 LRESULT Ret = 0; 248 249 switch (NmHdr->code) 250 { 251 case NM_RCLICK: 252 { 253 break; 254 } 255 256 case NM_DBLCLK: 257 case NM_RETURN: 258 { 259 break; 260 } 261 } 262 263 return Ret; 264 } 265 266 BOOL 267 CCharMapWindow::OnContext(_In_ LPARAM lParam) 268 { 269 return 0;// m_GridView->OnContextMenu(lParam); 270 } 271 272 BOOL 273 CCharMapWindow::OnCommand(_In_ WPARAM wParam, 274 _In_ LPARAM /*lParam*/) 275 { 276 LRESULT RetCode = 0; 277 WORD Msg; 278 279 // Get the message 280 Msg = LOWORD(wParam); 281 282 switch (Msg) 283 { 284 case IDC_CHECK_ADVANCED: 285 break; 286 287 case IDC_FONTCOMBO: 288 if (HIWORD(wParam) == CBN_SELCHANGE) 289 { 290 ChangeMapFont(); 291 } 292 break; 293 294 default: 295 // We didn't handle it 296 RetCode = -1; 297 break; 298 } 299 300 return RetCode; 301 } 302 303 BOOL 304 CCharMapWindow::OnDestroy(void) 305 { 306 // Clear the user data pointer 307 SetWindowLongPtr(m_hMainWnd, GWLP_USERDATA, 0); 308 309 // Break the message loop 310 PostQuitMessage(0); 311 312 return TRUE; 313 } 314 315 INT_PTR CALLBACK 316 CCharMapWindow::DialogProc( 317 _In_ HWND hwndDlg, 318 _In_ UINT Msg, 319 _In_ WPARAM wParam, 320 _In_ LPARAM lParam 321 ) 322 { 323 CCharMapWindow *This; 324 LRESULT RetCode = 0; 325 326 // Get the object pointer from window context 327 This = (CCharMapWindow *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 328 if (This == NULL) 329 { 330 // Check that this isn't a create message 331 if (Msg != WM_INITDIALOG) 332 { 333 // Don't handle null info pointer 334 return FALSE; 335 } 336 } 337 338 switch (Msg) 339 { 340 case WM_INITDIALOG: 341 { 342 // Get the object pointer from the create param 343 This = (CCharMapWindow *)lParam; 344 345 // Store the pointer in the window's global user data 346 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)This); 347 348 // Call the create handler 349 return This->OnCreate(hwndDlg); 350 } 351 352 case WM_SIZE: 353 { 354 return This->OnSize(wParam); 355 } 356 357 case WM_NOTIFY: 358 { 359 return This->OnNotify(lParam); 360 } 361 362 case WM_CONTEXTMENU: 363 { 364 return This->OnContext(lParam); 365 } 366 367 case WM_COMMAND: 368 { 369 return This->OnCommand(wParam, lParam); 370 } 371 372 case WM_SYSCOMMAND: 373 switch (wParam) 374 { 375 case ID_ABOUT: 376 // Apportion blame 377 MessageBoxW(This->m_hMainWnd, 378 L"ReactOS Character Map\r\nCopyright Ged Murphy 2015", 379 L"About", 380 MB_OK | MB_APPLMODAL); 381 break; 382 } 383 break; 384 385 case WM_ENTERMENULOOP: 386 { 387 This->UpdateStatusBar(true); 388 return TRUE; 389 } 390 391 case WM_EXITMENULOOP: 392 { 393 This->UpdateStatusBar(false); 394 return TRUE; 395 } 396 397 case WM_CLOSE: 398 { 399 // Destroy the main window 400 return DestroyWindow(hwndDlg); 401 } 402 403 404 case WM_DESTROY: 405 { 406 // Call the destroy handler 407 return This->OnDestroy(); 408 } 409 } 410 411 return FALSE; 412 } 413 414 struct EnumFontParams 415 { 416 CCharMapWindow *This; 417 HWND hCombo; 418 }; 419 420 int 421 CALLBACK 422 CCharMapWindow::EnumDisplayFont(ENUMLOGFONTEXW *lpelfe, 423 NEWTEXTMETRICEXW *lpntme, 424 DWORD FontType, 425 LPARAM lParam) 426 { 427 EnumFontParams *Params = (EnumFontParams *)lParam; 428 LPWSTR pszName = lpelfe->elfLogFont.lfFaceName; 429 430 /* Skip rotated font */ 431 if (pszName[0] == L'@') return 1; 432 433 /* make sure font doesn't already exist in our list */ 434 if (SendMessageW(Params->hCombo, 435 CB_FINDSTRINGEXACT, 436 0, 437 (LPARAM)pszName) == CB_ERR) 438 { 439 INT idx; 440 idx = (INT)SendMessageW(Params->hCombo, 441 CB_ADDSTRING, 442 0, 443 (LPARAM)pszName); 444 445 /* record the font's attributes (Fixedwidth and Truetype) */ 446 BOOL fFixed = (lpelfe->elfLogFont.lfPitchAndFamily & FIXED_PITCH) ? TRUE : FALSE; 447 BOOL fTrueType = (lpelfe->elfLogFont.lfOutPrecision == OUT_STROKE_PRECIS) ? TRUE : FALSE; 448 449 /* store this information in the list-item's userdata area */ 450 SendMessageW(Params->hCombo, 451 CB_SETITEMDATA, 452 idx, 453 MAKEWPARAM(fFixed, fTrueType)); 454 } 455 456 return 1; 457 } 458 459 460 bool 461 CCharMapWindow::CreateFontComboBox() 462 { 463 HWND hCombo; 464 hCombo = GetDlgItem(m_hMainWnd, IDC_FONTCOMBO); 465 466 NONCLIENTMETRICSW NonClientMetrics; 467 NonClientMetrics.cbSize = sizeof(NONCLIENTMETRICSW); 468 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 469 sizeof(NONCLIENTMETRICSW), 470 &NonClientMetrics, 471 0); 472 473 // Get a handle to the font 474 HFONT GuiFont; 475 GuiFont = CreateFontIndirectW(&NonClientMetrics.lfMessageFont); 476 477 // Set the font used in the combo box 478 SendMessageW(hCombo, 479 WM_SETFONT, 480 (WPARAM)GuiFont, 481 0); 482 483 // Set the fonts which we want to enumerate 484 LOGFONTW FontsToEnum; 485 ZeroMemory(&FontsToEnum, sizeof(LOGFONTW)); 486 FontsToEnum.lfCharSet = DEFAULT_CHARSET; 487 488 // Set the params we want to pass to the callback 489 EnumFontParams Params; 490 Params.This = this; 491 Params.hCombo = hCombo; 492 493 // Get a DC for combo box 494 HDC hdc; 495 hdc = GetDC(hCombo); 496 497 // Enumerate all the fonts 498 int ret; 499 ret = EnumFontFamiliesExW(hdc, 500 &FontsToEnum, 501 (FONTENUMPROCW)EnumDisplayFont, 502 (LPARAM)&Params, 503 0); 504 505 ReleaseDC(hCombo, hdc); 506 DeleteObject(GuiFont); 507 508 // Select the first item in the list 509 SendMessageW(hCombo, 510 CB_SETCURSEL, 511 0, 512 0); 513 514 return (ret == 1); 515 } 516 517 bool 518 CCharMapWindow::ChangeMapFont( 519 ) 520 { 521 HWND hCombo; 522 hCombo = GetDlgItem(m_hMainWnd, IDC_FONTCOMBO); 523 524 INT Length; 525 Length = GetWindowTextLengthW(hCombo); 526 if (!Length) return false; 527 528 CAtlStringW FontName; 529 FontName.Preallocate(Length); 530 531 SendMessageW(hCombo, 532 WM_GETTEXT, 533 FontName.GetAllocLength(), 534 (LPARAM)FontName.GetBuffer()); 535 536 return m_GridView->SetFont(FontName); 537 } 538