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 
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 
33 CCharMapWindow::~CCharMapWindow(void)
34 {
35 }
36 
37 bool
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
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
91 CCharMapWindow::Uninitialize(void)
92 {
93     if (m_hRichEd)
94         FreeLibrary(m_hRichEd);
95 }
96 
97 int
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
113 CCharMapWindow::UpdateStatusBar(_In_ bool InMenuLoop)
114 {
115     SendMessageW(m_hStatusBar,
116                  SB_SIMPLE,
117                  (WPARAM)InMenuLoop,
118                  0);
119 }
120 
121 bool
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
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
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
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
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
267 CCharMapWindow::OnContext(_In_ LPARAM lParam)
268 {
269     return 0;// m_GridView->OnContextMenu(lParam);
270 }
271 
272 BOOL
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
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
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             // Apportion blame
377             MessageBoxW(This->m_hMainWnd,
378                         L"ReactOS Character Map\r\nCopyright Ged Murphy 2015",
379                         L"About",
380                         MB_OK | MB_APPLMODAL);
381             break;
382         }
383         break;
384 
385     case WM_ENTERMENULOOP:
386     {
387         This->UpdateStatusBar(true);
388         return TRUE;
389     }
390 
391     case WM_EXITMENULOOP:
392     {
393         This->UpdateStatusBar(false);
394         return TRUE;
395     }
396 
397     case WM_CLOSE:
398     {
399         // Destroy the main window
400         return DestroyWindow(hwndDlg);
401     }
402 
403 
404     case WM_DESTROY:
405     {
406         // Call the destroy handler
407         return This->OnDestroy();
408     }
409     }
410 
411     return FALSE;
412 }
413 
414 struct EnumFontParams
415 {
416     CCharMapWindow *This;
417     HWND hCombo;
418 };
419 
420 int
421 CALLBACK
422 CCharMapWindow::EnumDisplayFont(ENUMLOGFONTEXW *lpelfe,
423                                 NEWTEXTMETRICEXW *lpntme,
424                                 DWORD FontType,
425                                 LPARAM lParam)
426 {
427     EnumFontParams *Params = (EnumFontParams *)lParam;
428     LPWSTR pszName = lpelfe->elfLogFont.lfFaceName;
429 
430     /* Skip rotated font */
431     if (pszName[0] == L'@') return 1;
432 
433     /* make sure font doesn't already exist in our list */
434     if (SendMessageW(Params->hCombo,
435                      CB_FINDSTRINGEXACT,
436                      0,
437                      (LPARAM)pszName) == CB_ERR)
438     {
439         INT idx;
440         idx = (INT)SendMessageW(Params->hCombo,
441                                 CB_ADDSTRING,
442                                 0,
443                                 (LPARAM)pszName);
444 
445         /* record the font's attributes (Fixedwidth and Truetype) */
446         BOOL fFixed = (lpelfe->elfLogFont.lfPitchAndFamily & FIXED_PITCH) ? TRUE : FALSE;
447         BOOL fTrueType = (lpelfe->elfLogFont.lfOutPrecision == OUT_STROKE_PRECIS) ? TRUE : FALSE;
448 
449         /* store this information in the list-item's userdata area */
450         SendMessageW(Params->hCombo,
451                      CB_SETITEMDATA,
452                      idx,
453                      MAKEWPARAM(fFixed, fTrueType));
454     }
455 
456     return 1;
457 }
458 
459 
460 bool
461 CCharMapWindow::CreateFontComboBox()
462 {
463     HWND hCombo;
464     hCombo = GetDlgItem(m_hMainWnd, IDC_FONTCOMBO);
465 
466     NONCLIENTMETRICSW NonClientMetrics;
467     NonClientMetrics.cbSize = sizeof(NONCLIENTMETRICSW);
468     SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
469                           sizeof(NONCLIENTMETRICSW),
470                           &NonClientMetrics,
471                           0);
472 
473     // Get a handle to the font
474     HFONT GuiFont;
475     GuiFont = CreateFontIndirectW(&NonClientMetrics.lfMessageFont);
476 
477     // Set the font used in the combo box
478     SendMessageW(hCombo,
479                  WM_SETFONT,
480                  (WPARAM)GuiFont,
481                  0);
482 
483     // Set the fonts which we want to enumerate
484     LOGFONTW FontsToEnum;
485     ZeroMemory(&FontsToEnum, sizeof(LOGFONTW));
486     FontsToEnum.lfCharSet = DEFAULT_CHARSET;
487 
488     // Set the params we want to pass to the callback
489     EnumFontParams Params;
490     Params.This = this;
491     Params.hCombo = hCombo;
492 
493     // Get a DC for combo box
494     HDC hdc;
495     hdc = GetDC(hCombo);
496 
497     // Enumerate all the fonts
498     int ret;
499     ret = EnumFontFamiliesExW(hdc,
500                               &FontsToEnum,
501                               (FONTENUMPROCW)EnumDisplayFont,
502                               (LPARAM)&Params,
503                               0);
504 
505     ReleaseDC(hCombo, hdc);
506     DeleteObject(GuiFont);
507 
508     // Select the first item in the list
509     SendMessageW(hCombo,
510                  CB_SETCURSEL,
511                  0,
512                  0);
513 
514     return (ret == 1);
515 }
516 
517 bool
518 CCharMapWindow::ChangeMapFont(
519     )
520 {
521     HWND hCombo;
522     hCombo = GetDlgItem(m_hMainWnd, IDC_FONTCOMBO);
523 
524     INT Length;
525     Length = GetWindowTextLengthW(hCombo);
526     if (!Length) return false;
527 
528     CAtlStringW FontName;
529     FontName.Preallocate(Length);
530 
531     SendMessageW(hCombo,
532                  WM_GETTEXT,
533                  FontName.GetAllocLength(),
534                  (LPARAM)FontName.GetBuffer());
535 
536     return m_GridView->SetFont(FontName);
537 }
538