xref: /reactos/base/applications/charmap/charmap.c (revision 09dde2cf)
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