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