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