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
FillCharacterSetComboList(HWND hwndCombo)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
FillGroupByComboList(HWND hwndCombo)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
EnumFontNames(ENUMLOGFONTEXW * lpelfe,NEWTEXTMETRICEXW * lpntme,DWORD FontType,LPARAM lParam)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
FillFontStyleComboList(HWND hwndCombo)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
ChangeMapFont(HWND hDlg)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
CopyCharacters(HWND hDlg)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
GetFontMetrics(HWND hWnd,HFONT hFont)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
AddCharToSelection(HWND hDlg,WCHAR ch)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
UpdateSettings(HWND hDlg)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
UpdateStatusBar(WCHAR wch)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
ChangeView(HWND hWnd)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
CharMapDlgProc(HWND hDlg,UINT Message,WPARAM wParam,LPARAM lParam)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
AdvancedDlgProc(HWND hDlg,UINT Message,WPARAM wParam,LPARAM lParam)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
PanelOnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam)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
PanelWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)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
InitInstance(HINSTANCE hInst)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
wWinMain(HINSTANCE hInst,HINSTANCE hPrev,LPWSTR Cmd,int iCmd)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