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