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