xref: /reactos/base/applications/mspaint/main.cpp (revision 501c2bdd)
1 /*
2  * PROJECT:    PAINT for ReactOS
3  * LICENSE:    LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4  * PURPOSE:    Initializing everything
5  * COPYRIGHT:  Copyright 2015 Benedikt Freisen <b.freisen@gmx.net>
6  */
7 
8 #include "precomp.h"
9 
10 #include <mapi.h>
11 
12 POINT g_ptStart, g_ptEnd;
13 BOOL g_askBeforeEnlarging = FALSE;  // TODO: initialize from registry
14 HINSTANCE g_hinstExe = NULL;
15 TCHAR g_szFileName[MAX_LONG_PATH] = { 0 };
16 WCHAR g_szMailTempFile[MAX_LONG_PATH] = { 0 };
17 BOOL g_isAFile = FALSE;
18 BOOL g_imageSaved = FALSE;
19 BOOL g_showGrid = FALSE;
20 
21 CMainWindow mainWindow;
22 
23 /* FUNCTIONS ********************************************************/
24 
25 // get file name extension from filter string
26 static BOOL
27 FileExtFromFilter(LPTSTR pExt, OPENFILENAME *pOFN)
28 {
29     LPTSTR pchExt = pExt;
30     *pchExt = 0;
31 
32     DWORD nIndex = 1;
33     for (LPCTSTR pch = pOFN->lpstrFilter; *pch; ++nIndex)
34     {
35         pch += lstrlen(pch) + 1;
36         if (pOFN->nFilterIndex == nIndex)
37         {
38             for (++pch; *pch && *pch != _T(';'); ++pch)
39             {
40                 *pchExt++ = *pch;
41             }
42             *pchExt = 0;
43             CharLower(pExt);
44             return TRUE;
45         }
46         pch += lstrlen(pch) + 1;
47     }
48     return FALSE;
49 }
50 
51 // Hook procedure for OPENFILENAME to change the file name extension
52 static UINT_PTR APIENTRY
53 OFNHookProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
54 {
55     HWND hParent;
56     OFNOTIFY *pon;
57     switch (uMsg)
58     {
59     case WM_NOTIFY:
60         pon = (OFNOTIFY *)lParam;
61         if (pon->hdr.code == CDN_TYPECHANGE)
62         {
63             hParent = GetParent(hwnd);
64             TCHAR Path[MAX_PATH];
65             SendMessage(hParent, CDM_GETFILEPATH, _countof(Path), (LPARAM)Path);
66             FileExtFromFilter(PathFindExtension(Path), pon->lpOFN);
67             SendMessage(hParent, CDM_SETCONTROLTEXT, 0x047c, (LPARAM)PathFindFileName(Path));
68             lstrcpyn(pon->lpOFN->lpstrFile, Path, pon->lpOFN->nMaxFile);
69         }
70         break;
71     }
72     return 0;
73 }
74 
75 typedef ULONG (WINAPI *FN_MAPISendMail)(LHANDLE, ULONG_PTR, lpMapiMessage, FLAGS, ULONG);
76 typedef ULONG (WINAPI *FN_MAPISendMailW)(LHANDLE, ULONG_PTR, lpMapiMessageW, FLAGS, ULONG);
77 
78 BOOL OpenMailer(HWND hWnd, LPCWSTR pszPathName)
79 {
80     // Delete the temporary file if any
81     if (g_szMailTempFile[0])
82     {
83         ::DeleteFileW(g_szMailTempFile);
84         g_szMailTempFile[0] = UNICODE_NULL;
85     }
86 
87     CStringW strFileTitle;
88     if (PathFileExistsW(pszPathName) && imageModel.IsImageSaved())
89     {
90         strFileTitle = PathFindFileNameW(pszPathName);
91     }
92     else // Not existing or not saved
93     {
94         // Get the name of a temporary file
95         WCHAR szTempDir[MAX_PATH];
96         ::GetTempPathW(_countof(szTempDir), szTempDir);
97         if (!::GetTempFileNameW(szTempDir, L"afx", 0, g_szMailTempFile))
98             return FALSE; // Failure
99 
100         if (PathFileExistsW(g_szFileName))
101         {
102             // Set file title
103             strFileTitle = PathFindFileNameW(g_szFileName);
104 
105             // Copy to the temporary file
106             if (!::CopyFileW(g_szFileName, g_szMailTempFile, FALSE))
107             {
108                 g_szMailTempFile[0] = UNICODE_NULL;
109                 return FALSE; // Failure
110             }
111         }
112         else
113         {
114             // Set file title
115             strFileTitle.LoadString(IDS_DEFAULTFILENAME);
116             strFileTitle += L".png";
117 
118             // Save it to the temporary file
119             HBITMAP hbmLocked = imageModel.LockBitmap();
120             BOOL ret = SaveDIBToFile(hbmLocked, g_szMailTempFile, FALSE, Gdiplus::ImageFormatPNG);
121             imageModel.UnlockBitmap(hbmLocked);
122             if (!ret)
123             {
124                 g_szMailTempFile[0] = UNICODE_NULL;
125                 return FALSE; // Failure
126             }
127         }
128 
129         // Use the temporary file
130         pszPathName = g_szMailTempFile;
131     }
132 
133     // Load "mapi32.dll"
134     HINSTANCE hMAPI = LoadLibraryW(L"mapi32.dll");
135     if (!hMAPI)
136         return FALSE; // Failure
137 
138     // Attachment
139     MapiFileDescW attachmentW = { 0 };
140     attachmentW.nPosition = (ULONG)-1;
141     attachmentW.lpszPathName = (LPWSTR)pszPathName;
142     attachmentW.lpszFileName = (LPWSTR)(LPCWSTR)strFileTitle;
143 
144     // Message with attachment
145     MapiMessageW messageW = { 0 };
146     messageW.lpszSubject = NULL;
147     messageW.nFileCount = 1;
148     messageW.lpFiles = &attachmentW;
149 
150     // First, try to open the mailer by the function of Unicode version
151     FN_MAPISendMailW pMAPISendMailW = (FN_MAPISendMailW)::GetProcAddress(hMAPI, "MAPISendMailW");
152     if (pMAPISendMailW)
153     {
154         pMAPISendMailW(0, (ULONG_PTR)hWnd, &messageW, MAPI_DIALOG | MAPI_LOGON_UI, 0);
155         ::FreeLibrary(hMAPI);
156         return TRUE; // MAPISendMailW will show an error message on failure
157     }
158 
159     // Convert to ANSI strings
160     CStringA szPathNameA(pszPathName), szFileTitleA(strFileTitle);
161 
162     MapiFileDesc attachment = { 0 };
163     attachment.nPosition = (ULONG)-1;
164     attachment.lpszPathName = (LPSTR)(LPCSTR)szPathNameA;
165     attachment.lpszFileName = (LPSTR)(LPCSTR)szFileTitleA;
166 
167     MapiMessage message = { 0 };
168     message.lpszSubject = NULL;
169     message.nFileCount = 1;
170     message.lpFiles = &attachment;
171 
172     // Try again but in ANSI version
173     FN_MAPISendMail pMAPISendMail = (FN_MAPISendMail)::GetProcAddress(hMAPI, "MAPISendMail");
174     if (pMAPISendMail)
175     {
176         pMAPISendMail(0, (ULONG_PTR)hWnd, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0);
177         ::FreeLibrary(hMAPI);
178         return TRUE; // MAPISendMail will show an error message on failure
179     }
180 
181     ::FreeLibrary(hMAPI);
182     return FALSE; // Failure
183 }
184 
185 BOOL CMainWindow::GetOpenFileName(IN OUT LPTSTR pszFile, INT cchMaxFile)
186 {
187     static OPENFILENAME ofn = { 0 };
188     static CString strFilter;
189 
190     if (ofn.lStructSize == 0)
191     {
192         // The "All Files" item text
193         CString strAllPictureFiles;
194         strAllPictureFiles.LoadString(g_hinstExe, IDS_ALLPICTUREFILES);
195 
196         // Get the import filter
197         CSimpleArray<GUID> aguidFileTypesI;
198         CImage::GetImporterFilterString(strFilter, aguidFileTypesI, strAllPictureFiles,
199                                         CImage::excludeDefaultLoad, _T('\0'));
200 
201         // Initializing the OPENFILENAME structure for GetOpenFileName
202         ZeroMemory(&ofn, sizeof(ofn));
203         ofn.lStructSize = sizeof(ofn);
204         ofn.hwndOwner   = m_hWnd;
205         ofn.hInstance   = g_hinstExe;
206         ofn.lpstrFilter = strFilter;
207         ofn.Flags       = OFN_EXPLORER | OFN_HIDEREADONLY;
208         ofn.lpstrDefExt = L"png";
209     }
210 
211     ofn.lpstrFile = pszFile;
212     ofn.nMaxFile  = cchMaxFile;
213     return ::GetOpenFileName(&ofn);
214 }
215 
216 BOOL CMainWindow::GetSaveFileName(IN OUT LPTSTR pszFile, INT cchMaxFile)
217 {
218     static OPENFILENAME sfn = { 0 };
219     static CString strFilter;
220 
221     if (sfn.lStructSize == 0)
222     {
223         // Get the export filter
224         CSimpleArray<GUID> aguidFileTypesE;
225         CImage::GetExporterFilterString(strFilter, aguidFileTypesE, NULL,
226                                         CImage::excludeDefaultSave, _T('\0'));
227 
228         // Initializing the OPENFILENAME structure for GetSaveFileName
229         ZeroMemory(&sfn, sizeof(sfn));
230         sfn.lStructSize = sizeof(sfn);
231         sfn.hwndOwner   = m_hWnd;
232         sfn.hInstance   = g_hinstExe;
233         sfn.lpstrFilter = strFilter;
234         sfn.Flags       = OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_ENABLEHOOK;
235         sfn.lpfnHook    = OFNHookProc;
236         sfn.lpstrDefExt = L"png";
237 
238         LPWSTR pchDotExt = PathFindExtensionW(pszFile);
239         if (*pchDotExt == UNICODE_NULL)
240         {
241             // Choose PNG
242             wcscat(pszFile, L".png");
243             for (INT i = 0; i < aguidFileTypesE.GetSize(); ++i)
244             {
245                 if (aguidFileTypesE[i] == Gdiplus::ImageFormatPNG)
246                 {
247                     sfn.nFilterIndex = i + 1;
248                     break;
249                 }
250             }
251         }
252     }
253 
254     sfn.lpstrFile = pszFile;
255     sfn.nMaxFile  = cchMaxFile;
256     return ::GetSaveFileName(&sfn);
257 }
258 
259 BOOL CMainWindow::ChooseColor(IN OUT COLORREF *prgbColor)
260 {
261     static CHOOSECOLOR choosecolor = { 0 };
262     static COLORREF custColors[16] =
263     {
264         0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff,
265         0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff
266     };
267 
268     if (choosecolor.lStructSize == 0)
269     {
270         // Initializing the CHOOSECOLOR structure for ChooseColor
271         ZeroMemory(&choosecolor, sizeof(choosecolor));
272         choosecolor.lStructSize  = sizeof(choosecolor);
273         choosecolor.hwndOwner    = m_hWnd;
274         choosecolor.lpCustColors = custColors;
275     }
276 
277     choosecolor.Flags = CC_RGBINIT;
278     choosecolor.rgbResult = *prgbColor;
279     if (!::ChooseColor(&choosecolor))
280         return FALSE;
281 
282     *prgbColor = choosecolor.rgbResult;
283     return TRUE;
284 }
285 
286 HWND CMainWindow::DoCreate()
287 {
288     ::LoadString(g_hinstExe, IDS_DEFAULTFILENAME, g_szFileName, _countof(g_szFileName));
289 
290     CString strTitle;
291     strTitle.Format(IDS_WINDOWTITLE, PathFindFileName(g_szFileName));
292 
293     RECT& rc = registrySettings.WindowPlacement.rcNormalPosition;
294     return Create(HWND_DESKTOP, rc, strTitle, WS_OVERLAPPEDWINDOW, WS_EX_ACCEPTFILES);
295 }
296 
297 // entry point
298 INT WINAPI
299 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, INT nCmdShow)
300 {
301     g_hinstExe = hInstance;
302 
303     // Initialize common controls library
304     INITCOMMONCONTROLSEX iccx;
305     iccx.dwSize = sizeof(iccx);
306     iccx.dwICC = ICC_STANDARD_CLASSES | ICC_USEREX_CLASSES | ICC_BAR_CLASSES;
307     InitCommonControlsEx(&iccx);
308 
309     // Load settings from registry
310     registrySettings.Load(nCmdShow);
311 
312     // Create the main window
313     if (!mainWindow.DoCreate())
314     {
315         MessageBox(NULL, TEXT("Failed to create main window."), NULL, MB_ICONERROR);
316         return 1;
317     }
318 
319     // Initialize imageModel
320     if (__argc < 2 || !DoLoadImageFile(mainWindow, __targv[1], TRUE))
321         InitializeImage(NULL, NULL, FALSE);
322 
323     // Make the window visible on the screen
324     mainWindow.ShowWindow(registrySettings.WindowPlacement.showCmd);
325 
326     // Load the access keys
327     HACCEL hAccel = ::LoadAccelerators(hInstance, MAKEINTRESOURCE(800));
328 
329     // The message loop
330     MSG msg;
331     while (::GetMessage(&msg, NULL, 0, 0))
332     {
333         if (fontsDialog.IsWindow() && fontsDialog.IsDialogMessage(&msg))
334             continue;
335 
336         if (::TranslateAccelerator(mainWindow, hAccel, &msg))
337             continue;
338 
339         ::TranslateMessage(&msg);
340         ::DispatchMessage(&msg);
341     }
342 
343     // Unload the access keys
344     ::DestroyAcceleratorTable(hAccel);
345 
346     // Write back settings to registry
347     registrySettings.Store();
348 
349     if (g_szMailTempFile[0])
350         ::DeleteFileW(g_szMailTempFile);
351 
352     // Return the value that PostQuitMessage() gave
353     return (INT)msg.wParam;
354 }
355