[COMDLG32] Save open/save location by extension (#1514)
[reactos.git] / dll / win32 / comdlg32 / filedlg.c
1 /*
2 * COMMDLG - File Open Dialogs Win95 look and feel
3 *
4 * Copyright 1999 Francois Boisvert
5 * Copyright 1999, 2000 Juergen Schmied
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 *
21 * FIXME: The whole concept of handling unicode is badly broken.
22 * many hook-messages expect a pointer to a
23 * OPENFILENAMEA or W structure. With the current architecture
24 * we would have to convert the beast at every call to a hook.
25 * we have to find a better solution but it would likely cause
26 * a complete rewrite after which we should handle the
27 * OPENFILENAME structure without any converting (jsch).
28 *
29 * FIXME: any hook gets a OPENFILENAMEA structure
30 *
31 * FIXME: CDN_FILEOK is wrong implemented, other CDN_ messages likely too
32 *
33 * FIXME: old style hook messages are not implemented (except FILEOKSTRING)
34 *
35 * FIXME: algorithm for selecting the initial directory is too simple
36 *
37 * FIXME: add to recent docs
38 *
39 * FIXME: flags not implemented: OFN_DONTADDTORECENT,
40 * OFN_NODEREFERENCELINKS, OFN_NOREADONLYRETURN,
41 * OFN_NOTESTFILECREATE, OFN_USEMONIKERS
42 *
43 * FIXME: lCustData for lpfnHook (WM_INITDIALOG)
44 *
45 *
46 */
47
48 #include "config.h"
49 #include "wine/port.h"
50
51 #include <ctype.h>
52 #include <stdlib.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <string.h>
56
57 #define COBJMACROS
58 #define NONAMELESSUNION
59
60 #include "windef.h"
61 #include "winbase.h"
62 #include "winternl.h"
63 #include "winnls.h"
64 #include "wingdi.h"
65 #ifdef __REACTOS__
66 /* RegGetValueW is supported by Win2k3 SP1 but headers need Win Vista */
67 #undef _WIN32_WINNT
68 #define _WIN32_WINNT 0x0600
69 #endif
70 #include "winreg.h"
71 #include "winuser.h"
72 #include "commdlg.h"
73 #include "dlgs.h"
74 #include "cdlg.h"
75 #include "cderr.h"
76 #include "shellapi.h"
77 #include "shlobj.h"
78 #include "filedlgbrowser.h"
79 #include "shlwapi.h"
80
81 #include "wine/unicode.h"
82 #include "wine/debug.h"
83 #include "wine/heap.h"
84
85 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
86
87 #define UNIMPLEMENTED_FLAGS \
88 (OFN_DONTADDTORECENT |\
89 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
90 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
91
92 /***********************************************************************
93 * Data structure and global variables
94 */
95 typedef struct SFolder
96 {
97 int m_iImageIndex; /* Index of picture in image list */
98 HIMAGELIST hImgList;
99 int m_iIndent; /* Indentation index */
100 LPITEMIDLIST pidlItem; /* absolute pidl of the item */
101
102 } SFOLDER,*LPSFOLDER;
103
104 typedef struct tagLookInInfo
105 {
106 int iMaxIndentation;
107 UINT uSelectedItem;
108 } LookInInfos;
109
110
111 /***********************************************************************
112 * Defines and global variables
113 */
114
115 /* Draw item constant */
116 #define XTEXTOFFSET 3
117
118 /* AddItem flags*/
119 #define LISTEND -1
120
121 /* SearchItem methods */
122 #define SEARCH_PIDL 1
123 #define SEARCH_EXP 2
124 #define ITEM_NOTFOUND -1
125
126 /* Undefined windows message sent by CreateViewObject*/
127 #define WM_GETISHELLBROWSER WM_USER+7
128
129 #define TBPLACES_CMDID_PLACE0 0xa064
130 #define TBPLACES_CMDID_PLACE1 0xa065
131 #define TBPLACES_CMDID_PLACE2 0xa066
132 #define TBPLACES_CMDID_PLACE3 0xa067
133 #define TBPLACES_CMDID_PLACE4 0xa068
134
135 /* NOTE
136 * Those macros exist in windowsx.h. However, you can't really use them since
137 * they rely on the UNICODE defines and can't be used inside Wine itself.
138 */
139
140 /* Combo box macros */
141 #define CBGetItemDataPtr(hwnd,iItemId) \
142 SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
143
144 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
145 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
146
147 static const WCHAR LastVisitedMRUW[] =
148 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
149 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
150 'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
151 'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
152 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
153
154 static const WCHAR filedlg_info_propnameW[] = {'F','i','l','e','O','p','e','n','D','l','g','I','n','f','o','s',0};
155
156 FileOpenDlgInfos *get_filedlg_infoptr(HWND hwnd)
157 {
158 return GetPropW(hwnd, filedlg_info_propnameW);
159 }
160
161 static BOOL is_dialog_hooked(const FileOpenDlgInfos *info)
162 {
163 return (info->ofnInfos->Flags & OFN_ENABLEHOOK) && info->ofnInfos->lpfnHook;
164 }
165
166 static BOOL filedialog_is_readonly_hidden(const FileOpenDlgInfos *info)
167 {
168 return (info->ofnInfos->Flags & OFN_HIDEREADONLY) || (info->DlgInfos.dwDlgProp & FODPROP_SAVEDLG);
169 }
170
171 /***********************************************************************
172 * Prototypes
173 */
174
175 /* Internal functions used by the dialog */
176 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
177 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
178 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
179 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
180 static BOOL FILEDLG95_OnOpen(HWND hwnd);
181 static LRESULT FILEDLG95_InitControls(HWND hwnd);
182 static void FILEDLG95_Clean(HWND hwnd);
183
184 /* Functions used by the shell navigation */
185 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
186 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd);
187 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
188 static void FILEDLG95_SHELL_Clean(HWND hwnd);
189
190 /* Functions used by the EDIT box */
191 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
192
193 /* Functions used by the filetype combo box */
194 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
195 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
196 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
197 static void FILEDLG95_FILETYPE_Clean(HWND hwnd);
198
199 /* Functions used by the Look In combo box */
200 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo);
201 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
202 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
203 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
204 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
205 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
206 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
207 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
208 static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
209
210 /* Functions for dealing with the most-recently-used registry keys */
211 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
212 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
213 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
214 #ifdef __REACTOS__
215 static void FILEDLG95_MRU_load_ext(LPWSTR stored_path, size_t cchMax, LPCWSTR defext);
216 static void FILEDLG95_MRU_save_ext(LPCWSTR filename);
217 #endif
218
219 /* Miscellaneous tool functions */
220 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
221 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
222 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl);
223 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
224 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
225 static UINT GetNumSelected( IDataObject *doSelected );
226 static void COMCTL32_ReleaseStgMedium(STGMEDIUM medium);
227
228 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
229 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
230 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
231 static BOOL BrowseSelectedFolder(HWND hwnd);
232
233 static BOOL get_config_key_as_dword(HKEY hkey, const WCHAR *name, DWORD *value)
234 {
235 DWORD type, data, size;
236
237 size = sizeof(data);
238 if (hkey && !RegQueryValueExW(hkey, name, 0, &type, (BYTE *)&data, &size))
239 {
240 *value = data;
241 return TRUE;
242 }
243
244 return FALSE;
245 }
246
247 static BOOL get_config_key_dword(HKEY hkey, const WCHAR *name, DWORD *value)
248 {
249 DWORD type, data, size;
250
251 size = sizeof(data);
252 if (hkey && !RegQueryValueExW(hkey, name, 0, &type, (BYTE *)&data, &size) && type == REG_DWORD)
253 {
254 *value = data;
255 return TRUE;
256 }
257
258 return FALSE;
259 }
260
261 static BOOL get_config_key_string(HKEY hkey, const WCHAR *name, WCHAR **value)
262 {
263 DWORD type, size;
264 WCHAR *str;
265
266 if (hkey && !RegQueryValueExW(hkey, name, 0, &type, NULL, &size))
267 {
268 if (type != REG_SZ && type != REG_EXPAND_SZ)
269 return FALSE;
270 }
271
272 str = heap_alloc(size);
273 if (RegQueryValueExW(hkey, name, 0, &type, (BYTE *)str, &size))
274 {
275 heap_free(str);
276 return FALSE;
277 }
278
279 *value = str;
280 return TRUE;
281 }
282
283 static BOOL is_places_bar_enabled(const FileOpenDlgInfos *fodInfos)
284 {
285 static const WCHAR noplacesbarW[] = {'N','o','P','l','a','c','e','s','B','a','r',0};
286 DWORD value;
287 HKEY hkey;
288
289 if (fodInfos->ofnInfos->lStructSize != sizeof(*fodInfos->ofnInfos) ||
290 (fodInfos->ofnInfos->FlagsEx & OFN_EX_NOPLACESBAR) ||
291 !(fodInfos->ofnInfos->Flags & OFN_EXPLORER))
292 {
293 return FALSE;
294 }
295
296 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32", &hkey))
297 return TRUE;
298
299 value = 0;
300 get_config_key_as_dword(hkey, noplacesbarW, &value);
301 RegCloseKey(hkey);
302 return value == 0;
303 }
304
305 static void filedlg_collect_places_pidls(FileOpenDlgInfos *fodInfos)
306 {
307 static const int default_places[] =
308 {
309 CSIDL_DESKTOP,
310 CSIDL_MYDOCUMENTS,
311 CSIDL_DRIVES,
312 };
313 unsigned int i;
314 HKEY hkey;
315
316 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32\\Placesbar",
317 &hkey))
318 {
319 for (i = 0; i < ARRAY_SIZE(fodInfos->places); i++)
320 {
321 static const WCHAR placeW[] = {'P','l','a','c','e','%','d',0};
322 WCHAR nameW[8];
323 DWORD value;
324 HRESULT hr;
325 WCHAR *str;
326
327 sprintfW(nameW, placeW, i);
328 if (get_config_key_dword(hkey, nameW, &value))
329 {
330 hr = SHGetSpecialFolderLocation(NULL, value, &fodInfos->places[i]);
331 if (FAILED(hr))
332 WARN("Unrecognized special folder %u.\n", value);
333 }
334 else if (get_config_key_string(hkey, nameW, &str))
335 {
336 hr = SHParseDisplayName(str, NULL, &fodInfos->places[i], 0, NULL);
337 if (FAILED(hr))
338 WARN("Failed to parse custom places location, %s.\n", debugstr_w(str));
339 heap_free(str);
340 }
341 }
342
343 /* FIXME: eliminate duplicates. */
344
345 RegCloseKey(hkey);
346 return;
347 }
348
349 for (i = 0; i < ARRAY_SIZE(default_places); i++)
350 SHGetSpecialFolderLocation(NULL, default_places[i], &fodInfos->places[i]);
351 }
352
353 /***********************************************************************
354 * GetFileName95
355 *
356 * Creates an Open common dialog box that lets the user select
357 * the drive, directory, and the name of a file or set of files to open.
358 *
359 * IN : The FileOpenDlgInfos structure associated with the dialog
360 * OUT : TRUE on success
361 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
362 */
363 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
364 {
365 LRESULT lRes;
366 void *template;
367 HRSRC hRes;
368 HANDLE hDlgTmpl = 0;
369 WORD templateid;
370
371 /* test for missing functionality */
372 if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
373 {
374 FIXME("Flags 0x%08x not yet implemented\n",
375 fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
376 }
377
378 /* Create the dialog from a template */
379
380 if (is_places_bar_enabled(fodInfos))
381 templateid = NEWFILEOPENV2ORD;
382 else
383 templateid = NEWFILEOPENORD;
384
385 if (!(hRes = FindResourceW(COMDLG32_hInstance, MAKEINTRESOURCEW(templateid), (LPCWSTR)RT_DIALOG)))
386 {
387 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
388 return FALSE;
389 }
390 if (!(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes )) ||
391 !(template = LockResource( hDlgTmpl )))
392 {
393 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
394 return FALSE;
395 }
396
397 /* msdn: explorer style dialogs permit sizing by default.
398 * The OFN_ENABLESIZING flag is only needed when a hook or
399 * custom template is provided */
400 if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
401 !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
402 fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
403
404 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
405 {
406 fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
407 fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
408 }
409
410 /* old style hook messages */
411 if (is_dialog_hooked(fodInfos))
412 {
413 fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
414 fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
415 fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
416 fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
417 }
418
419 if (fodInfos->unicode)
420 lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
421 template,
422 fodInfos->ofnInfos->hwndOwner,
423 FileOpenDlgProc95,
424 (LPARAM) fodInfos);
425 else
426 lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
427 template,
428 fodInfos->ofnInfos->hwndOwner,
429 FileOpenDlgProc95,
430 (LPARAM) fodInfos);
431 if (fodInfos->ole_initialized)
432 OleUninitialize();
433
434 /* Unable to create the dialog */
435 if( lRes == -1)
436 return FALSE;
437
438 return lRes;
439 }
440
441 static WCHAR *heap_strdupAtoW(const char *str)
442 {
443 WCHAR *ret;
444 INT len;
445
446 if (!str)
447 return NULL;
448
449 len = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
450 ret = heap_alloc(len * sizeof(WCHAR));
451 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
452
453 return ret;
454 }
455
456 static void init_filedlg_infoW(OPENFILENAMEW *ofn, FileOpenDlgInfos *info)
457 {
458 INITCOMMONCONTROLSEX icc;
459
460 /* Initialize ComboBoxEx32 */
461 icc.dwSize = sizeof(icc);
462 icc.dwICC = ICC_USEREX_CLASSES;
463 InitCommonControlsEx(&icc);
464
465 /* Initialize CommDlgExtendedError() */
466 COMDLG32_SetCommDlgExtendedError(0);
467
468 memset(info, 0, sizeof(*info));
469
470 /* Pass in the original ofn */
471 info->ofnInfos = ofn;
472
473 info->title = ofn->lpstrTitle;
474 info->defext = ofn->lpstrDefExt;
475 info->filter = ofn->lpstrFilter;
476 info->customfilter = ofn->lpstrCustomFilter;
477
478 if (ofn->lpstrFile)
479 {
480 info->filename = heap_alloc(ofn->nMaxFile * sizeof(WCHAR));
481 lstrcpynW(info->filename, ofn->lpstrFile, ofn->nMaxFile);
482 }
483
484 if (ofn->lpstrInitialDir)
485 {
486 DWORD len = ExpandEnvironmentStringsW(ofn->lpstrInitialDir, NULL, 0);
487 if (len)
488 {
489 info->initdir = heap_alloc(len * sizeof(WCHAR));
490 ExpandEnvironmentStringsW(ofn->lpstrInitialDir, info->initdir, len);
491 }
492 }
493
494 info->unicode = TRUE;
495 }
496
497 static void init_filedlg_infoA(OPENFILENAMEA *ofn, FileOpenDlgInfos *info)
498 {
499 OPENFILENAMEW ofnW;
500 int len;
501
502 ofnW = *(OPENFILENAMEW *)ofn;
503
504 ofnW.lpstrInitialDir = heap_strdupAtoW(ofn->lpstrInitialDir);
505 ofnW.lpstrDefExt = heap_strdupAtoW(ofn->lpstrDefExt);
506 ofnW.lpstrTitle = heap_strdupAtoW(ofn->lpstrTitle);
507
508 if (ofn->lpstrFile)
509 {
510 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, NULL, 0);
511 ofnW.lpstrFile = heap_alloc(len * sizeof(WCHAR));
512 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, ofnW.lpstrFile, len);
513 ofnW.nMaxFile = len;
514 }
515
516 if (ofn->lpstrFilter)
517 {
518 LPCSTR s;
519 int n;
520
521 /* filter is a list... title\0ext\0......\0\0 */
522 s = ofn->lpstrFilter;
523 while (*s) s = s+strlen(s)+1;
524 s++;
525 n = s - ofn->lpstrFilter;
526 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0);
527 ofnW.lpstrFilter = heap_alloc(len * sizeof(WCHAR));
528 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, (WCHAR *)ofnW.lpstrFilter, len);
529 }
530
531 /* convert lpstrCustomFilter */
532 if (ofn->lpstrCustomFilter)
533 {
534 int n, len;
535 LPCSTR s;
536
537 /* customfilter contains a pair of strings... title\0ext\0 */
538 s = ofn->lpstrCustomFilter;
539 if (*s) s = s+strlen(s)+1;
540 if (*s) s = s+strlen(s)+1;
541 n = s - ofn->lpstrCustomFilter;
542 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0);
543 ofnW.lpstrCustomFilter = heap_alloc(len * sizeof(WCHAR));
544 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, ofnW.lpstrCustomFilter, len);
545 }
546
547 init_filedlg_infoW(&ofnW, info);
548
549 /* fixup A-specific fields */
550 info->ofnInfos = (OPENFILENAMEW *)ofn;
551 info->unicode = FALSE;
552
553 /* free what was duplicated */
554 heap_free((void *)ofnW.lpstrInitialDir);
555 heap_free(ofnW.lpstrFile);
556 }
557
558 /***********************************************************************
559 * GetFileDialog95
560 *
561 * Call GetFileName95 with this structure and clean the memory.
562 */
563 static BOOL GetFileDialog95(FileOpenDlgInfos *info, UINT dlg_type)
564 {
565 WCHAR *current_dir = NULL;
566 unsigned int i;
567 BOOL ret;
568
569 /* save current directory */
570 if (info->ofnInfos->Flags & OFN_NOCHANGEDIR)
571 {
572 current_dir = heap_alloc(MAX_PATH * sizeof(WCHAR));
573 GetCurrentDirectoryW(MAX_PATH, current_dir);
574 }
575
576 switch (dlg_type)
577 {
578 case OPEN_DIALOG:
579 ret = GetFileName95(info);
580 break;
581 case SAVE_DIALOG:
582 info->DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
583 ret = GetFileName95(info);
584 break;
585 default:
586 ret = FALSE;
587 }
588
589 /* set the lpstrFileTitle */
590 if (ret && info->ofnInfos->lpstrFile && info->ofnInfos->lpstrFileTitle)
591 {
592 if (info->unicode)
593 {
594 LPOPENFILENAMEW ofn = info->ofnInfos;
595 WCHAR *file_title = PathFindFileNameW(ofn->lpstrFile);
596 lstrcpynW(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
597 }
598 else
599 {
600 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)info->ofnInfos;
601 char *file_title = PathFindFileNameA(ofn->lpstrFile);
602 lstrcpynA(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
603 }
604 }
605
606 if (current_dir)
607 {
608 SetCurrentDirectoryW(current_dir);
609 heap_free(current_dir);
610 }
611
612 if (!info->unicode)
613 {
614 heap_free((void *)info->defext);
615 heap_free((void *)info->title);
616 heap_free((void *)info->filter);
617 heap_free((void *)info->customfilter);
618 }
619
620 heap_free(info->filename);
621 heap_free(info->initdir);
622
623 for (i = 0; i < ARRAY_SIZE(info->places); i++)
624 ILFree(info->places[i]);
625
626 return ret;
627 }
628
629 /******************************************************************************
630 * COMDLG32_GetDisplayNameOf [internal]
631 *
632 * Helper function to get the display name for a pidl.
633 */
634 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
635 LPSHELLFOLDER psfDesktop;
636 STRRET strret;
637
638 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
639 return FALSE;
640
641 if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
642 IShellFolder_Release(psfDesktop);
643 return FALSE;
644 }
645
646 IShellFolder_Release(psfDesktop);
647 return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
648 }
649
650 /******************************************************************************
651 * COMDLG32_GetCanonicalPath [internal]
652 *
653 * Helper function to get the canonical path.
654 */
655 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
656 LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
657 {
658 WCHAR lpstrTemp[MAX_PATH];
659
660 /* Get the current directory name */
661 if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
662 {
663 /* last fallback */
664 GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
665 }
666 PathAddBackslashW(lpstrPathAndFile);
667
668 TRACE("current directory=%s, file=%s\n", debugstr_w(lpstrPathAndFile), debugstr_w(lpstrFile));
669
670 /* if the user specified a fully qualified path use it */
671 if(PathIsRelativeW(lpstrFile))
672 {
673 lstrcatW(lpstrPathAndFile, lpstrFile);
674 }
675 else
676 {
677 /* does the path have a drive letter? */
678 if (PathGetDriveNumberW(lpstrFile) == -1)
679 lstrcpyW(lpstrPathAndFile+2, lpstrFile);
680 else
681 lstrcpyW(lpstrPathAndFile, lpstrFile);
682 }
683
684 /* resolve "." and ".." */
685 PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
686 lstrcpyW(lpstrPathAndFile, lpstrTemp);
687 TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
688 }
689
690 /***********************************************************************
691 * COMDLG32_SplitFileNames [internal]
692 *
693 * Creates a delimited list of filenames.
694 */
695 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
696 {
697 UINT nStrCharCount = 0; /* index in src buffer */
698 UINT nFileIndex = 0; /* index in dest buffer */
699 UINT nFileCount = 0; /* number of files */
700
701 /* we might get single filename without any '"',
702 * so we need nStrLen + terminating \0 + end-of-list \0 */
703 *lpstrFileList = heap_alloc((nStrLen + 2) * sizeof(WCHAR));
704 *sizeUsed = 0;
705
706 /* build delimited file list from filenames */
707 while ( nStrCharCount <= nStrLen )
708 {
709 if ( lpstrEdit[nStrCharCount]=='"' )
710 {
711 nStrCharCount++;
712 while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
713 {
714 (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
715 nStrCharCount++;
716 }
717 (*lpstrFileList)[nFileIndex++] = 0;
718 nFileCount++;
719 }
720 nStrCharCount++;
721 }
722
723 /* single, unquoted string */
724 if ((nStrLen > 0) && (nFileIndex == 0) )
725 {
726 lstrcpyW(*lpstrFileList, lpstrEdit);
727 nFileIndex = lstrlenW(lpstrEdit) + 1;
728 nFileCount = 1;
729 }
730
731 /* trailing \0 */
732 (*lpstrFileList)[nFileIndex++] = '\0';
733
734 *sizeUsed = nFileIndex;
735 return nFileCount;
736 }
737
738 /***********************************************************************
739 * ArrangeCtrlPositions [internal]
740 *
741 * NOTE: Make sure to add testcases for any changes made here.
742 */
743 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
744 {
745 HWND hwndChild, hwndStc32;
746 RECT rectParent, rectChild, rectStc32;
747 INT help_fixup = 0;
748 int chgx, chgy;
749
750 /* Take into account if open as read only checkbox and help button
751 * are hidden
752 */
753 if (hide_help)
754 {
755 RECT rectHelp, rectCancel;
756 GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
757 GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
758 /* subtract the height of the help button plus the space between
759 * the help button and the cancel button to the height of the dialog
760 */
761 help_fixup = rectHelp.bottom - rectCancel.bottom;
762 }
763
764 /*
765 There are two possibilities to add components to the default file dialog box.
766
767 By default, all the new components are added below the standard dialog box (the else case).
768
769 However, if there is a static text component with the stc32 id, a special case happens.
770 The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
771 in the window and the cx and cy indicate how to size the window.
772 Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
773 of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
774
775 */
776
777 GetClientRect(hwndParentDlg, &rectParent);
778
779 /* when arranging controls we have to use fixed parent size */
780 rectParent.bottom -= help_fixup;
781
782 hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
783 if (hwndStc32)
784 {
785 GetWindowRect(hwndStc32, &rectStc32);
786 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
787
788 /* set the size of the stc32 control according to the size of
789 * client area of the parent dialog
790 */
791 SetWindowPos(hwndStc32, 0,
792 0, 0,
793 rectParent.right, rectParent.bottom,
794 SWP_NOMOVE | SWP_NOZORDER);
795 }
796 else
797 SetRectEmpty(&rectStc32);
798
799 /* this part moves controls of the child dialog */
800 hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
801 while (hwndChild)
802 {
803 if (hwndChild != hwndStc32)
804 {
805 GetWindowRect(hwndChild, &rectChild);
806 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
807
808 /* move only if stc32 exist */
809 if (hwndStc32 && rectChild.left > rectStc32.right)
810 {
811 /* move to the right of visible controls of the parent dialog */
812 rectChild.left += rectParent.right;
813 rectChild.left -= rectStc32.right;
814 }
815 /* move even if stc32 doesn't exist */
816 if (rectChild.top >= rectStc32.bottom)
817 {
818 /* move below visible controls of the parent dialog */
819 rectChild.top += rectParent.bottom;
820 rectChild.top -= rectStc32.bottom - rectStc32.top;
821 }
822
823 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
824 0, 0, SWP_NOSIZE | SWP_NOZORDER);
825 }
826 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
827 }
828
829 /* this part moves controls of the parent dialog */
830 hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
831 while (hwndChild)
832 {
833 if (hwndChild != hwndChildDlg)
834 {
835 GetWindowRect(hwndChild, &rectChild);
836 MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
837
838 /* left,top of stc32 marks the position of controls
839 * from the parent dialog
840 */
841 rectChild.left += rectStc32.left;
842 rectChild.top += rectStc32.top;
843
844 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
845 0, 0, SWP_NOSIZE | SWP_NOZORDER);
846 }
847 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
848 }
849
850 /* calculate the size of the resulting dialog */
851
852 /* here we have to use original parent size */
853 GetClientRect(hwndParentDlg, &rectParent);
854 GetClientRect(hwndChildDlg, &rectChild);
855 TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
856 wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
857
858 if (hwndStc32)
859 {
860 /* width */
861 if (rectParent.right > rectStc32.right - rectStc32.left)
862 chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
863 else
864 chgx = rectChild.right - rectParent.right;
865 /* height */
866 if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
867 chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
868 else
869 /* Unconditionally set new dialog
870 * height to that of the child
871 */
872 chgy = rectChild.bottom - rectParent.bottom;
873 }
874 else
875 {
876 chgx = 0;
877 chgy = rectChild.bottom - help_fixup;
878 }
879 /* set the size of the parent dialog */
880 GetWindowRect(hwndParentDlg, &rectParent);
881 SetWindowPos(hwndParentDlg, 0,
882 0, 0,
883 rectParent.right - rectParent.left + chgx,
884 rectParent.bottom - rectParent.top + chgy,
885 SWP_NOMOVE | SWP_NOZORDER);
886 }
887
888 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
889 {
890 switch(uMsg) {
891 case WM_INITDIALOG:
892 return TRUE;
893 }
894 return FALSE;
895 }
896
897 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
898 {
899 LPCVOID template;
900 HRSRC hRes;
901 HANDLE hDlgTmpl = 0;
902 HWND hChildDlg = 0;
903
904 TRACE("%p, %p\n", fodInfos, hwnd);
905
906 /*
907 * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
908 * structure's hInstance parameter is not a HINSTANCE, but
909 * instead a pointer to a template resource to use.
910 */
911 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
912 {
913 HINSTANCE hinst;
914 if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
915 {
916 hinst = COMDLG32_hInstance;
917 if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
918 {
919 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
920 return NULL;
921 }
922 }
923 else
924 {
925 hinst = fodInfos->ofnInfos->hInstance;
926 if(fodInfos->unicode)
927 {
928 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
929 hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
930 }
931 else
932 {
933 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
934 hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
935 }
936 if (!hRes)
937 {
938 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
939 return NULL;
940 }
941 if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
942 !(template = LockResource( hDlgTmpl )))
943 {
944 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
945 return NULL;
946 }
947 }
948 if (fodInfos->unicode)
949 hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
950 is_dialog_hooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
951 (LPARAM)fodInfos->ofnInfos);
952 else
953 hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
954 is_dialog_hooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
955 (LPARAM)fodInfos->ofnInfos);
956 return hChildDlg;
957 }
958 else if (is_dialog_hooked(fodInfos))
959 {
960 RECT rectHwnd;
961 struct {
962 DLGTEMPLATE tmplate;
963 WORD menu,class,title;
964 } temp;
965 GetClientRect(hwnd,&rectHwnd);
966 temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
967 temp.tmplate.dwExtendedStyle = 0;
968 temp.tmplate.cdit = 0;
969 temp.tmplate.x = 0;
970 temp.tmplate.y = 0;
971 temp.tmplate.cx = 0;
972 temp.tmplate.cy = 0;
973 temp.menu = temp.class = temp.title = 0;
974
975 hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
976 hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
977
978 return hChildDlg;
979 }
980 return NULL;
981 }
982
983 /***********************************************************************
984 * SendCustomDlgNotificationMessage
985 *
986 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
987 */
988
989 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
990 {
991 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwndParentDlg);
992 LRESULT hook_result;
993 OFNOTIFYW ofnNotify;
994
995 TRACE("%p %d\n", hwndParentDlg, uCode);
996
997 if (!fodInfos || !fodInfos->DlgInfos.hwndCustomDlg)
998 return 0;
999
1000 TRACE("CALL NOTIFY for %d\n", uCode);
1001
1002 ofnNotify.hdr.hwndFrom = hwndParentDlg;
1003 ofnNotify.hdr.idFrom = 0;
1004 ofnNotify.hdr.code = uCode;
1005 ofnNotify.lpOFN = fodInfos->ofnInfos;
1006 ofnNotify.pszFile = NULL;
1007
1008 if (fodInfos->unicode)
1009 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
1010 else
1011 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
1012
1013 TRACE("RET NOTIFY retval %#lx\n", hook_result);
1014
1015 return hook_result;
1016 }
1017
1018 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
1019 {
1020 UINT len, total;
1021 WCHAR *p, *buffer;
1022 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1023
1024 TRACE("CDM_GETFILEPATH:\n");
1025
1026 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
1027 return -1;
1028
1029 /* get path and filenames */
1030 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
1031 buffer = heap_alloc( (len + 2 + MAX_PATH) * sizeof(WCHAR) );
1032 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
1033 if (len)
1034 {
1035 p = buffer + strlenW(buffer);
1036 *p++ = '\\';
1037 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
1038 }
1039 if (fodInfos->unicode)
1040 {
1041 total = strlenW( buffer) + 1;
1042 if (result) lstrcpynW( result, buffer, size );
1043 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
1044 }
1045 else
1046 {
1047 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
1048 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
1049 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
1050 }
1051 heap_free( buffer );
1052 return total;
1053 }
1054
1055 /***********************************************************************
1056 * FILEDLG95_HandleCustomDialogMessages
1057 *
1058 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
1059 */
1060 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1061 {
1062 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1063 WCHAR lpstrPath[MAX_PATH];
1064 INT_PTR retval;
1065
1066 if(!fodInfos) return FALSE;
1067
1068 switch(uMsg)
1069 {
1070 case CDM_GETFILEPATH:
1071 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
1072 break;
1073
1074 case CDM_GETFOLDERPATH:
1075 TRACE("CDM_GETFOLDERPATH:\n");
1076 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
1077 if (lParam)
1078 {
1079 if (fodInfos->unicode)
1080 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
1081 else
1082 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
1083 (LPSTR)lParam, (int)wParam, NULL, NULL);
1084 }
1085 retval = lstrlenW(lpstrPath) + 1;
1086 break;
1087
1088 case CDM_GETFOLDERIDLIST:
1089 retval = ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
1090 if (retval <= wParam)
1091 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
1092 break;
1093
1094 case CDM_GETSPEC:
1095 TRACE("CDM_GETSPEC:\n");
1096 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
1097 if (lParam)
1098 {
1099 if (fodInfos->unicode)
1100 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1101 else
1102 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1103 }
1104 break;
1105
1106 case CDM_SETCONTROLTEXT:
1107 TRACE("CDM_SETCONTROLTEXT:\n");
1108 if ( lParam )
1109 {
1110 if( fodInfos->unicode )
1111 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
1112 else
1113 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
1114 }
1115 retval = TRUE;
1116 break;
1117
1118 case CDM_HIDECONTROL:
1119 /* MSDN states that it should fail for not OFN_EXPLORER case */
1120 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1121 {
1122 HWND control = GetDlgItem( hwnd, wParam );
1123 if (control) ShowWindow( control, SW_HIDE );
1124 retval = TRUE;
1125 }
1126 else retval = FALSE;
1127 break;
1128
1129 default:
1130 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1131 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
1132 return FALSE;
1133 }
1134 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
1135 return TRUE;
1136 }
1137
1138 /***********************************************************************
1139 * FILEDLG95_OnWMGetMMI
1140 *
1141 * WM_GETMINMAXINFO message handler for resizable dialogs
1142 */
1143 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
1144 {
1145 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1146 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1147 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1148 {
1149 mmiptr->ptMinTrackSize = fodInfos->initial_size;
1150 }
1151 return TRUE;
1152 }
1153
1154 /***********************************************************************
1155 * FILEDLG95_OnWMSize
1156 *
1157 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1158 *
1159 * FIXME: this could be made more elaborate. Now use a simple scheme
1160 * where the file view is enlarged and the controls are either moved
1161 * vertically or horizontally to get out of the way. Only the "grip"
1162 * is moved in both directions to stay in the corner.
1163 */
1164 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1165 {
1166 RECT rc, rcview;
1167 int chgx, chgy;
1168 HWND ctrl;
1169 HDWP hdwp;
1170 FileOpenDlgInfos *fodInfos;
1171
1172 if( wParam != SIZE_RESTORED) return FALSE;
1173 fodInfos = get_filedlg_infoptr(hwnd);
1174 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1175 /* get the new dialog rectangle */
1176 GetWindowRect( hwnd, &rc);
1177 TRACE("%p, size from %d,%d to %d,%d\n", hwnd, fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1178 rc.right -rc.left, rc.bottom -rc.top);
1179 /* not initialized yet */
1180 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1181 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1182 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1183 return FALSE;
1184 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1185 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1186 fodInfos->sizedlg.cx = rc.right - rc.left;
1187 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1188 /* change the size of the view window */
1189 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1190 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1191 hdwp = BeginDeferWindowPos( 10);
1192 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1193 rcview.right - rcview.left + chgx,
1194 rcview.bottom - rcview.top + chgy,
1195 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1196 /* change position and sizes of the controls */
1197 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1198 {
1199 int ctrlid = GetDlgCtrlID( ctrl);
1200 GetWindowRect( ctrl, &rc);
1201 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1202 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1203 {
1204 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1205 0, 0,
1206 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1207 }
1208 else if( rc.top > rcview.bottom)
1209 {
1210 /* if it was below the shell view
1211 * move to bottom */
1212 switch( ctrlid)
1213 {
1214 /* file name (edit or comboboxex) and file types combo change also width */
1215 case edt1:
1216 case cmb13:
1217 case cmb1:
1218 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1219 rc.right - rc.left + chgx, rc.bottom - rc.top,
1220 SWP_NOACTIVATE | SWP_NOZORDER);
1221 break;
1222 /* then these buttons must move out of the way */
1223 case IDOK:
1224 case IDCANCEL:
1225 case pshHelp:
1226 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1227 0, 0,
1228 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1229 break;
1230 default:
1231 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1232 0, 0,
1233 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1234 }
1235 }
1236 else if( rc.left > rcview.right)
1237 {
1238 /* if it was to the right of the shell view
1239 * move to right */
1240 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1241 0, 0,
1242 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1243 }
1244 else
1245 /* special cases */
1246 {
1247 switch( ctrlid)
1248 {
1249 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1250 case IDC_LOOKIN:
1251 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1252 rc.right - rc.left + chgx, rc.bottom - rc.top,
1253 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1254 break;
1255 case IDC_TOOLBARSTATIC:
1256 case IDC_TOOLBAR:
1257 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1258 0, 0,
1259 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1260 break;
1261 #endif
1262 /* not resized in windows. Since wine uses this invisible control
1263 * to size the browser view it needs to be resized */
1264 case IDC_SHELLSTATIC:
1265 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1266 rc.right - rc.left + chgx,
1267 rc.bottom - rc.top + chgy,
1268 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1269 break;
1270 case IDC_TOOLBARPLACES:
1271 DeferWindowPos( hdwp, ctrl, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top + chgy,
1272 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1273 break;
1274 }
1275 }
1276 }
1277 if(fodInfos->DlgInfos.hwndCustomDlg &&
1278 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1279 {
1280 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1281 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1282 {
1283 GetWindowRect( ctrl, &rc);
1284 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1285 if( rc.top > rcview.bottom)
1286 {
1287 /* if it was below the shell view
1288 * move to bottom */
1289 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1290 rc.right - rc.left, rc.bottom - rc.top,
1291 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1292 }
1293 else if( rc.left > rcview.right)
1294 {
1295 /* if it was to the right of the shell view
1296 * move to right */
1297 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1298 rc.right - rc.left, rc.bottom - rc.top,
1299 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1300 }
1301 }
1302 /* size the custom dialog at the end: some applications do some
1303 * control re-arranging at this point */
1304 GetClientRect(hwnd, &rc);
1305 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1306 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1307 }
1308 EndDeferWindowPos( hdwp);
1309 /* should not be needed */
1310 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1311 return TRUE;
1312 }
1313
1314 /***********************************************************************
1315 * FileOpenDlgProc95
1316 *
1317 * File open dialog procedure
1318 */
1319 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1320 {
1321 #if 0
1322 TRACE("%p 0x%04x\n", hwnd, uMsg);
1323 #endif
1324
1325 switch(uMsg)
1326 {
1327 case WM_INITDIALOG:
1328 {
1329 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1330 RECT rc, rcstc;
1331 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1332 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1333
1334 /* Some shell namespace extensions depend on COM being initialized. */
1335 if (SUCCEEDED(OleInitialize(NULL)))
1336 fodInfos->ole_initialized = TRUE;
1337
1338 SetPropW(hwnd, filedlg_info_propnameW, fodInfos);
1339
1340 FILEDLG95_InitControls(hwnd);
1341
1342 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1343 {
1344 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1345 DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1346 RECT client, client_adjusted;
1347
1348 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1349 {
1350 style |= WS_SIZEBOX;
1351 ex_style |= WS_EX_WINDOWEDGE;
1352 }
1353 else
1354 style &= ~WS_SIZEBOX;
1355 SetWindowLongW(hwnd, GWL_STYLE, style);
1356 SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1357
1358 GetClientRect( hwnd, &client );
1359 GetClientRect( hwnd, &client_adjusted );
1360 AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1361
1362 GetWindowRect( hwnd, &rc );
1363 rc.right += client_adjusted.right - client.right;
1364 rc.bottom += client_adjusted.bottom - client.bottom;
1365 SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1366 SWP_NOZORDER | SWP_NOMOVE);
1367
1368 GetWindowRect( hwnd, &rc );
1369 fodInfos->DlgInfos.hwndGrip =
1370 CreateWindowExA( 0, "SCROLLBAR", NULL,
1371 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1372 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1373 rc.right - gripx, rc.bottom - gripy,
1374 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1375 }
1376
1377 fodInfos->DlgInfos.hwndCustomDlg =
1378 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1379
1380 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1381 FILEDLG95_FillControls(hwnd, wParam, lParam);
1382
1383 if( fodInfos->DlgInfos.hwndCustomDlg)
1384 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1385
1386 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1387 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1388 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1389 }
1390
1391 /* if the app has changed the position of the invisible listbox,
1392 * change that of the listview (browser) as well */
1393 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1394 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1395 if( !EqualRect( &rc, &rcstc))
1396 {
1397 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1398 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1399 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1400 SWP_NOACTIVATE | SWP_NOZORDER);
1401 }
1402
1403 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1404 {
1405 GetWindowRect( hwnd, &rc);
1406 fodInfos->sizedlg.cx = rc.right - rc.left;
1407 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1408 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1409 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1410 GetClientRect( hwnd, &rc);
1411 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1412 rc.right - gripx, rc.bottom - gripy,
1413 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1414 /* resize the dialog to the previous invocation */
1415 if( MemDialogSize.cx && MemDialogSize.cy)
1416 SetWindowPos( hwnd, NULL,
1417 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1418 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1419 }
1420
1421 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1422 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1423
1424 return 0;
1425 }
1426 case WM_SIZE:
1427 return FILEDLG95_OnWMSize(hwnd, wParam);
1428 case WM_GETMINMAXINFO:
1429 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1430 case WM_COMMAND:
1431 return FILEDLG95_OnWMCommand(hwnd, wParam);
1432 case WM_DRAWITEM:
1433 {
1434 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1435 {
1436 case IDC_LOOKIN:
1437 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1438 return TRUE;
1439 }
1440 }
1441 return FALSE;
1442
1443 case WM_GETISHELLBROWSER:
1444 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1445
1446 case WM_DESTROY:
1447 {
1448 FileOpenDlgInfos * fodInfos = get_filedlg_infoptr(hwnd);
1449 HWND places_bar = GetDlgItem(hwnd, IDC_TOOLBARPLACES);
1450 HIMAGELIST himl;
1451
1452 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1453 MemDialogSize = fodInfos->sizedlg;
1454
1455 if (places_bar)
1456 {
1457 himl = (HIMAGELIST)SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_GETIMAGELIST, 0, 0);
1458 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETIMAGELIST, 0, 0);
1459 ImageList_Destroy(himl);
1460 }
1461 return FALSE;
1462 }
1463
1464 case WM_NCDESTROY:
1465 RemovePropW(hwnd, filedlg_info_propnameW);
1466 return 0;
1467
1468 case WM_NOTIFY:
1469 {
1470 LPNMHDR lpnmh = (LPNMHDR)lParam;
1471 UINT stringId = -1;
1472
1473 /* set up the button tooltips strings */
1474 if(TTN_GETDISPINFOA == lpnmh->code )
1475 {
1476 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1477 switch(lpnmh->idFrom )
1478 {
1479 /* Up folder button */
1480 case FCIDM_TB_UPFOLDER:
1481 stringId = IDS_UPFOLDER;
1482 break;
1483 /* New folder button */
1484 case FCIDM_TB_NEWFOLDER:
1485 stringId = IDS_NEWFOLDER;
1486 break;
1487 /* List option button */
1488 case FCIDM_TB_SMALLICON:
1489 stringId = IDS_LISTVIEW;
1490 break;
1491 /* Details option button */
1492 case FCIDM_TB_REPORTVIEW:
1493 stringId = IDS_REPORTVIEW;
1494 break;
1495 /* Desktop button */
1496 case FCIDM_TB_DESKTOP:
1497 stringId = IDS_TODESKTOP;
1498 break;
1499 default:
1500 stringId = 0;
1501 }
1502 lpdi->hinst = COMDLG32_hInstance;
1503 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1504 }
1505 return FALSE;
1506 }
1507 default :
1508 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1509 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1510 return FALSE;
1511 }
1512 }
1513
1514 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1515 {
1516 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1517 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1518 }
1519
1520 /***********************************************************************
1521 * FILEDLG95_InitControls
1522 *
1523 * WM_INITDIALOG message handler (before hook notification)
1524 */
1525 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1526 {
1527 BOOL win2000plus = FALSE;
1528 BOOL win98plus = FALSE;
1529 BOOL handledPath = FALSE;
1530 OSVERSIONINFOW osVi;
1531 static const WCHAR szwSlash[] = { '\\', 0 };
1532 static const WCHAR szwStar[] = { '*',0 };
1533
1534 static const TBBUTTON tbb[] =
1535 {
1536 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1537 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1538 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1539 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1540 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1541 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1542 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1543 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1544 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1545 };
1546 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1547
1548 RECT rectTB;
1549 RECT rectlook;
1550
1551 HIMAGELIST toolbarImageList;
1552 ITEMIDLIST *desktopPidl;
1553 SHFILEINFOW fileinfo;
1554
1555 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1556
1557 TRACE("%p\n", fodInfos);
1558
1559 /* Get windows version emulating */
1560 osVi.dwOSVersionInfoSize = sizeof(osVi);
1561 GetVersionExW(&osVi);
1562 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1563 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1564 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1565 win2000plus = (osVi.dwMajorVersion > 4);
1566 if (win2000plus) win98plus = TRUE;
1567 }
1568 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1569
1570
1571 /* Use either the edit or the comboboxex for the filename control */
1572 if (filename_is_edit( fodInfos ))
1573 {
1574 DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1575 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1576 }
1577 else
1578 {
1579 DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1580 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1581 }
1582
1583 /* Get the hwnd of the controls */
1584 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1585 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1586
1587 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1588 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1589
1590 /* construct the toolbar */
1591 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1592 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1593
1594 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1595 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1596 rectTB.left = rectlook.right;
1597 rectTB.top = rectlook.top-1;
1598
1599 if (fodInfos->unicode)
1600 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1601 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NORESIZE,
1602 rectTB.left, rectTB.top,
1603 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1604 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1605 else
1606 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1607 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NORESIZE,
1608 rectTB.left, rectTB.top,
1609 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1610 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1611
1612 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1613
1614 /* FIXME: use TB_LOADIMAGES when implemented */
1615 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1616 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1617 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1618
1619 /* Retrieve and add desktop icon to the toolbar */
1620 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1621 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1622 SHGetFileInfoW((const WCHAR *)desktopPidl, 0, &fileinfo, sizeof(fileinfo),
1623 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1624 ImageList_AddIcon(toolbarImageList, fileinfo.hIcon);
1625
1626 DestroyIcon(fileinfo.hIcon);
1627 CoTaskMemFree(desktopPidl);
1628
1629 /* Finish Toolbar Construction */
1630 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1631 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1632
1633 if (is_places_bar_enabled(fodInfos))
1634 {
1635 TBBUTTON tb = { 0 };
1636 HIMAGELIST himl;
1637 RECT rect;
1638 int i, cx;
1639
1640 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_BUTTONSTRUCTSIZE, 0, 0);
1641 GetClientRect(GetDlgItem(hwnd, IDC_TOOLBARPLACES), &rect);
1642 cx = rect.right - rect.left;
1643
1644 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETBUTTONWIDTH, 0, MAKELPARAM(cx, cx));
1645 himl = ImageList_Create(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), ILC_COLOR32, 4, 1);
1646
1647 filedlg_collect_places_pidls(fodInfos);
1648 for (i = 0; i < ARRAY_SIZE(fodInfos->places); i++)
1649 {
1650 int index;
1651
1652 if (!fodInfos->places[i])
1653 continue;
1654
1655 memset(&fileinfo, 0, sizeof(fileinfo));
1656 SHGetFileInfoW((const WCHAR *)fodInfos->places[i], 0, &fileinfo, sizeof(fileinfo),
1657 SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_ICON);
1658 index = ImageList_AddIcon(himl, fileinfo.hIcon);
1659
1660 tb.iBitmap = index;
1661 tb.iString = (INT_PTR)fileinfo.szDisplayName;
1662 tb.fsState = TBSTATE_ENABLED | TBSTATE_WRAP;
1663 tb.idCommand = TBPLACES_CMDID_PLACE0 + i;
1664 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_ADDBUTTONSW, 1, (LPARAM)&tb);
1665
1666 DestroyIcon(fileinfo.hIcon);
1667 }
1668
1669 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETIMAGELIST, 0, (LPARAM)himl);
1670 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETBUTTONSIZE, 0, MAKELPARAM(cx, cx * 3 / 4));
1671 }
1672
1673 /* Set the window text with the text specified in the OPENFILENAME structure */
1674 if(fodInfos->title)
1675 {
1676 SetWindowTextW(hwnd,fodInfos->title);
1677 }
1678 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1679 {
1680 WCHAR buf[64];
1681 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, ARRAY_SIZE(buf));
1682 SetWindowTextW(hwnd, buf);
1683 }
1684
1685 /* Initialise the file name edit control */
1686 handledPath = FALSE;
1687 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1688
1689 if(fodInfos->filename)
1690 {
1691 /* 1. If win2000 or higher and filename contains a path, use it
1692 in preference over the lpstrInitialDir */
1693 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1694 WCHAR tmpBuf[MAX_PATH];
1695 WCHAR *nameBit;
1696 DWORD result;
1697
1698 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1699 if (result) {
1700
1701 /* nameBit is always shorter than the original filename. It may be NULL
1702 * when the filename contains only a drive name instead of file name */
1703 if (nameBit)
1704 {
1705 lstrcpyW(fodInfos->filename,nameBit);
1706 *nameBit = 0x00;
1707 }
1708 else
1709 *fodInfos->filename = '\0';
1710
1711 heap_free(fodInfos->initdir);
1712 fodInfos->initdir = heap_alloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1713 lstrcpyW(fodInfos->initdir, tmpBuf);
1714 handledPath = TRUE;
1715 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1716 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1717 }
1718 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1719
1720 } else {
1721 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1722 }
1723 }
1724
1725 /* 2. (All platforms) If initdir is not null, then use it */
1726 if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1727 {
1728 /* Work out the proper path as supplied one might be relative */
1729 /* (Here because supplying '.' as dir browses to My Computer) */
1730 WCHAR tmpBuf[MAX_PATH];
1731 WCHAR tmpBuf2[MAX_PATH];
1732 WCHAR *nameBit;
1733 DWORD result;
1734
1735 lstrcpyW(tmpBuf, fodInfos->initdir);
1736 if (PathFileExistsW(tmpBuf)) {
1737 /* initdir does not have to be a directory. If a file is
1738 * specified, the dir part is taken */
1739 if (PathIsDirectoryW(tmpBuf)) {
1740 PathAddBackslashW(tmpBuf);
1741 lstrcatW(tmpBuf, szwStar);
1742 }
1743 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1744 if (result) {
1745 *nameBit = 0x00;
1746 heap_free(fodInfos->initdir);
1747 fodInfos->initdir = heap_alloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1748 lstrcpyW(fodInfos->initdir, tmpBuf2);
1749 handledPath = TRUE;
1750 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1751 }
1752 }
1753 else if (fodInfos->initdir)
1754 {
1755 heap_free(fodInfos->initdir);
1756 fodInfos->initdir = NULL;
1757 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1758 }
1759 }
1760
1761 #ifdef __REACTOS__
1762 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1763 {
1764 /* 2.5. Win2000+: Recently used defext */
1765 if (win2000plus) {
1766 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1767 fodInfos->initdir[0] = '\0';
1768
1769 FILEDLG95_MRU_load_ext(fodInfos->initdir, MAX_PATH, fodInfos->defext);
1770
1771 if (fodInfos->initdir[0] && PathIsDirectoryW(fodInfos->initdir)) {
1772 handledPath = TRUE;
1773 } else {
1774 heap_free(fodInfos->initdir);
1775 fodInfos->initdir = NULL;
1776 }
1777 }
1778 }
1779 #endif
1780
1781 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1782 {
1783 /* 3. All except w2k+: if filename contains a path use it */
1784 if (!win2000plus && fodInfos->filename &&
1785 *fodInfos->filename &&
1786 strpbrkW(fodInfos->filename, szwSlash)) {
1787 WCHAR tmpBuf[MAX_PATH];
1788 WCHAR *nameBit;
1789 DWORD result;
1790
1791 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1792 tmpBuf, &nameBit);
1793 if (result) {
1794 int len;
1795
1796 /* nameBit is always shorter than the original filename */
1797 lstrcpyW(fodInfos->filename, nameBit);
1798 *nameBit = 0x00;
1799
1800 len = lstrlenW(tmpBuf);
1801 heap_free(fodInfos->initdir);
1802 fodInfos->initdir = heap_alloc((len+1)*sizeof(WCHAR));
1803 lstrcpyW(fodInfos->initdir, tmpBuf);
1804
1805 handledPath = TRUE;
1806 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1807 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1808 }
1809 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1810 }
1811
1812 /* 4. Win2000+: Recently used */
1813 if (!handledPath && win2000plus) {
1814 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1815 fodInfos->initdir[0] = '\0';
1816
1817 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1818
1819 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1820 handledPath = TRUE;
1821 }else{
1822 heap_free(fodInfos->initdir);
1823 fodInfos->initdir = NULL;
1824 }
1825 }
1826
1827 /* 5. win98+ and win2000+ if any files of specified filter types in
1828 current directory, use it */
1829 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1830
1831 LPCWSTR lpstrPos = fodInfos->filter;
1832 WIN32_FIND_DATAW FindFileData;
1833 HANDLE hFind;
1834
1835 while (1)
1836 {
1837 /* filter is a list... title\0ext\0......\0\0 */
1838
1839 /* Skip the title */
1840 if(! *lpstrPos) break; /* end */
1841 lpstrPos += lstrlenW(lpstrPos) + 1;
1842
1843 /* See if any files exist in the current dir with this extension */
1844 if(! *lpstrPos) break; /* end */
1845
1846 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1847
1848 if (hFind == INVALID_HANDLE_VALUE) {
1849 /* None found - continue search */
1850 lpstrPos += lstrlenW(lpstrPos) + 1;
1851
1852 } else {
1853
1854 heap_free(fodInfos->initdir);
1855 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1856 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1857
1858 handledPath = TRUE;
1859 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1860 debugstr_w(lpstrPos));
1861 FindClose(hFind);
1862 break;
1863 }
1864 }
1865 }
1866
1867 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1868 if (!handledPath && (win2000plus || win98plus)) {
1869 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1870
1871 if (SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir) == S_OK)
1872 {
1873 if (SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir) == S_OK)
1874 {
1875 /* last fallback */
1876 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1877 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1878 }
1879 else
1880 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1881 }
1882 else
1883 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1884
1885 handledPath = TRUE;
1886 } else if (!handledPath) {
1887 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1888 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1889 handledPath = TRUE;
1890 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1891 }
1892 }
1893 SetFocus( fodInfos->DlgInfos.hwndFileName );
1894 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1895
1896 /* Must the open as read only check box be checked ?*/
1897 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1898 {
1899 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1900 }
1901
1902 /* Must the open as read only check box be hidden? */
1903 if (filedialog_is_readonly_hidden(fodInfos))
1904 {
1905 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1906 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1907 }
1908
1909 /* Must the help button be hidden? */
1910 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1911 {
1912 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1913 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1914 }
1915
1916 /* change Open to Save */
1917 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1918 {
1919 WCHAR buf[16];
1920 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, ARRAY_SIZE(buf));
1921 SetDlgItemTextW(hwnd, IDOK, buf);
1922 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, ARRAY_SIZE(buf));
1923 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1924 }
1925
1926 /* Initialize the filter combo box */
1927 FILEDLG95_FILETYPE_Init(hwnd);
1928
1929 return 0;
1930 }
1931
1932 /***********************************************************************
1933 * FILEDLG95_ResizeControls
1934 *
1935 * WM_INITDIALOG message handler (after hook notification)
1936 */
1937 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1938 {
1939 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1940
1941 if (fodInfos->DlgInfos.hwndCustomDlg)
1942 {
1943 RECT rc;
1944 UINT flags = SWP_NOACTIVATE;
1945
1946 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1947 filedialog_is_readonly_hidden(fodInfos) && !(fodInfos->ofnInfos->Flags & OFN_SHOWHELP));
1948
1949 /* resize the custom dialog to the parent size */
1950 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1951 GetClientRect(hwnd, &rc);
1952 else
1953 {
1954 /* our own fake template is zero sized and doesn't have children, so
1955 * there is no need to resize it. Picasa depends on it.
1956 */
1957 flags |= SWP_NOSIZE;
1958 SetRectEmpty(&rc);
1959 }
1960 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1961 0, 0, rc.right, rc.bottom, flags);
1962 }
1963 else
1964 {
1965 /* Resize the height; if opened as read-only, checkbox and help button are
1966 * hidden and we are not using a custom template nor a customDialog
1967 */
1968 if (filedialog_is_readonly_hidden(fodInfos) &&
1969 (!(fodInfos->ofnInfos->Flags &
1970 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1971 {
1972 RECT rectDlg, rectHelp, rectCancel;
1973 GetWindowRect(hwnd, &rectDlg);
1974 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1975 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1976 /* subtract the height of the help button plus the space between the help
1977 * button and the cancel button to the height of the dialog
1978 */
1979 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1980 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1981 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1982 }
1983 }
1984 return TRUE;
1985 }
1986
1987 /***********************************************************************
1988 * FILEDLG95_FillControls
1989 *
1990 * WM_INITDIALOG message handler (after hook notification)
1991 */
1992 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1993 {
1994 LPITEMIDLIST pidlItemId = NULL;
1995
1996 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1997
1998 TRACE("dir=%s file=%s\n",
1999 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
2000
2001 /* Get the initial directory pidl */
2002
2003 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
2004 {
2005 WCHAR path[MAX_PATH];
2006
2007 GetCurrentDirectoryW(MAX_PATH,path);
2008 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
2009 }
2010
2011 /* Initialise shell objects */
2012 FILEDLG95_SHELL_Init(hwnd);
2013
2014 /* Initialize the Look In combo box */
2015 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
2016
2017 /* Browse to the initial directory */
2018 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
2019
2020 ILFree(pidlItemId);
2021
2022 return TRUE;
2023 }
2024 /***********************************************************************
2025 * FILEDLG95_Clean
2026 *
2027 * Regroups all the cleaning functions of the filedlg
2028 */
2029 void FILEDLG95_Clean(HWND hwnd)
2030 {
2031 FILEDLG95_FILETYPE_Clean(hwnd);
2032 FILEDLG95_LOOKIN_Clean(hwnd);
2033 FILEDLG95_SHELL_Clean(hwnd);
2034 }
2035
2036
2037 /***********************************************************************
2038 * Browse to arbitrary pidl
2039 */
2040 static void filedlg_browse_to_pidl(const FileOpenDlgInfos *info, LPITEMIDLIST pidl)
2041 {
2042 TRACE("%p, %p\n", info->ShellInfos.hwndOwner, pidl);
2043
2044 IShellBrowser_BrowseObject(info->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2045 if (info->ofnInfos->Flags & OFN_EXPLORER)
2046 SendCustomDlgNotificationMessage(info->ShellInfos.hwndOwner, CDN_FOLDERCHANGE);
2047 }
2048
2049 /***********************************************************************
2050 * FILEDLG95_OnWMCommand
2051 *
2052 * WM_COMMAND message handler
2053 */
2054 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
2055 {
2056 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2057 WORD wNotifyCode = HIWORD(wParam); /* notification code */
2058 WORD id = LOWORD(wParam); /* item, control, or accelerator identifier */
2059
2060 switch (id)
2061 {
2062 /* OK button */
2063 case IDOK:
2064 FILEDLG95_OnOpen(hwnd);
2065 break;
2066 /* Cancel button */
2067 case IDCANCEL:
2068 FILEDLG95_Clean(hwnd);
2069 EndDialog(hwnd, FALSE);
2070 break;
2071 /* Filetype combo box */
2072 case IDC_FILETYPE:
2073 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
2074 break;
2075 /* LookIn combo box */
2076 case IDC_LOOKIN:
2077 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
2078 break;
2079
2080 /* --- toolbar --- */
2081 /* Up folder button */
2082 case FCIDM_TB_UPFOLDER:
2083 FILEDLG95_SHELL_UpFolder(hwnd);
2084 break;
2085 /* New folder button */
2086 case FCIDM_TB_NEWFOLDER:
2087 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
2088 break;
2089 /* List option button */
2090 case FCIDM_TB_SMALLICON:
2091 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
2092 break;
2093 /* Details option button */
2094 case FCIDM_TB_REPORTVIEW:
2095 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
2096 break;
2097
2098 case FCIDM_TB_DESKTOP:
2099 {
2100 LPITEMIDLIST pidl;
2101
2102 SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, &pidl);
2103 filedlg_browse_to_pidl(fodInfos, pidl);
2104 ILFree(pidl);
2105 break;
2106 }
2107
2108 /* Places bar */
2109 case TBPLACES_CMDID_PLACE0:
2110 case TBPLACES_CMDID_PLACE1:
2111 case TBPLACES_CMDID_PLACE2:
2112 case TBPLACES_CMDID_PLACE3:
2113 case TBPLACES_CMDID_PLACE4:
2114 filedlg_browse_to_pidl(fodInfos, fodInfos->places[id - TBPLACES_CMDID_PLACE0]);
2115 break;
2116
2117 case edt1:
2118 case cmb13:
2119 break;
2120
2121 }
2122 /* Do not use the listview selection anymore */
2123 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
2124 return 0;
2125 }
2126
2127 /***********************************************************************
2128 * FILEDLG95_OnWMGetIShellBrowser
2129 *
2130 * WM_GETISHELLBROWSER message handler
2131 */
2132 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
2133 {
2134 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2135
2136 TRACE("\n");
2137
2138 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
2139
2140 return TRUE;
2141 }
2142
2143
2144 /***********************************************************************
2145 * FILEDLG95_SendFileOK
2146 *
2147 * Sends the CDN_FILEOK notification if required
2148 *
2149 * RETURNS
2150 * TRUE if the dialog should close
2151 * FALSE if the dialog should not be closed
2152 */
2153 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
2154 {
2155 /* ask the hook if we can close */
2156 if (is_dialog_hooked(fodInfos))
2157 {
2158 LRESULT retval = 0;
2159
2160 TRACE("---\n");
2161 /* First send CDN_FILEOK as MSDN doc says */
2162 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2163 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
2164 if( retval)
2165 {
2166 TRACE("canceled\n");
2167 return FALSE;
2168 }
2169
2170 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
2171 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
2172 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
2173 if( retval)
2174 {
2175 TRACE("canceled\n");
2176 return FALSE;
2177 }
2178 }
2179 return TRUE;
2180 }
2181
2182 /***********************************************************************
2183 * FILEDLG95_OnOpenMultipleFiles
2184 *
2185 * Handles the opening of multiple files.
2186 *
2187 * FIXME
2188 * check destination buffer size
2189 */
2190 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
2191 {
2192 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2193 WCHAR lpstrPathSpec[MAX_PATH] = {0};
2194 UINT nCount, nSizePath;
2195
2196 TRACE("\n");
2197
2198 if(fodInfos->unicode)
2199 {
2200 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2201 ofn->lpstrFile[0] = '\0';
2202 }
2203 else
2204 {
2205 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
2206 ofn->lpstrFile[0] = '\0';
2207 }
2208
2209 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
2210
2211 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2212 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
2213 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
2214 {
2215 LPWSTR lpstrTemp = lpstrFileList;
2216
2217 for ( nCount = 0; nCount < nFileCount; nCount++ )
2218 {
2219 LPITEMIDLIST pidl;
2220
2221 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
2222 if (!pidl)
2223 {
2224 WCHAR lpstrNotFound[100];
2225 WCHAR lpstrMsg[100];
2226 WCHAR tmp[400];
2227 static const WCHAR nl[] = {'\n',0};
2228
2229 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
2230 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
2231
2232 lstrcpyW(tmp, lpstrTemp);
2233 lstrcatW(tmp, nl);
2234 lstrcatW(tmp, lpstrNotFound);
2235 lstrcatW(tmp, nl);
2236 lstrcatW(tmp, lpstrMsg);
2237
2238 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
2239 return FALSE;
2240 }
2241
2242 /* move to the next file in the list of files */
2243 lpstrTemp += lstrlenW(lpstrTemp) + 1;
2244 ILFree(pidl);
2245 }
2246 }
2247
2248 nSizePath = lstrlenW(lpstrPathSpec) + 1;
2249 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2250 {
2251 /* For "oldstyle" dialog the components have to
2252 be separated by blanks (not '\0'!) and short
2253 filenames have to be used! */
2254 FIXME("Components have to be separated by blanks\n");
2255 }
2256 if(fodInfos->unicode)
2257 {
2258 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2259 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2260 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2261 }
2262 else
2263 {
2264 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2265
2266 if (ofn->lpstrFile != NULL)
2267 {
2268 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2269 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2270 if (ofn->nMaxFile > nSizePath)
2271 {
2272 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2273 ofn->lpstrFile + nSizePath,
2274 ofn->nMaxFile - nSizePath, NULL, NULL);
2275 }
2276 }
2277 }
2278
2279 fodInfos->ofnInfos->nFileOffset = nSizePath;
2280 fodInfos->ofnInfos->nFileExtension = 0;
2281
2282 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2283 return FALSE;
2284
2285 /* clean and exit */
2286 FILEDLG95_Clean(hwnd);
2287 return EndDialog(hwnd,TRUE);
2288 }
2289
2290 /* Returns the 'slot name' of the given module_name in the registry's
2291 * most-recently-used list. This will be an ASCII value in the
2292 * range ['a','z'). Returns zero on error.
2293 *
2294 * The slot's value in the registry has the form:
2295 * module_name\0mru_path\0
2296 *
2297 * If stored_path is given, then stored_path will contain the path name
2298 * stored in the registry's MRU list for the given module_name.
2299 *
2300 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2301 * MRU list key for the given module_name.
2302 */
2303 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2304 {
2305 WCHAR mru_list[32], *cur_mru_slot;
2306 BOOL taken[25] = {0};
2307 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2308 HKEY hkey_tmp, *hkey;
2309 LONG ret;
2310
2311 if(hkey_ret)
2312 hkey = hkey_ret;
2313 else
2314 hkey = &hkey_tmp;
2315
2316 if(stored_path)
2317 *stored_path = '\0';
2318
2319 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2320 if(ret){
2321 WARN("Unable to create MRU key: %d\n", ret);
2322 return 0;
2323 }
2324
2325 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2326 (LPBYTE)mru_list, &mru_list_size);
2327 if(ret || key_type != REG_SZ){
2328 if(ret == ERROR_FILE_NOT_FOUND)
2329 return 'a';
2330
2331 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2332 RegCloseKey(*hkey);
2333 return 0;
2334 }
2335
2336 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2337 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2338 DWORD value_data_size = sizeof(value_data);
2339
2340 *value_name = *cur_mru_slot;
2341
2342 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2343 &key_type, (LPBYTE)value_data, &value_data_size);
2344 if(ret || key_type != REG_BINARY){
2345 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2346 continue;
2347 }
2348
2349 if(!strcmpiW(module_name, value_data)){
2350 if(!hkey_ret)
2351 RegCloseKey(*hkey);
2352 if(stored_path)
2353 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2354 return *value_name;
2355 }
2356 }
2357
2358 if(!hkey_ret)
2359 RegCloseKey(*hkey);
2360
2361 /* the module name isn't in the registry, so find the next open slot */
2362 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2363 taken[*cur_mru_slot - 'a'] = TRUE;
2364 for(i = 0; i < 25; ++i){
2365 if(!taken[i])
2366 return i + 'a';
2367 }
2368
2369 /* all slots are taken, so return the last one in MRUList */
2370 --cur_mru_slot;
2371 return *cur_mru_slot;
2372 }
2373
2374 /* save the given filename as most-recently-used path for this module */
2375 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2376 {
2377 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2378 LONG ret;
2379 HKEY hkey;
2380
2381 /* get the current executable's name */
2382 if (!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, ARRAY_SIZE(module_path)))
2383 {
2384 WARN("GotModuleFileName failed: %d\n", GetLastError());
2385 return;
2386 }
2387 module_name = strrchrW(module_path, '\\');
2388 if(!module_name)
2389 module_name = module_path;
2390 else
2391 module_name += 1;
2392
2393 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2394 if(!slot)
2395 return;
2396 *slot_name = slot;
2397
2398 { /* update the slot's info */
2399 WCHAR *path_ends, *final;
2400 DWORD path_len, final_len;
2401
2402 /* use only the path segment of `filename' */
2403 path_ends = strrchrW(filename, '\\');
2404 path_len = path_ends - filename;
2405
2406 final_len = path_len + lstrlenW(module_name) + 2;
2407
2408 final = heap_alloc(final_len * sizeof(WCHAR));
2409 if(!final)
2410 return;
2411 lstrcpyW(final, module_name);
2412 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2413 final[final_len-1] = '\0';
2414
2415 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2416 final_len * sizeof(WCHAR));
2417 if(ret){
2418 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2419 heap_free(final);
2420 RegCloseKey(hkey);
2421 return;
2422 }
2423
2424 heap_free(final);
2425 }
2426
2427 { /* update MRUList value */
2428 WCHAR old_mru_list[32], new_mru_list[32];
2429 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2430 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2431
2432 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2433 (LPBYTE)old_mru_list, &mru_list_size);
2434 if(ret || key_type != REG_SZ){
2435 if(ret == ERROR_FILE_NOT_FOUND){
2436 new_mru_list[0] = slot;
2437 new_mru_list[1] = '\0';
2438 }else{
2439 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2440 RegCloseKey(hkey);
2441 return;
2442 }
2443 }else{
2444 /* copy old list data over so that the new slot is at the start
2445 * of the list */
2446 *new_mru_slot++ = slot;
2447 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2448 if(*old_mru_slot != slot)
2449 *new_mru_slot++ = *old_mru_slot;
2450 }
2451 *new_mru_slot = '\0';
2452 }
2453
2454 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2455 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2456 if(ret){
2457 WARN("Error saving MRUList data: %d\n", ret);
2458 RegCloseKey(hkey);
2459 return;
2460 }
2461 }
2462 }
2463
2464 /* load the most-recently-used path for this module */
2465 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2466 {
2467 WCHAR module_path[MAX_PATH], *module_name;
2468
2469 /* get the current executable's name */
2470 if (!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, ARRAY_SIZE(module_path)))
2471 {
2472 WARN("GotModuleFileName failed: %d\n", GetLastError());
2473 return;
2474 }
2475 module_name = strrchrW(module_path, '\\');
2476 if(!module_name)
2477 module_name = module_path;
2478 else
2479 module_name += 1;
2480
2481 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2482 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2483 }
2484 #ifdef __REACTOS__
2485 static const WCHAR s_subkey[] =
2486 {
2487 'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
2488 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s',
2489 'i','o','n','\\','E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g',
2490 '3','2','\\','O','p','e','n','S','a','v','e','M','R','U',0
2491 };
2492 static const WCHAR s_szAst[] = { '*', 0 };
2493
2494 typedef INT (CALLBACK *MRUStringCmpFnW)(LPCWSTR lhs, LPCWSTR rhs);
2495 typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length);
2496
2497 /* https://docs.microsoft.com/en-us/windows/desktop/shell/mruinfo */
2498 typedef struct tagMRUINFOW
2499 {
2500 DWORD cbSize;
2501 UINT uMax;
2502 UINT fFlags;
2503 HKEY hKey;
2504 LPCWSTR lpszSubKey;
2505 union
2506 {
2507 MRUStringCmpFnW string_cmpfn;
2508 MRUBinaryCmpFn binary_cmpfn;
2509 } u;
2510 } MRUINFOW, *LPMRUINFOW;
2511
2512 /* flags for MRUINFOW.fFlags */
2513 #define MRU_STRING 0x0000
2514 #define MRU_BINARY 0x0001
2515 #define MRU_CACHEWRITE 0x0002
2516
2517 static HINSTANCE s_hComCtl32 = NULL;
2518
2519 /* comctl32.400: CreateMRUListW */
2520 typedef HANDLE (WINAPI *CREATEMRULISTW)(const MRUINFOW *);
2521 static CREATEMRULISTW s_pCreateMRUListW = NULL;
2522
2523 /* comctl32.401: AddMRUStringW */
2524 typedef INT (WINAPI *ADDMRUSTRINGW)(HANDLE, LPCWSTR);
2525 static ADDMRUSTRINGW s_pAddMRUStringW = NULL;
2526
2527 /* comctl32.402: FindMRUStringW */
2528 typedef INT (WINAPI *FINDMRUSTRINGW)(HANDLE, LPCWSTR, LPINT);
2529 static FINDMRUSTRINGW s_pFindMRUStringW = NULL;
2530
2531 /* comctl32.403: EnumMRUListW */
2532 typedef INT (WINAPI *ENUMMRULISTW)(HANDLE, INT, LPVOID, DWORD);
2533 static ENUMMRULISTW s_pEnumMRUListW = NULL;
2534
2535 /* comctl32.152: FreeMRUList */
2536 typedef void (WINAPI *FREEMRULIST)(HANDLE);
2537 static FREEMRULIST s_pFreeMRUList = NULL;
2538
2539 static BOOL FILEDLG_InitMRUList(void)
2540 {
2541 if (s_hComCtl32)
2542 return TRUE;
2543
2544 s_hComCtl32 = GetModuleHandleA("comctl32");
2545 if (!s_hComCtl32)
2546 return FALSE;
2547
2548 s_pCreateMRUListW = (CREATEMRULISTW)GetProcAddress(s_hComCtl32, (LPCSTR)400);
2549 s_pAddMRUStringW = (ADDMRUSTRINGW)GetProcAddress(s_hComCtl32, (LPCSTR)401);
2550 s_pFindMRUStringW = (FINDMRUSTRINGW)GetProcAddress(s_hComCtl32, (LPCSTR)402);
2551 s_pEnumMRUListW = (ENUMMRULISTW)GetProcAddress(s_hComCtl32, (LPCSTR)403);
2552 s_pFreeMRUList = (FREEMRULIST)GetProcAddress(s_hComCtl32, (LPCSTR)152);
2553 if (!s_pCreateMRUListW ||
2554 !s_pAddMRUStringW ||
2555 !s_pFindMRUStringW ||
2556 !s_pEnumMRUListW ||
2557 !s_pFreeMRUList)
2558 {
2559 s_hComCtl32 = NULL;
2560 return FALSE;
2561 }
2562
2563 return TRUE;
2564 }
2565
2566 static BOOL ExtIsPicture(LPCWSTR ext)
2567 {
2568 static const WCHAR s_image_exts[][6] =
2569 {
2570 { 'b','m','p',0 },
2571 { 'd','i','b',0 },
2572 { 'j','p','g',0 },
2573 { 'j','p','e','g',0 },
2574 { 'j','p','e',0 },
2575 { 'j','f','i','f',0 },
2576 { 'p','n','g',0 },
2577 { 'g','i','f',0 },
2578 { 't','i','f',0 },
2579 { 't','i','f','f',0 }
2580 };
2581 size_t i;
2582
2583 for (i = 0; i < ARRAY_SIZE(s_image_exts); ++i)
2584 {
2585 if (lstrcmpiW(ext, s_image_exts[i]) == 0)
2586 {
2587 return TRUE;
2588 }
2589 }
2590 return FALSE;
2591 }
2592
2593 static void FILEDLG95_MRU_load_ext(LPWSTR stored_path, size_t cchMax, LPCWSTR defext)
2594 {
2595 HKEY hOpenSaveMRT = NULL;
2596 LONG result;
2597 MRUINFOW mi;
2598 HANDLE hList;
2599 WCHAR szText[MAX_PATH];
2600 INT ret = 0;
2601
2602 stored_path[0] = 0;
2603
2604 if (!defext || !*defext || !FILEDLG_InitMRUList())
2605 {
2606 return;
2607 }
2608
2609 if (*defext == '.')
2610 ++defext;
2611
2612 result = RegOpenKeyW(HKEY_CURRENT_USER, s_subkey, &hOpenSaveMRT);
2613 if (!result && hOpenSaveMRT)
2614 {
2615 ZeroMemory(&mi, sizeof(mi));
2616 mi.cbSize = sizeof(mi);
2617 mi.uMax = 26;
2618 mi.fFlags = MRU_STRING;
2619 mi.hKey = hOpenSaveMRT;
2620 mi.lpszSubKey = defext;
2621 mi.u.string_cmpfn = lstrcmpiW;
2622 hList = (*s_pCreateMRUListW)(&mi);
2623 if (hList)
2624 {
2625 ret = (*s_pEnumMRUListW)(hList, 0, szText, sizeof(szText));
2626 if (ret > 0)
2627 {
2628 lstrcpynW(stored_path, szText, cchMax);
2629 PathRemoveFileSpecW(stored_path);
2630 }
2631 (*s_pFreeMRUList)(hList);
2632 }
2633
2634 if (stored_path[0] == 0)
2635 {
2636 mi.cbSize = sizeof(mi);
2637 mi.uMax = 26;
2638 mi.fFlags = MRU_STRING;
2639 mi.hKey = hOpenSaveMRT;
2640 mi.lpszSubKey = s_szAst;
2641 mi.u.string_cmpfn = lstrcmpiW;
2642 hList = (*s_pCreateMRUListW)(&mi);
2643 if (hList)
2644 {
2645 ret = (*s_pEnumMRUListW)(hList, 0, szText, sizeof(szText));
2646 if (ret > 0)
2647 {
2648 lstrcpynW(stored_path, szText, cchMax);
2649 PathRemoveFileSpecW(stored_path);
2650 }
2651 (*s_pFreeMRUList)(hList);
2652 }
2653 }
2654
2655 RegCloseKey(hOpenSaveMRT);
2656 }
2657
2658 if (stored_path[0] == 0)
2659 {
2660 LPITEMIDLIST pidl;
2661 if (ExtIsPicture(defext))
2662 {
2663 SHGetSpecialFolderLocation(NULL, CSIDL_MYPICTURES, &pidl);
2664 }
2665 else
2666 {
2667 SHGetSpecialFolderLocation(NULL, CSIDL_MYDOCUMENTS, &pidl);
2668 }
2669 SHGetPathFromIDListW(pidl, stored_path);
2670 ILFree(pidl);
2671 }
2672 }
2673
2674 static void FILEDLG95_MRU_save_ext(LPCWSTR filename)
2675 {
2676 HKEY hOpenSaveMRT = NULL;
2677 LONG result;
2678 MRUINFOW mi;
2679 HANDLE hList;
2680 LPCWSTR defext = PathFindExtensionW(filename);
2681
2682 if (!defext || !*defext || !FILEDLG_InitMRUList())
2683 {
2684 return;
2685 }
2686
2687 if (*defext == '.')
2688 ++defext;
2689
2690 result = RegOpenKeyW(HKEY_CURRENT_USER, s_subkey, &hOpenSaveMRT);
2691 if (!result && hOpenSaveMRT)
2692 {
2693 ZeroMemory(&mi, sizeof(mi));
2694 mi.cbSize = sizeof(mi);
2695 mi.uMax = 26;
2696 mi.fFlags = MRU_STRING;
2697 mi.hKey = hOpenSaveMRT;
2698 mi.lpszSubKey = defext;
2699 mi.u.string_cmpfn = lstrcmpiW;
2700 hList = (*s_pCreateMRUListW)(&mi);
2701 if (hList)
2702 {
2703 (*s_pAddMRUStringW)(hList, filename);
2704 (*s_pFreeMRUList)(hList);
2705 }
2706
2707 mi.cbSize = sizeof(mi);
2708 mi.uMax = 26;
2709 mi.fFlags = MRU_STRING;
2710 mi.hKey = hOpenSaveMRT;
2711 mi.lpszSubKey = s_szAst;
2712 mi.u.string_cmpfn = lstrcmpiW;
2713 hList = (*s_pCreateMRUListW)(&mi);
2714 if (hList)
2715 {
2716 (*s_pAddMRUStringW)(hList, filename);
2717 (*s_pFreeMRUList)(hList);
2718 }
2719
2720 RegCloseKey(hOpenSaveMRT);
2721 }
2722 }
2723 #endif
2724
2725 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2726 {
2727 WCHAR strMsgTitle[MAX_PATH];
2728 WCHAR strMsgText [MAX_PATH];
2729 if (idCaption)
2730 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, ARRAY_SIZE(strMsgTitle));
2731 else
2732 strMsgTitle[0] = '\0';
2733 LoadStringW(COMDLG32_hInstance, idText, strMsgText, ARRAY_SIZE(strMsgText));
2734 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2735 }
2736
2737 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2738 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2739 {
2740 int nOpenAction = defAction;
2741 LPWSTR lpszTemp, lpszTemp1;
2742 LPITEMIDLIST pidl = NULL;
2743 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2744
2745 /* check for invalid chars */
2746 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2747 {
2748 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2749 return FALSE;
2750 }
2751
2752 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2753
2754 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2755 while (lpszTemp1)
2756 {
2757 LPSHELLFOLDER lpsfChild;
2758 WCHAR lpwstrTemp[MAX_PATH];
2759 DWORD dwEaten, dwAttributes;
2760 LPWSTR p;
2761
2762 lstrcpyW(lpwstrTemp, lpszTemp);
2763 p = PathFindNextComponentW(lpwstrTemp);
2764
2765 if (!p) break; /* end of path */
2766
2767 *p = 0;
2768 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2769
2770 /* There are no wildcards when OFN_NOVALIDATE is set */
2771 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2772 {
2773 static const WCHAR wszWild[] = { '*', '?', 0 };
2774 /* if the last element is a wildcard do a search */
2775 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2776 {
2777 nOpenAction = ONOPEN_SEARCH;
2778 break;
2779 }
2780 }
2781 lpszTemp1 = lpszTemp;
2782
2783 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2784
2785 /* append a backslash to drive letters */
2786 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2787 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2788 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2789 {
2790 PathAddBackslashW(lpwstrTemp);
2791 }
2792
2793 dwAttributes = SFGAO_FOLDER;
2794 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2795 {
2796 /* the path component is valid, we have a pidl of the next path component */
2797 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2798 if(dwAttributes & SFGAO_FOLDER)
2799 {
2800 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2801 {
2802 ERR("bind to failed\n"); /* should not fail */
2803 break;
2804 }
2805 IShellFolder_Release(*ppsf);
2806 *ppsf = lpsfChild;
2807 lpsfChild = NULL;
2808 }
2809 else
2810 {
2811 TRACE("value\n");
2812
2813 /* end dialog, return value */
2814 nOpenAction = ONOPEN_OPEN;
2815 break;
2816 }
2817 ILFree(pidl);
2818 pidl = NULL;
2819 }
2820 else if (!(flags & OFN_NOVALIDATE))
2821 {
2822 if(*lpszTemp || /* points to trailing null for last path element */
2823 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2824 {
2825 if(flags & OFN_PATHMUSTEXIST)
2826 {
2827 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2828 break;
2829 }
2830 }
2831 else
2832 {
2833 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2834 {
2835 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2836 break;
2837 }
2838 }
2839 /* change to the current folder */
2840 nOpenAction = ONOPEN_OPEN;
2841 break;
2842 }
2843 else
2844 {
2845 nOpenAction = ONOPEN_OPEN;
2846 break;
2847 }
2848 }
2849 ILFree(pidl);
2850
2851 return nOpenAction;
2852 }
2853
2854 /***********************************************************************
2855 * FILEDLG95_OnOpen
2856 *
2857 * Ok button WM_COMMAND message handler
2858 *
2859 * If the function succeeds, the return value is nonzero.
2860 */
2861 BOOL FILEDLG95_OnOpen(HWND hwnd)
2862 {
2863 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2864 LPWSTR lpstrFileList;
2865 UINT nFileCount = 0;
2866 UINT sizeUsed = 0;
2867 BOOL ret = TRUE;
2868 WCHAR lpstrPathAndFile[MAX_PATH];
2869 LPSHELLFOLDER lpsf = NULL;
2870 int nOpenAction;
2871
2872 TRACE("hwnd=%p\n", hwnd);
2873
2874 /* try to browse the selected item */
2875 if(BrowseSelectedFolder(hwnd))
2876 return FALSE;
2877
2878 /* get the files from the edit control */
2879 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2880
2881 if(nFileCount == 0)
2882 return FALSE;
2883
2884 if(nFileCount > 1)
2885 {
2886 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2887 goto ret;
2888 }
2889
2890 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2891
2892 /*
2893 Step 1: Build a complete path name from the current folder and
2894 the filename or path in the edit box.
2895 Special cases:
2896 - the path in the edit box is a root path
2897 (with or without drive letter)
2898 - the edit box contains ".." (or a path with ".." in it)
2899 */
2900
2901 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2902 heap_free(lpstrFileList);
2903
2904 /*
2905 Step 2: here we have a cleaned up path
2906
2907 We have to parse the path step by step to see if we have to browse
2908 to a folder if the path points to a directory or the last
2909 valid element is a directory.
2910
2911 valid variables:
2912 lpstrPathAndFile: cleaned up path
2913 */
2914
2915 if (nFileCount &&
2916 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2917 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2918 nOpenAction = ONOPEN_OPEN;
2919 else
2920 nOpenAction = ONOPEN_BROWSE;
2921
2922 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2923 fodInfos->ofnInfos->Flags,
2924 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2925 nOpenAction);
2926 if(!nOpenAction)
2927 goto ret;
2928
2929 /*
2930 Step 3: here we have a cleaned up and validated path
2931
2932 valid variables:
2933 lpsf: ShellFolder bound to the rightmost valid path component
2934 lpstrPathAndFile: cleaned up path
2935 nOpenAction: action to do
2936 */
2937 TRACE("end validate sf=%p\n", lpsf);
2938
2939 switch(nOpenAction)
2940 {
2941 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2942 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2943 {
2944 int iPos;
2945 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2946 DWORD len;
2947
2948 /* replace the current filter */
2949 heap_free(fodInfos->ShellInfos.lpstrCurrentFilter);
2950 len = lstrlenW(lpszTemp)+1;
2951 fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc(len * sizeof(WCHAR));
2952 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2953
2954 /* set the filter cb to the extension when possible */
2955 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2956 SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETCURSEL, iPos, 0);
2957 }
2958 /* fall through */
2959 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2960 TRACE("ONOPEN_BROWSE\n");
2961 {
2962 IPersistFolder2 * ppf2;
2963 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2964 {
2965 LPITEMIDLIST pidlCurrent;
2966 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2967 IPersistFolder2_Release(ppf2);
2968 if (!ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2969 {
2970 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2971 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2972 {
2973 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2974 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2975 }
2976 }
2977 else if( nOpenAction == ONOPEN_SEARCH )
2978 {
2979 if (fodInfos->Shell.FOIShellView)
2980 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2981 }
2982 ILFree(pidlCurrent);
2983 if (filename_is_edit( fodInfos ))
2984 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2985 else
2986 {
2987 HWND hwnd;
2988
2989 hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2990 SendMessageW(hwnd, EM_SETSEL, 0, -1);
2991 }
2992 }
2993 }
2994 ret = FALSE;
2995 break;
2996 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2997 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2998 {
2999 WCHAR *ext = NULL;
3000
3001 /* update READONLY check box flag */
3002 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
3003 fodInfos->ofnInfos->Flags |= OFN_READONLY;
3004 else
3005 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
3006
3007 /* Attach the file extension with file name*/
3008 ext = PathFindExtensionW(lpstrPathAndFile);
3009 if (! *ext && fodInfos->defext)
3010 {
3011 /* if no extension is specified with file name, then */
3012 /* attach the extension from file filter or default one */
3013
3014 WCHAR *filterExt = NULL;
3015 LPWSTR lpstrFilter = NULL;
3016 static const WCHAR szwDot[] = {'.',0};
3017 int PathLength = lstrlenW(lpstrPathAndFile);
3018
3019 /*Get the file extension from file type filter*/
3020 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3021 fodInfos->ofnInfos->nFilterIndex-1);
3022
3023 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
3024 {
3025 WCHAR* filterSearchIndex;
3026 filterExt = heap_alloc((lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
3027 strcpyW(filterExt, lpstrFilter);
3028
3029 /* if a semicolon-separated list of file extensions was given, do not include the
3030 semicolon or anything after it in the extension.
3031 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
3032 filterSearchIndex = strchrW(filterExt, ';');
3033 if (filterSearchIndex)
3034 {
3035 filterSearchIndex[0] = '\0';
3036 }
3037
3038 /* find the file extension by searching for the first dot in filterExt */
3039 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
3040 /* if the extension is invalid or contains a glob, ignore it */
3041 filterSearchIndex = strchrW(filterExt, '.');
3042 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
3043 {
3044 strcpyW(filterExt, filterSearchIndex);
3045 }
3046 else
3047 {
3048 heap_free(filterExt);
3049 filterExt = NULL;
3050 }
3051 }
3052
3053 if (!filterExt)
3054 {
3055 /* use the default file extension */
3056 filterExt = heap_alloc((lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
3057 strcpyW(filterExt, fodInfos->defext);
3058 }
3059
3060 if (*filterExt) /* ignore filterExt="" */
3061 {
3062 /* Attach the dot*/
3063 lstrcatW(lpstrPathAndFile, szwDot);
3064 /* Attach the extension */
3065 lstrcatW(lpstrPathAndFile, filterExt);
3066 }
3067
3068 heap_free(filterExt);
3069
3070 /* In Open dialog: if file does not exist try without extension */
3071 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
3072 lpstrPathAndFile[PathLength] = '\0';
3073
3074 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
3075 if (*ext)
3076 ext++;
3077 if (!lstrcmpiW(fodInfos->defext, ext))
3078 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
3079 else
3080 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
3081 }
3082
3083 /* In Save dialog: check if the file already exists */
3084 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
3085 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
3086 && PathFileExistsW(lpstrPathAndFile))
3087 {
3088 WCHAR lpstrOverwrite[100];
3089 int answer;
3090
3091 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
3092 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
3093 MB_YESNO | MB_ICONEXCLAMATION);
3094 if (answer == IDNO || answer == IDCANCEL)
3095 {
3096 ret = FALSE;
3097 goto ret;
3098 }
3099 }
3100
3101 /* In Open dialog: check if it should be created if it doesn't exist */
3102 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
3103 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
3104 && !PathFileExistsW(lpstrPathAndFile))
3105 {
3106 WCHAR lpstrCreate[100];
3107 int answer;
3108
3109 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);