1 /* 2 * PROJECT: ReactOS Character Map 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: base/applications/charmap/charmap.c 5 * PURPOSE: main dialog implementation 6 * COPYRIGHT: Copyright 2007 Ged Murphy <gedmurphy@reactos.org> 7 */ 8 9 #include "precomp.h" 10 11 #include <commctrl.h> 12 #include <richedit.h> 13 #include <winnls.h> 14 15 //#define REMOVE_ADVANCED 16 17 #define ID_ABOUT 0x1 18 19 HINSTANCE hInstance; 20 HWND hAdvancedDlg; 21 HWND hCharmapDlg; 22 HWND hStatusWnd; 23 HICON hSmIcon; 24 HICON hBgIcon; 25 SETTINGS Settings; 26 27 static 28 VOID 29 FillCharacterSetComboList(HWND hwndCombo) 30 { 31 WCHAR szCharSetText[256]; 32 LPWSTR trimmedName; 33 CPINFOEXW cpInfo; 34 INT i; 35 36 if (LoadStringW(hInstance, IDS_UNICODE, szCharSetText, SIZEOF(szCharSetText))) 37 { 38 SendMessageW(hwndCombo, 39 CB_ADDSTRING, 40 0, 41 (LPARAM)szCharSetText); 42 } 43 44 for (i = 0; i < SIZEOF(codePages); i++) 45 { 46 if (GetCPInfoExW(codePages[i], 0, &cpInfo)) 47 { 48 trimmedName = wcschr(cpInfo.CodePageName, L'('); 49 if (!trimmedName) 50 trimmedName = cpInfo.CodePageName; 51 52 SendMessageW(hwndCombo, 53 CB_ADDSTRING, 54 0, 55 (LPARAM)trimmedName); 56 } 57 } 58 59 SendMessageW(hwndCombo, CB_SETCURSEL, 0, 0); 60 } 61 62 static 63 VOID 64 FillGroupByComboList(HWND hwndCombo) 65 { 66 WCHAR szAllText[256]; 67 68 if (LoadStringW(hInstance, IDS_ALL, szAllText, SIZEOF(szAllText))) 69 { 70 SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM)szAllText); 71 } 72 73 SendMessageW(hwndCombo, CB_SETCURSEL, 0, 0); 74 } 75 76 /* Font-enumeration callback */ 77 static 78 int 79 CALLBACK 80 EnumFontNames(ENUMLOGFONTEXW *lpelfe, 81 NEWTEXTMETRICEXW *lpntme, 82 DWORD FontType, 83 LPARAM lParam) 84 { 85 HWND hwndCombo = (HWND)lParam; 86 LPWSTR pszName = lpelfe->elfLogFont.lfFaceName; 87 88 /* Skip rotated font */ 89 if(pszName[0] == L'@') return 1; 90 91 /* make sure font doesn't already exist in our list */ 92 if(SendMessageW(hwndCombo, 93 CB_FINDSTRINGEXACT, 94 0, 95 (LPARAM)pszName) == CB_ERR) 96 { 97 INT idx; 98 BOOL fFixed; 99 BOOL fTrueType; 100 101 /* add the font */ 102 idx = (INT)SendMessageW(hwndCombo, 103 CB_ADDSTRING, 104 0, 105 (LPARAM)pszName); 106 107 /* record the font's attributes (Fixedwidth and Truetype) */ 108 fFixed = (lpelfe->elfLogFont.lfPitchAndFamily & FIXED_PITCH) ? TRUE : FALSE; 109 fTrueType = (lpelfe->elfLogFont.lfOutPrecision == OUT_STROKE_PRECIS) ? TRUE : FALSE; 110 111 /* store this information in the list-item's userdata area */ 112 SendMessageW(hwndCombo, 113 CB_SETITEMDATA, 114 idx, 115 MAKEWPARAM(fFixed, fTrueType)); 116 } 117 118 return 1; 119 } 120 121 122 /* Initialize the font-list by enumeration all system fonts */ 123 static 124 VOID 125 FillFontStyleComboList(HWND hwndCombo) 126 { 127 HDC hdc; 128 LOGFONTW lf; 129 130 /* FIXME: for fun, draw each font in its own style */ 131 HFONT hFont = GetStockObject(DEFAULT_GUI_FONT); 132 SendMessageW(hwndCombo, 133 WM_SETFONT, 134 (WPARAM)hFont, 135 0); 136 137 ZeroMemory(&lf, sizeof(lf)); 138 lf.lfCharSet = DEFAULT_CHARSET; 139 140 hdc = GetDC(hwndCombo); 141 142 /* store the list of fonts in the combo */ 143 EnumFontFamiliesExW(hdc, 144 &lf, 145 (FONTENUMPROCW)EnumFontNames, 146 (LPARAM)hwndCombo, 147 0); 148 149 ReleaseDC(hwndCombo, 150 hdc); 151 152 SendMessageW(hwndCombo, 153 CB_SETCURSEL, 154 0, 155 0); 156 } 157 158 159 extern 160 VOID 161 ChangeMapFont(HWND hDlg) 162 { 163 HWND hCombo; 164 HWND hMap; 165 LPWSTR lpFontName; 166 INT Len; 167 168 hCombo = GetDlgItem(hDlg, IDC_FONTCOMBO); 169 170 Len = GetWindowTextLengthW(hCombo); 171 172 if (Len != 0) 173 { 174 lpFontName = HeapAlloc(GetProcessHeap(), 175 0, 176 (Len + 1) * sizeof(WCHAR)); 177 178 if (lpFontName) 179 { 180 SendMessageW(hCombo, 181 WM_GETTEXT, 182 Len + 1, 183 (LPARAM)lpFontName); 184 185 hMap = GetDlgItem(hDlg, IDC_FONTMAP); 186 187 SendMessageW(hMap, 188 FM_SETFONT, 189 0, 190 (LPARAM)lpFontName); 191 } 192 193 HeapFree(GetProcessHeap(), 194 0, 195 lpFontName); 196 } 197 } 198 199 // Copy collected characters into the clipboard 200 static 201 void 202 CopyCharacters(HWND hDlg) 203 { 204 HWND hText = GetDlgItem(hDlg, IDC_TEXTBOX); 205 DWORD dwStart, dwEnd; 206 207 // Acquire selection limits 208 SendMessage(hText, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd); 209 210 // Test if the whose text is unselected 211 if(dwStart == dwEnd) { 212 213 // Select the whole text 214 SendMessageW(hText, EM_SETSEL, 0, -1); 215 216 // Copy text 217 SendMessageW(hText, WM_COPY, 0, 0); 218 219 // Restore previous values 220 SendMessageW(hText, EM_SETSEL, (WPARAM)dwStart, (LPARAM)dwEnd); 221 222 } else { 223 224 // Copy text 225 SendMessageW(hText, WM_COPY, 0, 0); 226 } 227 } 228 229 // Recover charset for the given font 230 static 231 BYTE 232 GetFontMetrics(HWND hWnd, HFONT hFont) 233 { 234 TEXTMETRIC tmFont; 235 HGDIOBJ hOldObj; 236 HDC hDC; 237 238 hDC = GetDC(hWnd); 239 hOldObj = SelectObject(hDC, hFont); 240 GetTextMetrics(hDC, &tmFont); 241 SelectObject(hDC, hOldObj); 242 ReleaseDC(hWnd, hDC); 243 244 return tmFont.tmCharSet; 245 } 246 247 // Select a new character 248 static 249 VOID 250 AddCharToSelection(HWND hDlg, WCHAR ch) 251 { 252 HWND hMap = GetDlgItem(hDlg, IDC_FONTMAP); 253 HWND hText = GetDlgItem(hDlg, IDC_TEXTBOX); 254 HFONT hFont; 255 LOGFONT lFont; 256 CHARFORMAT cf; 257 258 // Retrieve current character selected 259 if (ch == 0) 260 { 261 ch = (WCHAR) SendMessageW(hMap, FM_GETCHAR, 0, 0); 262 if (!ch) 263 return; 264 } 265 266 // Retrieve current selected font 267 hFont = (HFONT)SendMessage(hMap, FM_GETHFONT, 0, 0); 268 269 // Recover LOGFONT structure from hFont 270 if (!GetObject(hFont, sizeof(LOGFONT), &lFont)) 271 return; 272 273 // Recover font properties of Richedit control 274 ZeroMemory(&cf, sizeof(cf)); 275 cf.cbSize = sizeof(cf); 276 SendMessage(hText, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); 277 278 // Apply properties of the new font 279 cf.bCharSet = GetFontMetrics(hText, hFont); 280 281 // Update font name 282 wcscpy(cf.szFaceName, lFont.lfFaceName); 283 284 // Update font properties 285 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); 286 287 // Send selected character to Richedit 288 SendMessage(hText, WM_CHAR, (WPARAM)ch, 0); 289 } 290 291 #ifndef REMOVE_ADVANCED 292 static 293 void 294 UpdateSettings(HWND hDlg) 295 { 296 if (hDlg == hCharmapDlg) 297 { 298 Settings.IsAdvancedView = 299 SendDlgItemMessage(hDlg, IDC_CHECK_ADVANCED, BM_GETCHECK, 0, 0); 300 301 } 302 303 if (hDlg == hAdvancedDlg) 304 { 305 } 306 } 307 #endif 308 309 VOID 310 UpdateStatusBar(WCHAR wch) 311 { 312 WCHAR buff[MAX_PATH]; 313 WCHAR szDesc[MAX_PATH]; 314 315 GetUName(wch, szDesc); 316 wsprintfW(buff, L"U+%04X: %s", wch, szDesc); 317 SendMessageW(hStatusWnd, SB_SETTEXT, 0, (LPARAM)buff); 318 } 319 320 static 321 void 322 ChangeView(HWND hWnd) 323 { 324 RECT rcCharmap; 325 #ifndef REMOVE_ADVANCED 326 RECT rcAdvanced; 327 #else 328 RECT rcCopy; 329 #endif 330 RECT rcPanelExt; 331 RECT rcPanelInt; 332 RECT rcStatus; 333 UINT DeX, DeY; 334 LONG xPos, yPos; 335 UINT Width, Height; 336 UINT DeskTopWidth, DeskTopHeight; 337 #ifdef REMOVE_ADVANCED 338 HWND hCopy; 339 #endif 340 341 GetClientRect(hCharmapDlg, &rcCharmap); 342 #ifndef REMOVE_ADVANCED 343 GetClientRect(hAdvancedDlg, &rcAdvanced); 344 #else 345 hCopy = GetDlgItem(hCharmapDlg, IDC_COPY); 346 GetClientRect(hCopy, &rcCopy); 347 #endif 348 GetWindowRect(hWnd, &rcPanelExt); 349 GetClientRect(hWnd, &rcPanelInt); 350 GetClientRect(hStatusWnd, &rcStatus); 351 352 DeskTopWidth = GetSystemMetrics(SM_CXFULLSCREEN); 353 DeskTopHeight = GetSystemMetrics(SM_CYFULLSCREEN); 354 355 DeX = (rcPanelExt.right - rcPanelExt.left) - rcPanelInt.right; 356 DeY = (rcPanelExt.bottom - rcPanelExt.top) - rcPanelInt.bottom; 357 358 MoveWindow(hCharmapDlg, 0, 0, rcCharmap.right, rcCharmap.bottom, FALSE); 359 #ifndef REMOVE_ADVANCED 360 MoveWindow(hAdvancedDlg, 0, rcCharmap.bottom, rcAdvanced.right, rcAdvanced.bottom, FALSE); 361 ShowWindow(hAdvancedDlg, (Settings.IsAdvancedView) ? SW_SHOW : SW_HIDE); 362 #endif 363 xPos = rcPanelExt.left; 364 yPos = rcPanelExt.top; 365 366 Width = DeX + rcCharmap.right; 367 Height = DeY + rcCharmap.bottom + rcStatus.bottom; 368 #ifndef REMOVE_ADVANCED 369 if (Settings.IsAdvancedView) 370 Height += rcAdvanced.bottom; 371 #else 372 /* The lack of advanced button leaves an empty gap at the bottom of the window. 373 Shrink the window height a bit here to accomodate for that lost control. */ 374 Height = rcCharmap.bottom + rcCopy.bottom + 10; 375 #endif 376 // FIXME: This fails on multi monitor setups 377 if ((xPos + Width) > DeskTopWidth) 378 xPos = DeskTopWidth - Width; 379 380 if ((yPos + Height) > DeskTopHeight) 381 yPos = DeskTopHeight - Height; 382 383 MoveWindow(hWnd, 384 xPos, yPos, 385 Width, Height, 386 TRUE); 387 } 388 389 static 390 INT_PTR 391 CALLBACK 392 CharMapDlgProc(HWND hDlg, 393 UINT Message, 394 WPARAM wParam, 395 LPARAM lParam) 396 { 397 switch(Message) 398 { 399 case WM_INITDIALOG: 400 { 401 DWORD evMask; 402 #ifdef REMOVE_ADVANCED 403 HWND hAdv; 404 #endif 405 406 FillFontStyleComboList(GetDlgItem(hDlg, 407 IDC_FONTCOMBO)); 408 409 ChangeMapFont(hDlg); 410 411 // Configure Richedit control for sending notification changes. 412 evMask = SendDlgItemMessage(hDlg, IDC_TEXTBOX, EM_GETEVENTMASK, 0, 0); 413 evMask |= ENM_CHANGE; 414 SendDlgItemMessage(hDlg, IDC_TEXTBOX, EM_SETEVENTMASK, 0, (LPARAM)evMask); 415 #ifdef REMOVE_ADVANCED 416 hAdv = GetDlgItem(hDlg, IDC_CHECK_ADVANCED); 417 ShowWindow(hAdv, SW_HIDE); 418 #endif 419 return TRUE; 420 } 421 422 case WM_COMMAND: 423 { 424 switch(LOWORD(wParam)) 425 { 426 case IDC_FONTMAP: 427 switch (HIWORD(wParam)) 428 { 429 case FM_SETCHAR: 430 AddCharToSelection(hDlg, LOWORD(lParam)); 431 break; 432 } 433 break; 434 435 case IDC_FONTCOMBO: 436 if (HIWORD(wParam) == CBN_SELCHANGE) 437 { 438 ChangeMapFont(hDlg); 439 } 440 break; 441 442 case IDC_SELECT: 443 AddCharToSelection(hDlg, 0); 444 break; 445 446 case IDC_TEXTBOX: 447 switch (HIWORD(wParam)) { 448 case EN_CHANGE: 449 if (GetWindowTextLength(GetDlgItem(hDlg, IDC_TEXTBOX)) == 0) 450 EnableWindow(GetDlgItem(hDlg, IDC_COPY), FALSE); 451 else 452 EnableWindow(GetDlgItem(hDlg, IDC_COPY), TRUE); 453 break; 454 } 455 break; 456 457 case IDC_COPY: 458 CopyCharacters(hDlg); 459 break; 460 #ifndef REMOVE_ADVANCED 461 case IDC_CHECK_ADVANCED: 462 UpdateSettings(hDlg); 463 ChangeView(GetParent(hDlg)); 464 break; 465 #endif 466 } 467 } 468 break; 469 470 default: 471 break; 472 } 473 474 return FALSE; 475 } 476 #ifndef REMOVE_ADVANCED 477 static 478 INT_PTR 479 CALLBACK 480 AdvancedDlgProc(HWND hDlg, 481 UINT Message, 482 WPARAM wParam, 483 LPARAM lParam) 484 { 485 switch(Message) 486 { 487 case WM_INITDIALOG: 488 return TRUE; 489 490 case WM_COMMAND: 491 { 492 switch (LOWORD(wParam)) 493 { 494 case IDC_COMBO_CHARSET: 495 if (HIWORD(wParam) == CBN_SELCHANGE) 496 { 497 INT idx = (INT)SendMessageW((HWND)lParam, 498 CB_GETCURSEL, 499 0, 0); 500 SendMessageW(GetDlgItem(hCharmapDlg, IDC_FONTMAP), 501 FM_SETCHARMAP, 502 idx, 0); 503 504 EnableWindow(GetDlgItem(hAdvancedDlg, IDC_EDIT_UNICODE), idx == 0); 505 } 506 break; 507 } 508 } 509 510 default: 511 return FALSE; 512 } 513 514 return FALSE; 515 } 516 #endif 517 518 static int 519 PanelOnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) 520 { 521 HMENU hSysMenu; 522 WCHAR lpAboutText[256]; 523 524 hCharmapDlg = CreateDialog(hInstance, 525 MAKEINTRESOURCE(IDD_CHARMAP), 526 hWnd, 527 CharMapDlgProc); 528 529 // For now, the Help push button is disabled because of lacking of HTML Help support 530 EnableWindow(GetDlgItem(hCharmapDlg, IDC_CMHELP), FALSE); 531 532 #ifndef REMOVE_ADVANCED 533 hAdvancedDlg = CreateDialog(hInstance, 534 MAKEINTRESOURCE(IDD_ADVANCED), 535 hWnd, 536 AdvancedDlgProc); 537 538 FillCharacterSetComboList(GetDlgItem(hAdvancedDlg, IDC_COMBO_CHARSET)); 539 540 FillGroupByComboList(GetDlgItem(hAdvancedDlg, IDC_COMBO_GROUPBY)); 541 EnableWindow(GetDlgItem(hAdvancedDlg, IDC_COMBO_GROUPBY), FALSE); // FIXME: Implement 542 EnableWindow(GetDlgItem(hAdvancedDlg, IDC_BUTTON_SEARCH), FALSE); // FIXME: Implement 543 #endif 544 hStatusWnd = CreateWindow(STATUSCLASSNAME, 545 NULL, 546 WS_CHILD | WS_VISIBLE, 547 0, 0, 0, 0, 548 hWnd, 549 (HMENU)IDD_STATUSBAR, 550 hInstance, 551 NULL); 552 553 // Set the status bar for multiple parts output 554 SendMessage(hStatusWnd, SB_SIMPLE, (WPARAM)FALSE, (LPARAM)0); 555 556 ChangeView(hWnd); 557 558 hSysMenu = GetSystemMenu(hWnd, FALSE); 559 560 if (hSysMenu != NULL) 561 { 562 if (LoadStringW(hInstance, IDS_ABOUT, lpAboutText, SIZEOF(lpAboutText))) 563 { 564 AppendMenuW(hSysMenu, MF_SEPARATOR, 0, NULL); 565 AppendMenuW(hSysMenu, MF_STRING, ID_ABOUT, lpAboutText); 566 } 567 } 568 569 SetFocus(hCharmapDlg); 570 571 return 0; 572 } 573 574 static LRESULT CALLBACK 575 PanelWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 576 { 577 switch (msg) { 578 case WM_CREATE: 579 return PanelOnCreate(hWnd, wParam, lParam); 580 581 case WM_CLOSE: 582 DestroyWindow(hWnd); 583 return 0; 584 585 case WM_SIZE: 586 SendMessage(hStatusWnd, msg, wParam, lParam); 587 break; 588 589 case WM_DESTROY: 590 SaveSettings(); 591 PostQuitMessage(0); 592 return 0; 593 594 case WM_SYSCOMMAND: 595 switch(wParam) { 596 case ID_ABOUT: 597 ShowAboutDlg(hWnd); 598 break; 599 } 600 break; 601 602 } 603 604 return DefWindowProc(hWnd, msg, wParam, lParam); 605 } 606 607 static HWND 608 InitInstance(HINSTANCE hInst) 609 { 610 WCHAR szClass[] = L"CharMap"; 611 WCHAR szTitle[256]; 612 WNDCLASSEXW wc; 613 HWND hWnd; 614 615 LoadStringW(hInst, IDS_TITLE, szTitle, SIZEOF(szTitle)); 616 617 hSmIcon = LoadImage(hInstance, 618 MAKEINTRESOURCE(IDI_ICON), 619 IMAGE_ICON, 620 16, 621 16, 622 0); 623 624 hBgIcon = LoadImage(hInstance, 625 MAKEINTRESOURCE(IDI_ICON), 626 IMAGE_ICON, 627 32, 628 32, 629 0); 630 631 // Create workspace 632 ZeroMemory(&wc, sizeof(wc)); 633 634 wc.cbSize = sizeof(wc); 635 wc.lpfnWndProc = PanelWndProc; 636 wc.hInstance = hInst; 637 wc.hIcon = hBgIcon; 638 wc.hCursor = LoadCursor(NULL, IDC_ARROW); 639 wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); 640 wc.lpszMenuName = NULL; 641 wc.lpszClassName = szClass; 642 wc.hIconSm = hSmIcon; 643 644 RegisterClassExW(&wc); 645 646 hWnd = CreateWindowW( 647 szClass, 648 szTitle, 649 WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 650 CW_USEDEFAULT, 651 CW_USEDEFAULT, 652 CW_USEDEFAULT, 653 CW_USEDEFAULT, 654 NULL, 655 NULL, 656 hInst, 657 NULL); 658 659 if (hWnd != NULL) 660 { 661 LoadSettings(); 662 ShowWindow(hWnd, SW_SHOW); 663 UpdateWindow(hWnd); 664 } 665 666 return hWnd; 667 } 668 669 INT 670 WINAPI 671 wWinMain(HINSTANCE hInst, 672 HINSTANCE hPrev, 673 LPWSTR Cmd, 674 int iCmd) 675 { 676 INITCOMMONCONTROLSEX iccx; 677 INT Ret = 1; 678 HMODULE hRichEd20; 679 MSG Msg; 680 681 hInstance = hInst; 682 683 /* Mirroring code for the titlebar */ 684 switch (GetUserDefaultUILanguage()) 685 { 686 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): 687 SetProcessDefaultLayout(LAYOUT_RTL); 688 break; 689 690 default: 691 break; 692 } 693 694 iccx.dwSize = sizeof(INITCOMMONCONTROLSEX); 695 iccx.dwICC = ICC_TAB_CLASSES; 696 InitCommonControlsEx(&iccx); 697 698 if (RegisterMapClasses(hInstance)) 699 { 700 hRichEd20 = LoadLibraryW(L"RICHED20.DLL"); 701 702 if (hRichEd20 != NULL) 703 { 704 InitInstance(hInst); 705 706 for (;;) 707 { 708 if (GetMessage(&Msg, NULL, 0, 0) <= 0) 709 { 710 Ret = Msg.wParam; 711 break; 712 } 713 714 /* NOTE: CreateDialog needs IsDialogMessage call in message loop */ 715 if (hCharmapDlg && IsDialogMessage(hCharmapDlg, &Msg)) 716 continue; 717 #ifndef REMOVE_ADVANCED 718 if (hAdvancedDlg && IsDialogMessage(hAdvancedDlg, &Msg)) 719 continue; 720 #endif 721 722 TranslateMessage(&Msg); 723 DispatchMessage(&Msg); 724 } 725 726 FreeLibrary(hRichEd20); 727 } 728 UnregisterMapClasses(hInstance); 729 } 730 731 return Ret; 732 } 733