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