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