1 /*
2 * PROJECT: ReactOS Character Map
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/charmap/MainWindow.cpp
5 * PURPOSE: Implements the main dialog window
6 * COPYRIGHT: Copyright 2015 Ged Murphy <gedmurphy@reactos.org>
7 */
8
9
10 #include "precomp.h"
11 #include "MainWindow.h"
12
13
14 /* DATA *****************************************************/
15
16 #define ID_ABOUT 0x1
17
18 HINSTANCE g_hInstance = NULL;
19
20
21 /* PUBLIC METHODS **********************************************/
22
CCharMapWindow(void)23 CCharMapWindow::CCharMapWindow(void) :
24 m_hMainWnd(NULL),
25 m_hStatusBar(NULL),
26 m_CmdShow(0),
27 m_hRichEd(NULL),
28 m_GridView(nullptr)
29 {
30 m_GridView = new CGridView();
31 }
32
~CCharMapWindow(void)33 CCharMapWindow::~CCharMapWindow(void)
34 {
35 }
36
37 bool
Create(_In_ HINSTANCE hInst,_In_ int nCmdShow)38 CCharMapWindow::Create(_In_ HINSTANCE hInst,
39 _In_ int nCmdShow)
40 {
41 INITCOMMONCONTROLSEX icex;
42 CAtlStringW szAppName;
43 int Ret = 1;
44
45 // Store the instance
46 g_hInstance = hInst;
47 m_CmdShow = nCmdShow;
48
49 // Initialize common controls
50 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
51 icex.dwICC = ICC_BAR_CLASSES | ICC_COOL_CLASSES;
52 InitCommonControlsEx(&icex);
53
54 // Load the application name
55 if (szAppName.LoadStringW(g_hInstance, IDS_TITLE))
56 {
57 // Initialize the main window
58 if (Initialize(szAppName, nCmdShow))
59 {
60 // Run the application
61 Ret = Run();
62
63 // Uninitialize the main window
64 Uninitialize();
65 }
66 }
67
68 return (Ret == 0);
69 }
70
71
72
73 /* PRIVATE METHODS **********************************************/
74
75 bool
Initialize(_In_z_ LPCTSTR lpCaption,_In_ int nCmdShow)76 CCharMapWindow::Initialize(_In_z_ LPCTSTR lpCaption,
77 _In_ int nCmdShow)
78 {
79 // The dialog has a rich edit text box
80 m_hRichEd = LoadLibraryW(L"riched20.DLL");
81 if (m_hRichEd == NULL) return false;
82
83 return !!(CreateDialogParamW(g_hInstance,
84 MAKEINTRESOURCE(IDD_CHARMAP),
85 NULL,
86 DialogProc,
87 (LPARAM)this));
88 }
89
90 void
Uninitialize(void)91 CCharMapWindow::Uninitialize(void)
92 {
93 if (m_hRichEd)
94 FreeLibrary(m_hRichEd);
95 }
96
97 int
Run(void)98 CCharMapWindow::Run(void)
99 {
100 MSG Msg;
101
102 // Pump the message queue
103 while (GetMessageW(&Msg, NULL, 0, 0) != 0)
104 {
105 TranslateMessage(&Msg);
106 DispatchMessageW(&Msg);
107 }
108
109 return 0;
110 }
111
112 void
UpdateStatusBar(_In_ bool InMenuLoop)113 CCharMapWindow::UpdateStatusBar(_In_ bool InMenuLoop)
114 {
115 SendMessageW(m_hStatusBar,
116 SB_SIMPLE,
117 (WPARAM)InMenuLoop,
118 0);
119 }
120
121 bool
CreateStatusBar(void)122 CCharMapWindow::CreateStatusBar(void)
123 {
124 int StatWidths[] = { 110, -1 }; // widths of status bar
125 bool bRet = FALSE;
126
127 // Create the status bar
128 m_hStatusBar = CreateWindowExW(0,
129 STATUSCLASSNAME,
130 NULL,
131 WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
132 0, 0, 0, 0,
133 m_hMainWnd,
134 (HMENU)IDD_STATUSBAR,
135 g_hInstance,
136 NULL);
137 if (m_hStatusBar)
138 {
139 // Create the sections
140 bRet = (SendMessageW(m_hStatusBar,
141 SB_SETPARTS,
142 sizeof(StatWidths) / sizeof(int),
143 (LPARAM)StatWidths) != 0);
144
145 // Set the status bar for multiple parts output
146 SendMessage(m_hStatusBar, SB_SIMPLE, (WPARAM)FALSE, (LPARAM)0);
147 }
148
149 return bRet;
150 }
151
152 bool
StatusBarLoadString(_In_ HWND hStatusBar,_In_ INT PartId,_In_ HINSTANCE hInstance,_In_ UINT uID)153 CCharMapWindow::StatusBarLoadString(_In_ HWND hStatusBar,
154 _In_ INT PartId,
155 _In_ HINSTANCE hInstance,
156 _In_ UINT uID)
157 {
158 CAtlStringW szMessage;
159 bool bRet = false;
160
161 // Load the string from the resource
162 if (szMessage.LoadStringW(hInstance, uID))
163 {
164 // Display it on the status bar
165 bRet = (SendMessageW(hStatusBar,
166 SB_SETTEXT,
167 (WPARAM)PartId,
168 (LPARAM)szMessage.GetBuffer()) != 0);
169 }
170
171 return bRet;
172 }
173
174 BOOL
OnCreate(_In_ HWND hDlg)175 CCharMapWindow::OnCreate(_In_ HWND hDlg)
176 {
177 m_hMainWnd = hDlg;
178
179 if (!CreateStatusBar())
180 return FALSE;
181
182 if (!m_GridView->Create(hDlg))
183 return FALSE;
184
185 // Load an 'about' option into the system menu
186 HMENU hSysMenu;
187 hSysMenu = GetSystemMenu(m_hMainWnd, FALSE);
188 if (hSysMenu != NULL)
189 {
190 CAtlStringW AboutText;
191 if (AboutText.LoadStringW(IDS_ABOUT))
192 {
193 AppendMenuW(hSysMenu, MF_SEPARATOR, 0, NULL);
194 AppendMenuW(hSysMenu, MF_STRING, ID_ABOUT, AboutText);
195 }
196 }
197
198 // Add all the fonts to the list
199 if (!CreateFontComboBox())
200 return FALSE;
201
202 ChangeMapFont();
203
204 // Configure Richedit control for sending notification changes.
205 DWORD evMask;
206 evMask = SendDlgItemMessage(hDlg, IDC_TEXTBOX, EM_GETEVENTMASK, 0, 0);
207 evMask |= ENM_CHANGE;
208 SendDlgItemMessage(hDlg, IDC_TEXTBOX, EM_SETEVENTMASK, 0, (LPARAM)evMask);
209
210 // Display the window according to the user request
211 ShowWindow(m_hMainWnd, m_CmdShow);
212
213 return TRUE;
214 }
215
216 BOOL
OnSize(_In_ WPARAM wParam)217 CCharMapWindow::OnSize(
218 _In_ WPARAM wParam
219 )
220 {
221 RECT rcClient, rcStatus;
222 INT lvHeight, iStatusHeight;
223
224 // Resize the status bar
225 SendMessage(m_hStatusBar, WM_SIZE, 0, 0);
226
227 // Get the statusbar rect and save the height
228 GetWindowRect(m_hStatusBar, &rcStatus);
229 iStatusHeight = rcStatus.bottom - rcStatus.top;
230
231 // Get the full client rect
232 GetClientRect(m_hMainWnd, &rcClient);
233
234 // Calculate the remaining height for the gridview
235 lvHeight = rcClient.bottom - iStatusHeight;
236
237 // Resize the grid view
238 SendMessageW(m_GridView->GetHwnd(), WM_SIZE, wParam, 0);
239
240 return TRUE;
241 }
242
243 BOOL
OnNotify(_In_ LPARAM lParam)244 CCharMapWindow::OnNotify(_In_ LPARAM lParam)
245 {
246 LPNMHDR NmHdr = (LPNMHDR)lParam;
247 LRESULT Ret = 0;
248
249 switch (NmHdr->code)
250 {
251 case NM_RCLICK:
252 {
253 break;
254 }
255
256 case NM_DBLCLK:
257 case NM_RETURN:
258 {
259 break;
260 }
261 }
262
263 return Ret;
264 }
265
266 BOOL
OnContext(_In_ LPARAM lParam)267 CCharMapWindow::OnContext(_In_ LPARAM lParam)
268 {
269 return 0;// m_GridView->OnContextMenu(lParam);
270 }
271
272 BOOL
OnCommand(_In_ WPARAM wParam,_In_ LPARAM)273 CCharMapWindow::OnCommand(_In_ WPARAM wParam,
274 _In_ LPARAM /*lParam*/)
275 {
276 LRESULT RetCode = 0;
277 WORD Msg;
278
279 // Get the message
280 Msg = LOWORD(wParam);
281
282 switch (Msg)
283 {
284 case IDC_CHECK_ADVANCED:
285 break;
286
287 case IDC_FONTCOMBO:
288 if (HIWORD(wParam) == CBN_SELCHANGE)
289 {
290 ChangeMapFont();
291 }
292 break;
293
294 default:
295 // We didn't handle it
296 RetCode = -1;
297 break;
298 }
299
300 return RetCode;
301 }
302
303 BOOL
OnDestroy(void)304 CCharMapWindow::OnDestroy(void)
305 {
306 // Clear the user data pointer
307 SetWindowLongPtr(m_hMainWnd, GWLP_USERDATA, 0);
308
309 // Break the message loop
310 PostQuitMessage(0);
311
312 return TRUE;
313 }
314
315 INT_PTR CALLBACK
DialogProc(_In_ HWND hwndDlg,_In_ UINT Msg,_In_ WPARAM wParam,_In_ LPARAM lParam)316 CCharMapWindow::DialogProc(
317 _In_ HWND hwndDlg,
318 _In_ UINT Msg,
319 _In_ WPARAM wParam,
320 _In_ LPARAM lParam
321 )
322 {
323 CCharMapWindow *This;
324 LRESULT RetCode = 0;
325
326 // Get the object pointer from window context
327 This = (CCharMapWindow *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
328 if (This == NULL)
329 {
330 // Check that this isn't a create message
331 if (Msg != WM_INITDIALOG)
332 {
333 // Don't handle null info pointer
334 return FALSE;
335 }
336 }
337
338 switch (Msg)
339 {
340 case WM_INITDIALOG:
341 {
342 // Get the object pointer from the create param
343 This = (CCharMapWindow *)lParam;
344
345 // Store the pointer in the window's global user data
346 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)This);
347
348 // Call the create handler
349 return This->OnCreate(hwndDlg);
350 }
351
352 case WM_SIZE:
353 {
354 return This->OnSize(wParam);
355 }
356
357 case WM_NOTIFY:
358 {
359 return This->OnNotify(lParam);
360 }
361
362 case WM_CONTEXTMENU:
363 {
364 return This->OnContext(lParam);
365 }
366
367 case WM_COMMAND:
368 {
369 return This->OnCommand(wParam, lParam);
370 }
371
372 case WM_SYSCOMMAND:
373 switch (wParam)
374 {
375 case ID_ABOUT:
376 MessageBoxW(This->m_hMainWnd,
377 L"ReactOS Character Map\nCopyright Ged Murphy 2015",
378 L"About",
379 MB_OK | MB_APPLMODAL);
380 break;
381 }
382 break;
383
384 case WM_ENTERMENULOOP:
385 {
386 This->UpdateStatusBar(true);
387 return TRUE;
388 }
389
390 case WM_EXITMENULOOP:
391 {
392 This->UpdateStatusBar(false);
393 return TRUE;
394 }
395
396 case WM_CLOSE:
397 {
398 // Destroy the main window
399 return DestroyWindow(hwndDlg);
400 }
401
402
403 case WM_DESTROY:
404 {
405 // Call the destroy handler
406 return This->OnDestroy();
407 }
408 }
409
410 return FALSE;
411 }
412
413 struct EnumFontParams
414 {
415 CCharMapWindow *This;
416 HWND hCombo;
417 };
418
419 int
420 CALLBACK
EnumDisplayFont(ENUMLOGFONTEXW * lpelfe,NEWTEXTMETRICEXW * lpntme,DWORD FontType,LPARAM lParam)421 CCharMapWindow::EnumDisplayFont(ENUMLOGFONTEXW *lpelfe,
422 NEWTEXTMETRICEXW *lpntme,
423 DWORD FontType,
424 LPARAM lParam)
425 {
426 EnumFontParams *Params = (EnumFontParams *)lParam;
427 LPWSTR pszName = lpelfe->elfLogFont.lfFaceName;
428
429 /* Skip rotated font */
430 if (pszName[0] == L'@') return 1;
431
432 /* make sure font doesn't already exist in our list */
433 if (SendMessageW(Params->hCombo,
434 CB_FINDSTRINGEXACT,
435 0,
436 (LPARAM)pszName) == CB_ERR)
437 {
438 INT idx;
439 idx = (INT)SendMessageW(Params->hCombo,
440 CB_ADDSTRING,
441 0,
442 (LPARAM)pszName);
443
444 /* record the font's attributes (Fixedwidth and Truetype) */
445 BOOL fFixed = (lpelfe->elfLogFont.lfPitchAndFamily & FIXED_PITCH) ? TRUE : FALSE;
446 BOOL fTrueType = (lpelfe->elfLogFont.lfOutPrecision == OUT_STROKE_PRECIS) ? TRUE : FALSE;
447
448 /* store this information in the list-item's userdata area */
449 SendMessageW(Params->hCombo,
450 CB_SETITEMDATA,
451 idx,
452 MAKEWPARAM(fFixed, fTrueType));
453 }
454
455 return 1;
456 }
457
458
459 bool
CreateFontComboBox()460 CCharMapWindow::CreateFontComboBox()
461 {
462 HWND hCombo;
463 hCombo = GetDlgItem(m_hMainWnd, IDC_FONTCOMBO);
464
465 NONCLIENTMETRICSW NonClientMetrics;
466 NonClientMetrics.cbSize = sizeof(NONCLIENTMETRICSW);
467 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
468 sizeof(NONCLIENTMETRICSW),
469 &NonClientMetrics,
470 0);
471
472 // Get a handle to the font
473 HFONT GuiFont;
474 GuiFont = CreateFontIndirectW(&NonClientMetrics.lfMessageFont);
475
476 // Set the font used in the combo box
477 SendMessageW(hCombo,
478 WM_SETFONT,
479 (WPARAM)GuiFont,
480 0);
481
482 // Set the fonts which we want to enumerate
483 LOGFONTW FontsToEnum;
484 ZeroMemory(&FontsToEnum, sizeof(LOGFONTW));
485 FontsToEnum.lfCharSet = DEFAULT_CHARSET;
486
487 // Set the params we want to pass to the callback
488 EnumFontParams Params;
489 Params.This = this;
490 Params.hCombo = hCombo;
491
492 // Get a DC for combo box
493 HDC hdc;
494 hdc = GetDC(hCombo);
495
496 // Enumerate all the fonts
497 int ret;
498 ret = EnumFontFamiliesExW(hdc,
499 &FontsToEnum,
500 (FONTENUMPROCW)EnumDisplayFont,
501 (LPARAM)&Params,
502 0);
503
504 ReleaseDC(hCombo, hdc);
505 DeleteObject(GuiFont);
506
507 // Select the first item in the list
508 SendMessageW(hCombo,
509 CB_SETCURSEL,
510 0,
511 0);
512
513 return (ret == 1);
514 }
515
516 bool
ChangeMapFont()517 CCharMapWindow::ChangeMapFont(
518 )
519 {
520 HWND hCombo;
521 hCombo = GetDlgItem(m_hMainWnd, IDC_FONTCOMBO);
522
523 INT Length;
524 Length = GetWindowTextLengthW(hCombo);
525 if (!Length) return false;
526
527 CAtlStringW FontName;
528 FontName.Preallocate(Length);
529
530 SendMessageW(hCombo,
531 WM_GETTEXT,
532 FontName.GetAllocLength(),
533 (LPARAM)FontName.GetBuffer());
534
535 return m_GridView->SetFont(FontName);
536 }
537