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