msvcrt: Implement ___setlc_active_func/___unguarded_readlc_active_add_func.
[wine] / dlls / shell32 / dialogs.c
1 /*
2  *      common shell dialogs
3  *
4  * Copyright 2000 Juergen Schmied
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <string.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include "winerror.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "commdlg.h"
34 #include "wine/debug.h"
35
36 #include "shellapi.h"
37 #include "shlobj.h"
38 #include "shell32_main.h"
39 #include "shresdef.h"
40 #include "undocshell.h"
41
42 typedef struct
43     {
44         HWND hwndOwner ;
45         HICON hIcon ;
46         LPCWSTR lpstrDirectory ;
47         LPCWSTR lpstrTitle ;
48         LPCWSTR lpstrDescription ;
49         UINT uFlags ;
50     } RUNFILEDLGPARAMS ;
51
52 typedef BOOL (*WINAPI LPFNOFN) (OPENFILENAMEW *) ;
53
54 WINE_DEFAULT_DEBUG_CHANNEL(shell);
55 static INT_PTR CALLBACK RunDlgProc (HWND, UINT, WPARAM, LPARAM) ;
56 static void FillList (HWND, char *, BOOL) ;
57
58
59 /*************************************************************************
60  * PickIconDlg                                  [SHELL32.62]
61  *
62  */
63 BOOL WINAPI PickIconDlg(
64         HWND hwndOwner,
65         LPSTR lpstrFile,
66         DWORD nMaxFile,
67         LPDWORD lpdwIconIndex)
68 {
69         FIXME("(%p,%s,%08x,%p):stub.\n",
70           hwndOwner, lpstrFile, nMaxFile,lpdwIconIndex);
71         return 0xffffffff;
72 }
73
74 /*************************************************************************
75  * RunFileDlgW                                  [internal]
76  *
77  * The Unicode function that is available as ordinal 61 on Windows NT/2000/XP/...
78  *
79  * SEE ALSO
80  *   RunFileDlgAW
81  */
82 void WINAPI RunFileDlgW(
83         HWND hwndOwner,
84         HICON hIcon,
85         LPCWSTR lpstrDirectory,
86         LPCWSTR lpstrTitle,
87         LPCWSTR lpstrDescription,
88         UINT uFlags)
89 {
90     static const WCHAR resnameW[] = {'S','H','E','L','L','_','R','U','N','_','D','L','G',0};
91     RUNFILEDLGPARAMS rfdp;
92     HRSRC hRes;
93     LPVOID template;
94     TRACE("\n");
95
96     rfdp.hwndOwner        = hwndOwner;
97     rfdp.hIcon            = hIcon;
98     rfdp.lpstrDirectory   = lpstrDirectory;
99     rfdp.lpstrTitle       = lpstrTitle;
100     rfdp.lpstrDescription = lpstrDescription;
101     rfdp.uFlags           = uFlags;
102
103     if (!(hRes = FindResourceW(shell32_hInstance, resnameW, (LPWSTR)RT_DIALOG)) ||
104         !(template = LoadResource(shell32_hInstance, hRes)))
105     {
106         ERR("Couldn't load SHELL_RUN_DLG resource\n");
107         ShellMessageBoxW(shell32_hInstance, hwndOwner, MAKEINTRESOURCEW(IDS_RUNDLG_ERROR), NULL, MB_OK | MB_ICONERROR);
108         return;
109     }
110
111     DialogBoxIndirectParamW(shell32_hInstance,
112                             template, hwndOwner, RunDlgProc, (LPARAM)&rfdp);
113
114 }
115
116 /* Dialog procedure for RunFileDlg */
117 static INT_PTR CALLBACK RunDlgProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
118     {
119     RUNFILEDLGPARAMS *prfdp = (RUNFILEDLGPARAMS *)GetWindowLongPtrW(hwnd, DWLP_USER);
120
121     switch (message)
122         {
123         case WM_INITDIALOG :
124             prfdp = (RUNFILEDLGPARAMS *)lParam ;
125             SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)prfdp);
126
127             if (prfdp->lpstrTitle)
128                 SetWindowTextW(hwnd, prfdp->lpstrTitle);
129             if (prfdp->lpstrDescription)
130                 SetWindowTextW(GetDlgItem(hwnd, IDC_RUNDLG_DESCRIPTION), prfdp->lpstrDescription);
131             if (prfdp->uFlags & RFF_NOBROWSE)
132             {
133                 HWND browse = GetDlgItem(hwnd, IDC_RUNDLG_BROWSE);
134                 ShowWindow(browse, SW_HIDE);
135                 EnableWindow(browse, FALSE);
136             }
137             if (prfdp->uFlags & RFF_NOLABEL)
138                 ShowWindow(GetDlgItem(hwnd, IDC_RUNDLG_LABEL), SW_HIDE);
139             if (prfdp->uFlags & RFF_CALCDIRECTORY)
140                 FIXME("RFF_CALCDIRECTORY not supported\n");
141
142             SetClassLongPtrW (hwnd, GCLP_HICON, (LPARAM)prfdp->hIcon) ;
143             SendMessageW (GetDlgItem (hwnd, IDC_RUNDLG_ICON), STM_SETICON,
144                           (WPARAM)LoadIconW (NULL, (LPCWSTR)IDI_WINLOGO), 0);
145             FillList (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), NULL, (prfdp->uFlags & RFF_NODEFAULT) == 0) ;
146             SetFocus (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH)) ;
147             return TRUE ;
148
149         case WM_COMMAND :
150             switch (LOWORD (wParam))
151                 {
152                 case IDOK :
153                     {
154                     int ic ;
155                     HWND htxt = GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH);
156                     if ((ic = GetWindowTextLengthW (htxt)))
157                         {
158                         WCHAR *psz ;
159                         psz = HeapAlloc( GetProcessHeap(), 0, (ic + 1)*sizeof(WCHAR) );
160                         GetWindowTextW (htxt, psz, ic + 1) ;
161
162                         /* according to http://www.codeproject.com/KB/shell/runfiledlg.aspx we should send a
163                          * WM_NOTIFY before execution */
164
165                         if (ShellExecuteW(hwnd, NULL, psz, NULL, prfdp->lpstrDirectory, SW_SHOWNORMAL) < (HINSTANCE)33)
166                             {
167                             char *pszSysMsg = NULL ;
168                             char szMsg[256];
169                             FormatMessageA (
170                                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
171                                 FORMAT_MESSAGE_FROM_SYSTEM |
172                                 FORMAT_MESSAGE_IGNORE_INSERTS,
173                                 NULL, GetLastError (),
174                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
175                                 (LPSTR)&pszSysMsg, 0, NULL
176                                 ) ;
177                             sprintf (szMsg, "Error: %s", pszSysMsg) ;
178                             LocalFree ((HLOCAL)pszSysMsg) ;
179                             MessageBoxA (hwnd, szMsg, "Nix", MB_OK | MB_ICONEXCLAMATION) ;
180
181                             HeapFree(GetProcessHeap(), 0, psz);
182                             SendMessageA (htxt, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
183                             return TRUE ;
184                             }
185
186                         /* FillList is still ANSI */
187                         GetWindowTextA (htxt, (LPSTR)psz, ic + 1) ;
188                         FillList (htxt, (LPSTR)psz, FALSE) ;
189
190                         HeapFree(GetProcessHeap(), 0, psz);
191                         EndDialog (hwnd, 0) ;
192                         }
193                     }
194
195                 case IDCANCEL :
196                     EndDialog (hwnd, 0) ;
197                     return TRUE ;
198
199                 case IDC_RUNDLG_BROWSE :
200                     {
201                     HMODULE hComdlg = NULL ;
202                     LPFNOFN ofnProc = NULL ;
203                     static const WCHAR comdlg32W[] = {'c','o','m','d','l','g','3','2',0};
204                     WCHAR szFName[1024] = {0};
205                     WCHAR *pszFilter, szCaption[MAX_PATH];
206                     OPENFILENAMEW ofn;
207
208                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_FILTER, (LPWSTR)&pszFilter, 0);
209                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_CAPTION, szCaption, MAX_PATH);
210
211                     ZeroMemory(&ofn, sizeof(ofn));
212                     ofn.lStructSize = sizeof(OPENFILENAMEW);
213                     ofn.hwndOwner = hwnd;
214                     ofn.lpstrFilter = pszFilter;
215                     ofn.lpstrFile = szFName;
216                     ofn.nMaxFile = 1023;
217                     ofn.lpstrTitle = szCaption;
218                     ofn.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
219                     ofn.lpstrInitialDir = prfdp->lpstrDirectory;
220
221                     if (NULL == (hComdlg = LoadLibraryExW (comdlg32W, NULL, 0)) ||
222                         NULL == (ofnProc = (LPFNOFN)GetProcAddress (hComdlg, "GetOpenFileNameW")))
223                     {
224                         ERR("Couldn't get GetOpenFileName function entry (lib=%p, proc=%p)\n", hComdlg, ofnProc);
225                         ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_RUNDLG_BROWSE_ERROR), NULL, MB_OK | MB_ICONERROR);
226                         return TRUE ;
227                     }
228
229                     if (ofnProc(&ofn))
230                     {
231                         SetFocus (GetDlgItem (hwnd, IDOK)) ;
232                         SetWindowTextW (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), szFName) ;
233                         SendMessageW (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
234                         SetFocus (GetDlgItem (hwnd, IDOK)) ;
235                     }
236
237                     FreeLibrary (hComdlg) ;
238
239                     return TRUE ;
240                     }
241                 }
242             return TRUE ;
243         }
244     return FALSE ;
245     }
246
247 /* This grabs the MRU list from the registry and fills the combo for the "Run" dialog above */
248 /* fShowDefault ignored if pszLatest != NULL */
249 static void FillList (HWND hCb, char *pszLatest, BOOL fShowDefault)
250     {
251     HKEY hkey ;
252 /*    char szDbgMsg[256] = "" ; */
253     char *pszList = NULL, *pszCmd = NULL, cMatch = 0, cMax = 0x60, szIndex[2] = "-" ;
254     DWORD icList = 0, icCmd = 0 ;
255     UINT Nix ;
256
257     SendMessageA (hCb, CB_RESETCONTENT, 0, 0) ;
258
259     if (ERROR_SUCCESS != RegCreateKeyExA (
260         HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU",
261         0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
262         MessageBoxA (hCb, "Unable to open registry key !", "Nix", MB_OK) ;
263
264     RegQueryValueExA (hkey, "MRUList", NULL, NULL, NULL, &icList) ;
265
266     if (icList > 0)
267         {
268         pszList = HeapAlloc( GetProcessHeap(), 0, icList) ;
269         if (ERROR_SUCCESS != RegQueryValueExA (hkey, "MRUList", NULL, NULL, (LPBYTE)pszList, &icList))
270             MessageBoxA (hCb, "Unable to grab MRUList !", "Nix", MB_OK) ;
271         }
272     else
273         {
274         icList = 1 ;
275         pszList = HeapAlloc( GetProcessHeap(), 0, icList) ;
276         pszList[0] = 0 ;
277         }
278
279     for (Nix = 0 ; Nix < icList - 1 ; Nix++)
280         {
281         if (pszList[Nix] > cMax)
282             cMax = pszList[Nix] ;
283
284         szIndex[0] = pszList[Nix] ;
285
286         if (ERROR_SUCCESS != RegQueryValueExA (hkey, szIndex, NULL, NULL, NULL, &icCmd))
287             MessageBoxA (hCb, "Unable to grab size of index", "Nix", MB_OK) ;
288         if( pszCmd )
289             pszCmd = HeapReAlloc(GetProcessHeap(), 0, pszCmd, icCmd) ;
290         else
291             pszCmd = HeapAlloc(GetProcessHeap(), 0, icCmd) ;
292         if (ERROR_SUCCESS != RegQueryValueExA (hkey, szIndex, NULL, NULL, (LPBYTE)pszCmd, &icCmd))
293             MessageBoxA (hCb, "Unable to grab index", "Nix", MB_OK) ;
294
295         if (NULL != pszLatest)
296             {
297             if (!lstrcmpiA(pszCmd, pszLatest))
298                 {
299                 /*
300                 sprintf (szDbgMsg, "Found existing (%d).\n", Nix) ;
301                 MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
302                 */
303                 SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszCmd) ;
304                 SetWindowTextA (hCb, pszCmd) ;
305                 SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
306
307                 cMatch = pszList[Nix] ;
308                 memmove (&pszList[1], pszList, Nix) ;
309                 pszList[0] = cMatch ;
310                 continue ;
311                 }
312             }
313
314         if (26 != icList - 1 || icList - 2 != Nix || cMatch || NULL == pszLatest)
315             {
316             /*
317             sprintf (szDbgMsg, "Happily appending (%d).\n", Nix) ;
318             MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
319             */
320             SendMessageA (hCb, CB_ADDSTRING, 0, (LPARAM)pszCmd) ;
321             if (!Nix && fShowDefault)
322                 {
323                 SetWindowTextA (hCb, pszCmd) ;
324                 SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
325                 }
326
327             }
328         else
329             {
330             /*
331             sprintf (szDbgMsg, "Doing loop thing.\n") ;
332             MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
333             */
334             SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest) ;
335             SetWindowTextA (hCb, pszLatest) ;
336             SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
337
338             cMatch = pszList[Nix] ;
339             memmove (&pszList[1], pszList, Nix) ;
340             pszList[0] = cMatch ;
341             szIndex[0] = cMatch ;
342             RegSetValueExA (hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, strlen (pszLatest) + 1) ;
343             }
344         }
345
346     if (!cMatch && NULL != pszLatest)
347         {
348         /*
349         sprintf (szDbgMsg, "Simply inserting (increasing list).\n") ;
350         MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
351         */
352         SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest) ;
353         SetWindowTextA (hCb, pszLatest) ;
354         SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
355
356         cMatch = ++cMax ;
357         if( pszList )
358             pszList = HeapReAlloc(GetProcessHeap(), 0, pszList, ++icList) ;
359         else
360             pszList = HeapAlloc(GetProcessHeap(), 0, ++icList) ;
361         memmove (&pszList[1], pszList, icList - 1) ;
362         pszList[0] = cMatch ;
363         szIndex[0] = cMatch ;
364         RegSetValueExA (hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, strlen (pszLatest) + 1) ;
365         }
366
367     RegSetValueExA (hkey, "MRUList", 0, REG_SZ, (LPBYTE)pszList, strlen (pszList) + 1) ;
368
369     HeapFree( GetProcessHeap(), 0, pszCmd) ;
370     HeapFree( GetProcessHeap(), 0, pszList) ;
371     }
372
373 /*************************************************************************
374  * RunFileDlgA                                  [internal]
375  *
376  * The ANSI function that is available as ordinal 61 on Windows 9x/Me
377  *
378  * SEE ALSO
379  *   RunFileDlgAW
380  */
381 void WINAPI RunFileDlgA(
382         HWND hwndOwner,
383         HICON hIcon,
384         LPCSTR lpstrDirectory,
385         LPCSTR lpstrTitle,
386         LPCSTR lpstrDescription,
387         UINT uFlags)
388 {
389     WCHAR title[MAX_PATH];       /* longer string wouldn't be visible in the dialog anyway */
390     WCHAR description[MAX_PATH];
391     WCHAR directory[MAX_PATH];
392
393     MultiByteToWideChar(CP_ACP, 0, lpstrTitle, -1, title, MAX_PATH);
394     title[MAX_PATH - 1] = 0;
395     MultiByteToWideChar(CP_ACP, 0, lpstrDescription, -1, description, MAX_PATH);
396     description[MAX_PATH - 1] = 0;
397     if (!MultiByteToWideChar(CP_ACP, 0, lpstrDirectory, -1, directory, MAX_PATH))
398         directory[0] = 0;
399
400     RunFileDlgW(hwndOwner, hIcon,
401         lpstrDirectory ? directory : NULL,
402         lpstrTitle ? title : NULL,
403         lpstrDescription ? description : NULL,
404         uFlags);
405 }
406
407 /*************************************************************************
408  * RunFileDlgAW                                 [SHELL32.61]
409  *
410  * An undocumented way to open the Run File dialog. A documented way is to use
411  * CLSID_Shell, IID_IShellDispatch (as of Wine 1.0, not implemented under Wine)
412  *
413  * Exported by ordinal. ANSI on Windows 9x and Unicode on Windows NT/2000/XP/etc
414  *
415  */
416 void WINAPI RunFileDlgAW(
417         HWND hwndOwner,
418         HICON hIcon,
419         LPCVOID lpstrDirectory,
420         LPCVOID lpstrTitle,
421         LPCVOID lpstrDescription,
422         UINT uFlags)
423 {
424     if (SHELL_OsIsUnicode())
425         RunFileDlgW(hwndOwner, hIcon, lpstrDirectory, lpstrTitle, lpstrDescription, uFlags);
426     else
427         RunFileDlgA(hwndOwner, hIcon, lpstrDirectory, lpstrTitle, lpstrDescription, uFlags);
428 }
429
430
431 /*************************************************************************
432  * ConfirmDialog                                [internal]
433  *
434  * Put up a confirm box, return TRUE if the user confirmed
435  */
436 static BOOL ConfirmDialog(HWND hWndOwner, UINT PromptId, UINT TitleId)
437 {
438   WCHAR Prompt[256];
439   WCHAR Title[256];
440
441   LoadStringW(shell32_hInstance, PromptId, Prompt, sizeof(Prompt) / sizeof(WCHAR));
442   LoadStringW(shell32_hInstance, TitleId, Title, sizeof(Title) / sizeof(WCHAR));
443   return MessageBoxW(hWndOwner, Prompt, Title, MB_YESNO|MB_ICONQUESTION) == IDYES;
444 }
445
446
447 /*************************************************************************
448  * RestartDialogEx                              [SHELL32.730]
449  */
450
451 int WINAPI RestartDialogEx(HWND hWndOwner, LPCWSTR lpwstrReason, DWORD uFlags, DWORD uReason)
452 {
453     TRACE("(%p)\n", hWndOwner);
454
455     /* FIXME: use lpwstrReason */
456     if (ConfirmDialog(hWndOwner, IDS_RESTART_PROMPT, IDS_RESTART_TITLE))
457     {
458         HANDLE hToken;
459         TOKEN_PRIVILEGES npr;
460
461         /* enable the shutdown privilege for the current process */
462         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
463         {
464             LookupPrivilegeValueA(0, "SeShutdownPrivilege", &npr.Privileges[0].Luid);
465             npr.PrivilegeCount = 1;
466             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
467             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
468             CloseHandle(hToken);
469         }
470         ExitWindowsEx(EWX_REBOOT, uReason);
471     }
472
473     return 0;
474 }
475
476
477 /*************************************************************************
478  * RestartDialog                                [SHELL32.59]
479  */
480
481 int WINAPI RestartDialog(HWND hWndOwner, LPCWSTR lpstrReason, DWORD uFlags)
482 {
483     return RestartDialogEx(hWndOwner, lpstrReason, uFlags, 0);
484 }
485
486
487 /*************************************************************************
488  * ExitWindowsDialog                            [SHELL32.60]
489  *
490  * NOTES
491  *     exported by ordinal
492  */
493 void WINAPI ExitWindowsDialog (HWND hWndOwner)
494 {
495     TRACE("(%p)\n", hWndOwner);
496
497     if (ConfirmDialog(hWndOwner, IDS_SHUTDOWN_PROMPT, IDS_SHUTDOWN_TITLE))
498     {
499         HANDLE hToken;
500         TOKEN_PRIVILEGES npr;
501
502         /* enable shutdown privilege for current process */
503         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
504         {
505             LookupPrivilegeValueA(0, "SeShutdownPrivilege", &npr.Privileges[0].Luid);
506             npr.PrivilegeCount = 1;
507             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
508             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
509             CloseHandle(hToken);
510         }
511         ExitWindowsEx(EWX_SHUTDOWN, 0);
512     }
513 }