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