Release 1.6-rc2.
[wine/testsucceed.git] / dlls / comdlg32 / filedlg.c
blobc08e8d4ae7615e65c915b12e69d2d9710527e619
1 /*
2 * COMMDLG - File Open Dialogs Win95 look and feel
4 * Copyright 1999 Francois Boisvert
5 * Copyright 1999, 2000 Juergen Schmied
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.
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.
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
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).
29 * FIXME: any hook gets a OPENFILENAMEA structure
31 * FIXME: CDN_FILEOK is wrong implemented, other CDN_ messages likely too
33 * FIXME: old style hook messages are not implemented (except FILEOKSTRING)
35 * FIXME: algorithm for selecting the initial directory is too simple
37 * FIXME: add to recent docs
39 * FIXME: flags not implemented: OFN_DONTADDTORECENT,
40 * OFN_NODEREFERENCELINKS, OFN_NOREADONLYRETURN,
41 * OFN_NOTESTFILECREATE, OFN_USEMONIKERS
43 * FIXME: lCustData for lpfnHook (WM_INITDIALOG)
48 #include "config.h"
49 #include "wine/port.h"
51 #include <ctype.h>
52 #include <stdlib.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <string.h>
57 #define COBJMACROS
58 #define NONAMELESSUNION
59 #define NONAMELESSSTRUCT
61 #include "windef.h"
62 #include "winbase.h"
63 #include "winternl.h"
64 #include "winnls.h"
65 #include "wingdi.h"
66 #include "winreg.h"
67 #include "winuser.h"
68 #include "commdlg.h"
69 #include "dlgs.h"
70 #include "cdlg.h"
71 #include "cderr.h"
72 #include "shellapi.h"
73 #include "shlobj.h"
74 #include "filedlgbrowser.h"
75 #include "shlwapi.h"
77 #include "wine/unicode.h"
78 #include "wine/debug.h"
80 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
82 #define UNIMPLEMENTED_FLAGS \
83 (OFN_DONTADDTORECENT |\
84 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
85 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
87 #define IsHooked(fodInfos) \
88 ((fodInfos->ofnInfos->Flags & OFN_ENABLEHOOK) && fodInfos->ofnInfos->lpfnHook)
89 /***********************************************************************
90 * Data structure and global variables
92 typedef struct SFolder
94 int m_iImageIndex; /* Index of picture in image list */
95 HIMAGELIST hImgList;
96 int m_iIndent; /* Indentation index */
97 LPITEMIDLIST pidlItem; /* absolute pidl of the item */
99 } SFOLDER,*LPSFOLDER;
101 typedef struct tagLookInInfo
103 int iMaxIndentation;
104 UINT uSelectedItem;
105 } LookInInfos;
108 /***********************************************************************
109 * Defines and global variables
112 /* Draw item constant */
113 #define XTEXTOFFSET 3
115 /* AddItem flags*/
116 #define LISTEND -1
118 /* SearchItem methods */
119 #define SEARCH_PIDL 1
120 #define SEARCH_EXP 2
121 #define ITEM_NOTFOUND -1
123 /* Undefined windows message sent by CreateViewObject*/
124 #define WM_GETISHELLBROWSER WM_USER+7
126 /* NOTE
127 * Those macros exist in windowsx.h. However, you can't really use them since
128 * they rely on the UNICODE defines and can't be used inside Wine itself.
131 /* Combo box macros */
132 #define CBAddString(hwnd,str) \
133 SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)(str));
135 #define CBInsertString(hwnd,str,pos) \
136 SendMessageW(hwnd, CB_INSERTSTRING, (WPARAM)(pos), (LPARAM)(str));
138 #define CBDeleteString(hwnd,pos) \
139 SendMessageW(hwnd, CB_DELETESTRING, (WPARAM)(pos), 0);
141 #define CBSetItemDataPtr(hwnd,iItemId,dataPtr) \
142 SendMessageW(hwnd, CB_SETITEMDATA, (WPARAM)(iItemId), (LPARAM)(dataPtr));
144 #define CBGetItemDataPtr(hwnd,iItemId) \
145 SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
147 #define CBGetLBText(hwnd,iItemId,str) \
148 SendMessageW(hwnd, CB_GETLBTEXT, (WPARAM)(iItemId), (LPARAM)(str));
150 #define CBGetCurSel(hwnd) \
151 SendMessageW(hwnd, CB_GETCURSEL, 0, 0);
153 #define CBSetCurSel(hwnd,pos) \
154 SendMessageW(hwnd, CB_SETCURSEL, (WPARAM)(pos), 0);
156 #define CBGetCount(hwnd) \
157 SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
158 #define CBShowDropDown(hwnd,show) \
159 SendMessageW(hwnd, CB_SHOWDROPDOWN, (WPARAM)(show), 0);
160 #define CBSetItemHeight(hwnd,index,height) \
161 SendMessageW(hwnd, CB_SETITEMHEIGHT, (WPARAM)(index), (LPARAM)(height));
163 #define CBSetExtendedUI(hwnd,flag) \
164 SendMessageW(hwnd, CB_SETEXTENDEDUI, (WPARAM)(flag), 0)
166 const char FileOpenDlgInfosStr[] = "FileOpenDlgInfos"; /* windows property description string */
167 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
168 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
170 static const WCHAR LastVisitedMRUW[] =
171 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
172 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
173 'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
174 'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
175 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
177 /***********************************************************************
178 * Prototypes
181 /* Internal functions used by the dialog */
182 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
183 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
184 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
185 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
186 static BOOL FILEDLG95_OnOpen(HWND hwnd);
187 static LRESULT FILEDLG95_InitControls(HWND hwnd);
188 static void FILEDLG95_Clean(HWND hwnd);
190 /* Functions used by the shell navigation */
191 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
192 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd);
193 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
194 static void FILEDLG95_SHELL_Clean(HWND hwnd);
195 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd);
197 /* Functions used by the EDIT box */
198 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
200 /* Functions used by the filetype combo box */
201 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
202 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
203 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
204 static void FILEDLG95_FILETYPE_Clean(HWND hwnd);
206 /* Functions used by the Look In combo box */
207 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo);
208 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
209 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
210 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
211 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
212 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
213 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
214 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
215 static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
217 /* Functions for dealing with the most-recently-used registry keys */
218 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
219 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
220 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
222 /* Miscellaneous tool functions */
223 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
224 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
225 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl);
226 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
227 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
228 static UINT GetNumSelected( IDataObject *doSelected );
230 /* Shell memory allocation */
231 static void *MemAlloc(UINT size);
232 static void MemFree(void *mem);
234 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
235 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
236 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
237 static BOOL BrowseSelectedFolder(HWND hwnd);
239 /***********************************************************************
240 * GetFileName95
242 * Creates an Open common dialog box that lets the user select
243 * the drive, directory, and the name of a file or set of files to open.
245 * IN : The FileOpenDlgInfos structure associated with the dialog
246 * OUT : TRUE on success
247 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
249 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
252 LRESULT lRes;
253 LPCVOID origTemplate;
254 DWORD dwSize;
255 LPDLGTEMPLATEW template;
256 HRSRC hRes;
257 HANDLE hDlgTmpl = 0;
258 HRESULT hr;
260 /* test for missing functionality */
261 if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
263 FIXME("Flags 0x%08x not yet implemented\n",
264 fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
267 /* Create the dialog from a template */
269 if(!(hRes = FindResourceW(COMDLG32_hInstance,MAKEINTRESOURCEW(NEWFILEOPENORD),(LPCWSTR)RT_DIALOG)))
271 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
272 return FALSE;
274 if (!(dwSize = SizeofResource(COMDLG32_hInstance, hRes)) ||
275 !(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes)) ||
276 !(origTemplate = LockResource(hDlgTmpl)))
278 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
279 return FALSE;
281 if (!(template = HeapAlloc(GetProcessHeap(), 0, dwSize)))
283 COMDLG32_SetCommDlgExtendedError(CDERR_MEMALLOCFAILURE);
284 return FALSE;
286 memcpy(template, origTemplate, dwSize);
288 /* msdn: explorer style dialogs permit sizing by default.
289 * The OFN_ENABLESIZING flag is only needed when a hook or
290 * custom tmeplate is provided */
291 if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
292 !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
293 fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
295 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
297 template->style |= WS_SIZEBOX;
298 fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
299 fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
301 else
302 template->style &= ~WS_SIZEBOX;
305 /* old style hook messages */
306 if (IsHooked(fodInfos))
308 fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
309 fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
310 fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
311 fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
314 /* Some shell namespace extensions depend on COM being initialized. */
315 hr = OleInitialize(NULL);
317 if (fodInfos->unicode)
318 lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
319 template,
320 fodInfos->ofnInfos->hwndOwner,
321 FileOpenDlgProc95,
322 (LPARAM) fodInfos);
323 else
324 lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
325 template,
326 fodInfos->ofnInfos->hwndOwner,
327 FileOpenDlgProc95,
328 (LPARAM) fodInfos);
329 if (SUCCEEDED(hr))
330 OleUninitialize();
332 HeapFree(GetProcessHeap(), 0, template);
334 /* Unable to create the dialog */
335 if( lRes == -1)
336 return FALSE;
338 return lRes;
341 /***********************************************************************
342 * GetFileDialog95A
344 * Call GetFileName95 with this structure and clean the memory.
346 * IN : The OPENFILENAMEA initialisation structure passed to
347 * GetOpenFileNameA win api function (see filedlg.c)
349 static BOOL GetFileDialog95A(LPOPENFILENAMEA ofn,UINT iDlgType)
351 BOOL ret;
352 FileOpenDlgInfos fodInfos;
353 LPSTR lpstrSavDir = NULL;
354 LPWSTR title = NULL;
355 LPWSTR defext = NULL;
356 LPWSTR filter = NULL;
357 LPWSTR customfilter = NULL;
358 INITCOMMONCONTROLSEX icc;
360 /* Initialize ComboBoxEx32 */
361 icc.dwSize = sizeof(icc);
362 icc.dwICC = ICC_USEREX_CLASSES;
363 InitCommonControlsEx(&icc);
365 /* Initialize CommDlgExtendedError() */
366 COMDLG32_SetCommDlgExtendedError(0);
368 /* Initialize FileOpenDlgInfos structure */
369 ZeroMemory(&fodInfos, sizeof(FileOpenDlgInfos));
371 /* Pass in the original ofn */
372 fodInfos.ofnInfos = (LPOPENFILENAMEW)ofn;
374 /* save current directory */
375 if (ofn->Flags & OFN_NOCHANGEDIR)
377 lpstrSavDir = MemAlloc(MAX_PATH);
378 GetCurrentDirectoryA(MAX_PATH, lpstrSavDir);
381 fodInfos.unicode = FALSE;
383 /* convert all the input strings to unicode */
384 if(ofn->lpstrInitialDir)
386 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrInitialDir, -1, NULL, 0 );
387 fodInfos.initdir = MemAlloc((len+1)*sizeof(WCHAR));
388 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrInitialDir, -1, fodInfos.initdir, len);
390 else
391 fodInfos.initdir = NULL;
393 if(ofn->lpstrFile)
395 fodInfos.filename = MemAlloc(ofn->nMaxFile*sizeof(WCHAR));
396 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFile, -1, fodInfos.filename, ofn->nMaxFile);
398 else
399 fodInfos.filename = NULL;
401 if(ofn->lpstrDefExt)
403 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrDefExt, -1, NULL, 0 );
404 defext = MemAlloc((len+1)*sizeof(WCHAR));
405 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrDefExt, -1, defext, len);
407 fodInfos.defext = defext;
409 if(ofn->lpstrTitle)
411 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrTitle, -1, NULL, 0 );
412 title = MemAlloc((len+1)*sizeof(WCHAR));
413 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrTitle, -1, title, len);
415 fodInfos.title = title;
417 if (ofn->lpstrFilter)
419 LPCSTR s;
420 int n, len;
422 /* filter is a list... title\0ext\0......\0\0 */
423 s = ofn->lpstrFilter;
424 while (*s) s = s+strlen(s)+1;
425 s++;
426 n = s - ofn->lpstrFilter;
427 len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0 );
428 filter = MemAlloc(len*sizeof(WCHAR));
429 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFilter, n, filter, len );
431 fodInfos.filter = filter;
433 /* convert lpstrCustomFilter */
434 if (ofn->lpstrCustomFilter)
436 LPCSTR s;
437 int n, len;
439 /* customfilter contains a pair of strings... title\0ext\0 */
440 s = ofn->lpstrCustomFilter;
441 if (*s) s = s+strlen(s)+1;
442 if (*s) s = s+strlen(s)+1;
443 n = s - ofn->lpstrCustomFilter;
444 len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0 );
445 customfilter = MemAlloc(len*sizeof(WCHAR));
446 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrCustomFilter, n, customfilter, len );
448 fodInfos.customfilter = customfilter;
450 /* Initialize the dialog property */
451 fodInfos.DlgInfos.dwDlgProp = 0;
452 fodInfos.DlgInfos.hwndCustomDlg = NULL;
454 switch(iDlgType)
456 case OPEN_DIALOG :
457 ret = GetFileName95(&fodInfos);
458 break;
459 case SAVE_DIALOG :
460 fodInfos.DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
461 ret = GetFileName95(&fodInfos);
462 break;
463 default :
464 ret = 0;
467 if (lpstrSavDir)
469 SetCurrentDirectoryA(lpstrSavDir);
470 MemFree(lpstrSavDir);
473 MemFree(title);
474 MemFree(defext);
475 MemFree(filter);
476 MemFree(customfilter);
477 MemFree(fodInfos.initdir);
478 MemFree(fodInfos.filename);
480 TRACE("selected file: %s\n",ofn->lpstrFile);
482 return ret;
485 /***********************************************************************
486 * GetFileDialog95W
488 * Copy the OPENFILENAMEW structure in a FileOpenDlgInfos structure.
489 * Call GetFileName95 with this structure and clean the memory.
492 static BOOL GetFileDialog95W(LPOPENFILENAMEW ofn,UINT iDlgType)
494 BOOL ret;
495 FileOpenDlgInfos fodInfos;
496 LPWSTR lpstrSavDir = NULL;
497 INITCOMMONCONTROLSEX icc;
499 /* Initialize ComboBoxEx32 */
500 icc.dwSize = sizeof(icc);
501 icc.dwICC = ICC_USEREX_CLASSES;
502 InitCommonControlsEx(&icc);
504 /* Initialize CommDlgExtendedError() */
505 COMDLG32_SetCommDlgExtendedError(0);
507 /* Initialize FileOpenDlgInfos structure */
508 ZeroMemory(&fodInfos, sizeof(FileOpenDlgInfos));
510 /* Pass in the original ofn */
511 fodInfos.ofnInfos = ofn;
513 fodInfos.title = ofn->lpstrTitle;
514 fodInfos.defext = ofn->lpstrDefExt;
515 fodInfos.filter = ofn->lpstrFilter;
516 fodInfos.customfilter = ofn->lpstrCustomFilter;
518 /* convert string arguments, save others */
519 if(ofn->lpstrFile)
521 fodInfos.filename = MemAlloc(ofn->nMaxFile*sizeof(WCHAR));
522 lstrcpynW(fodInfos.filename,ofn->lpstrFile,ofn->nMaxFile);
524 else
525 fodInfos.filename = NULL;
527 if(ofn->lpstrInitialDir)
529 /* fodInfos.initdir = strdupW(ofn->lpstrInitialDir); */
530 DWORD len = lstrlenW(ofn->lpstrInitialDir)+1;
531 fodInfos.initdir = MemAlloc(len*sizeof(WCHAR));
532 memcpy(fodInfos.initdir,ofn->lpstrInitialDir,len*sizeof(WCHAR));
534 else
535 fodInfos.initdir = NULL;
537 /* save current directory */
538 if (ofn->Flags & OFN_NOCHANGEDIR)
540 lpstrSavDir = MemAlloc(MAX_PATH*sizeof(WCHAR));
541 GetCurrentDirectoryW(MAX_PATH, lpstrSavDir);
544 fodInfos.unicode = TRUE;
546 switch(iDlgType)
548 case OPEN_DIALOG :
549 ret = GetFileName95(&fodInfos);
550 break;
551 case SAVE_DIALOG :
552 fodInfos.DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
553 ret = GetFileName95(&fodInfos);
554 break;
555 default :
556 ret = 0;
559 if (lpstrSavDir)
561 SetCurrentDirectoryW(lpstrSavDir);
562 MemFree(lpstrSavDir);
565 /* restore saved IN arguments and convert OUT arguments back */
566 MemFree(fodInfos.filename);
567 MemFree(fodInfos.initdir);
568 return ret;
571 /******************************************************************************
572 * COMDLG32_GetDisplayNameOf [internal]
574 * Helper function to get the display name for a pidl.
576 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
577 LPSHELLFOLDER psfDesktop;
578 STRRET strret;
580 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
581 return FALSE;
583 if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
584 IShellFolder_Release(psfDesktop);
585 return FALSE;
588 IShellFolder_Release(psfDesktop);
589 return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
592 /******************************************************************************
593 * COMDLG32_GetCanonicalPath [internal]
595 * Helper function to get the canonical path.
597 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
598 LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
600 WCHAR lpstrTemp[MAX_PATH];
602 /* Get the current directory name */
603 if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
605 /* last fallback */
606 GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
608 PathAddBackslashW(lpstrPathAndFile);
610 TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile));
612 /* if the user specified a fully qualified path use it */
613 if(PathIsRelativeW(lpstrFile))
615 lstrcatW(lpstrPathAndFile, lpstrFile);
617 else
619 /* does the path have a drive letter? */
620 if (PathGetDriveNumberW(lpstrFile) == -1)
621 lstrcpyW(lpstrPathAndFile+2, lpstrFile);
622 else
623 lstrcpyW(lpstrPathAndFile, lpstrFile);
626 /* resolve "." and ".." */
627 PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
628 lstrcpyW(lpstrPathAndFile, lpstrTemp);
629 TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
632 /***********************************************************************
633 * COMDLG32_SplitFileNames [internal]
635 * Creates a delimited list of filenames.
637 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
639 UINT nStrCharCount = 0; /* index in src buffer */
640 UINT nFileIndex = 0; /* index in dest buffer */
641 UINT nFileCount = 0; /* number of files */
643 /* we might get single filename without any '"',
644 * so we need nStrLen + terminating \0 + end-of-list \0 */
645 *lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
646 *sizeUsed = 0;
648 /* build delimited file list from filenames */
649 while ( nStrCharCount <= nStrLen )
651 if ( lpstrEdit[nStrCharCount]=='"' )
653 nStrCharCount++;
654 while ((lpstrEdit[nStrCharCount]!='"') && (nStrCharCount <= nStrLen))
656 (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
657 nStrCharCount++;
659 (*lpstrFileList)[nFileIndex++] = 0;
660 nFileCount++;
662 nStrCharCount++;
665 /* single, unquoted string */
666 if ((nStrLen > 0) && (nFileIndex == 0) )
668 lstrcpyW(*lpstrFileList, lpstrEdit);
669 nFileIndex = lstrlenW(lpstrEdit) + 1;
670 nFileCount = 1;
673 /* trailing \0 */
674 (*lpstrFileList)[nFileIndex++] = '\0';
676 *sizeUsed = nFileIndex;
677 return nFileCount;
680 /***********************************************************************
681 * ArrangeCtrlPositions [internal]
683 * NOTE: Make sure to add testcases for any changes made here.
685 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
687 HWND hwndChild, hwndStc32;
688 RECT rectParent, rectChild, rectStc32;
689 INT help_fixup = 0;
690 int chgx, chgy;
692 /* Take into account if open as read only checkbox and help button
693 * are hidden
695 if (hide_help)
697 RECT rectHelp, rectCancel;
698 GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
699 GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
700 /* subtract the height of the help button plus the space between
701 * the help button and the cancel button to the height of the dialog
703 help_fixup = rectHelp.bottom - rectCancel.bottom;
707 There are two possibilities to add components to the default file dialog box.
709 By default, all the new components are added below the standard dialog box (the else case).
711 However, if there is a static text component with the stc32 id, a special case happens.
712 The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
713 in the window and the cx and cy indicate how to size the window.
714 Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
715 of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
719 GetClientRect(hwndParentDlg, &rectParent);
721 /* when arranging controls we have to use fixed parent size */
722 rectParent.bottom -= help_fixup;
724 hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
725 if (hwndStc32)
727 GetWindowRect(hwndStc32, &rectStc32);
728 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
730 /* set the size of the stc32 control according to the size of
731 * client area of the parent dialog
733 SetWindowPos(hwndStc32, 0,
734 0, 0,
735 rectParent.right, rectParent.bottom,
736 SWP_NOMOVE | SWP_NOZORDER);
738 else
739 SetRectEmpty(&rectStc32);
741 /* this part moves controls of the child dialog */
742 hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
743 while (hwndChild)
745 if (hwndChild != hwndStc32)
747 GetWindowRect(hwndChild, &rectChild);
748 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
750 /* move only if stc32 exist */
751 if (hwndStc32 && rectChild.left > rectStc32.right)
753 /* move to the right of visible controls of the parent dialog */
754 rectChild.left += rectParent.right;
755 rectChild.left -= rectStc32.right;
757 /* move even if stc32 doesn't exist */
758 if (rectChild.top >= rectStc32.bottom)
760 /* move below visible controls of the parent dialog */
761 rectChild.top += rectParent.bottom;
762 rectChild.top -= rectStc32.bottom - rectStc32.top;
765 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
766 0, 0, SWP_NOSIZE | SWP_NOZORDER);
768 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
771 /* this part moves controls of the parent dialog */
772 hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
773 while (hwndChild)
775 if (hwndChild != hwndChildDlg)
777 GetWindowRect(hwndChild, &rectChild);
778 MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
780 /* left,top of stc32 marks the position of controls
781 * from the parent dialog
783 rectChild.left += rectStc32.left;
784 rectChild.top += rectStc32.top;
786 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
787 0, 0, SWP_NOSIZE | SWP_NOZORDER);
789 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
792 /* calculate the size of the resulting dialog */
794 /* here we have to use original parent size */
795 GetClientRect(hwndParentDlg, &rectParent);
796 GetClientRect(hwndChildDlg, &rectChild);
797 TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
798 wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
800 if (hwndStc32)
802 /* width */
803 if (rectParent.right > rectStc32.right - rectStc32.left)
804 chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
805 else
806 chgx = rectChild.right - rectParent.right;
807 /* height */
808 if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
809 chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
810 else
811 /* Unconditionally set new dialog
812 * height to that of the child
814 chgy = rectChild.bottom - rectParent.bottom;
816 else
818 chgx = 0;
819 chgy = rectChild.bottom - help_fixup;
821 /* set the size of the parent dialog */
822 GetWindowRect(hwndParentDlg, &rectParent);
823 SetWindowPos(hwndParentDlg, 0,
824 0, 0,
825 rectParent.right - rectParent.left + chgx,
826 rectParent.bottom - rectParent.top + chgy,
827 SWP_NOMOVE | SWP_NOZORDER);
830 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
832 switch(uMsg) {
833 case WM_INITDIALOG:
834 return TRUE;
836 return FALSE;
839 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
841 LPCVOID template;
842 HRSRC hRes;
843 HANDLE hDlgTmpl = 0;
844 HWND hChildDlg = 0;
846 TRACE("\n");
849 * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
850 * structure's hInstance parameter is not a HINSTANCE, but
851 * instead a pointer to a template resource to use.
853 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
855 HINSTANCE hinst;
856 if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
858 hinst = COMDLG32_hInstance;
859 if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
861 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
862 return NULL;
865 else
867 hinst = fodInfos->ofnInfos->hInstance;
868 if(fodInfos->unicode)
870 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
871 hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
873 else
875 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
876 hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
878 if (!hRes)
880 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
881 return NULL;
883 if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
884 !(template = LockResource( hDlgTmpl )))
886 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
887 return NULL;
890 if (fodInfos->unicode)
891 hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
892 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
893 (LPARAM)fodInfos->ofnInfos);
894 else
895 hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
896 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
897 (LPARAM)fodInfos->ofnInfos);
898 return hChildDlg;
900 else if( IsHooked(fodInfos))
902 RECT rectHwnd;
903 struct {
904 DLGTEMPLATE tmplate;
905 WORD menu,class,title;
906 } temp;
907 GetClientRect(hwnd,&rectHwnd);
908 temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
909 temp.tmplate.dwExtendedStyle = 0;
910 temp.tmplate.cdit = 0;
911 temp.tmplate.x = 0;
912 temp.tmplate.y = 0;
913 temp.tmplate.cx = 0;
914 temp.tmplate.cy = 0;
915 temp.menu = temp.class = temp.title = 0;
917 hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
918 hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
920 return hChildDlg;
922 return NULL;
925 /***********************************************************************
926 * SendCustomDlgNotificationMessage
928 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
931 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
933 LRESULT hook_result = 0;
934 FileOpenDlgInfos *fodInfos = GetPropA(hwndParentDlg,FileOpenDlgInfosStr);
936 TRACE("%p 0x%04x\n",hwndParentDlg, uCode);
938 if(!fodInfos) return 0;
940 if(fodInfos->DlgInfos.hwndCustomDlg)
942 TRACE("CALL NOTIFY for %x\n", uCode);
943 if(fodInfos->unicode)
945 OFNOTIFYW ofnNotify;
946 ofnNotify.hdr.hwndFrom=hwndParentDlg;
947 ofnNotify.hdr.idFrom=0;
948 ofnNotify.hdr.code = uCode;
949 ofnNotify.lpOFN = fodInfos->ofnInfos;
950 ofnNotify.pszFile = NULL;
951 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
953 else
955 OFNOTIFYA ofnNotify;
956 ofnNotify.hdr.hwndFrom=hwndParentDlg;
957 ofnNotify.hdr.idFrom=0;
958 ofnNotify.hdr.code = uCode;
959 ofnNotify.lpOFN = (LPOPENFILENAMEA)fodInfos->ofnInfos;
960 ofnNotify.pszFile = NULL;
961 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
963 TRACE("RET NOTIFY\n");
965 TRACE("Retval: 0x%08lx\n", hook_result);
966 return hook_result;
969 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
971 UINT len, total;
972 WCHAR *p, *buffer;
973 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
975 TRACE("CDM_GETFILEPATH:\n");
977 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
978 return -1;
980 /* get path and filenames */
981 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
982 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
983 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
984 if (len)
986 p = buffer + strlenW(buffer);
987 *p++ = '\\';
988 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
990 if (fodInfos->unicode)
992 total = strlenW( buffer) + 1;
993 if (result) lstrcpynW( result, buffer, size );
994 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
996 else
998 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
999 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
1000 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
1002 HeapFree( GetProcessHeap(), 0, buffer );
1003 return total;
1006 /***********************************************************************
1007 * FILEDLG95_HandleCustomDialogMessages
1009 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
1011 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1013 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1014 WCHAR lpstrPath[MAX_PATH];
1015 INT_PTR retval;
1017 if(!fodInfos) return FALSE;
1019 switch(uMsg)
1021 case CDM_GETFILEPATH:
1022 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
1023 break;
1025 case CDM_GETFOLDERPATH:
1026 TRACE("CDM_GETFOLDERPATH:\n");
1027 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
1028 if (lParam)
1030 if (fodInfos->unicode)
1031 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
1032 else
1033 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
1034 (LPSTR)lParam, (int)wParam, NULL, NULL);
1036 retval = lstrlenW(lpstrPath) + 1;
1037 break;
1039 case CDM_GETFOLDERIDLIST:
1040 retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
1041 if (retval <= wParam)
1042 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
1043 break;
1045 case CDM_GETSPEC:
1046 TRACE("CDM_GETSPEC:\n");
1047 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
1048 if (lParam)
1050 if (fodInfos->unicode)
1051 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1052 else
1053 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1055 break;
1057 case CDM_SETCONTROLTEXT:
1058 TRACE("CDM_SETCONTROLTEXT:\n");
1059 if ( lParam )
1061 if( fodInfos->unicode )
1062 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
1063 else
1064 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
1066 retval = TRUE;
1067 break;
1069 case CDM_HIDECONTROL:
1070 /* MSDN states that it should fail for not OFN_EXPLORER case */
1071 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1073 HWND control = GetDlgItem( hwnd, wParam );
1074 if (control) ShowWindow( control, SW_HIDE );
1075 retval = TRUE;
1077 else retval = FALSE;
1078 break;
1080 default:
1081 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1082 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
1083 return FALSE;
1085 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
1086 return TRUE;
1089 /***********************************************************************
1090 * FILEDLG95_OnWMGetMMI
1092 * WM_GETMINMAXINFO message handler for resizable dialogs
1094 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
1096 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1097 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1098 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1100 mmiptr->ptMinTrackSize = fodInfos->initial_size;
1102 return TRUE;
1105 /***********************************************************************
1106 * FILEDLG95_OnWMSize
1108 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1110 * FIXME: this could be made more elaborate. Now use a simple scheme
1111 * where the file view is enlarged and the controls are either moved
1112 * vertically or horizontally to get out of the way. Only the "grip"
1113 * is moved in both directions to stay in the corner.
1115 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1117 RECT rc, rcview;
1118 int chgx, chgy;
1119 HWND ctrl;
1120 HDWP hdwp;
1121 FileOpenDlgInfos *fodInfos;
1123 if( wParam != SIZE_RESTORED) return FALSE;
1124 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1125 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1126 /* get the new dialog rectangle */
1127 GetWindowRect( hwnd, &rc);
1128 TRACE("Size from %d,%d to %d,%d\n", fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1129 rc.right -rc.left, rc.bottom -rc.top);
1130 /* not initialized yet */
1131 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1132 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1133 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1134 return FALSE;
1135 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1136 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1137 fodInfos->sizedlg.cx = rc.right - rc.left;
1138 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1139 /* change the size of the view window */
1140 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1141 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1142 hdwp = BeginDeferWindowPos( 10);
1143 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1144 rcview.right - rcview.left + chgx,
1145 rcview.bottom - rcview.top + chgy,
1146 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1147 /* change position and sizes of the controls */
1148 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1150 int ctrlid = GetDlgCtrlID( ctrl);
1151 GetWindowRect( ctrl, &rc);
1152 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1153 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1155 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1156 0, 0,
1157 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1159 else if( rc.top > rcview.bottom)
1161 /* if it was below the shell view
1162 * move to bottom */
1163 switch( ctrlid)
1165 /* file name (edit or comboboxex) and file types combo change also width */
1166 case edt1:
1167 case cmb13:
1168 case cmb1:
1169 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1170 rc.right - rc.left + chgx, rc.bottom - rc.top,
1171 SWP_NOACTIVATE | SWP_NOZORDER);
1172 break;
1173 /* then these buttons must move out of the way */
1174 case IDOK:
1175 case IDCANCEL:
1176 case pshHelp:
1177 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1178 0, 0,
1179 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1180 break;
1181 default:
1182 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1183 0, 0,
1184 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1187 else if( rc.left > rcview.right)
1189 /* if it was to the right of the shell view
1190 * move to right */
1191 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1192 0, 0,
1193 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1195 else
1196 /* special cases */
1198 switch( ctrlid)
1200 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1201 case IDC_LOOKIN:
1202 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1203 rc.right - rc.left + chgx, rc.bottom - rc.top,
1204 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1205 break;
1206 case IDC_TOOLBARSTATIC:
1207 case IDC_TOOLBAR:
1208 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1209 0, 0,
1210 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1211 break;
1212 #endif
1213 /* not resized in windows. Since wine uses this invisible control
1214 * to size the browser view it needs to be resized */
1215 case IDC_SHELLSTATIC:
1216 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1217 rc.right - rc.left + chgx,
1218 rc.bottom - rc.top + chgy,
1219 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1220 break;
1224 if(fodInfos->DlgInfos.hwndCustomDlg &&
1225 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1227 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1228 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1230 GetWindowRect( ctrl, &rc);
1231 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1232 if( rc.top > rcview.bottom)
1234 /* if it was below the shell view
1235 * move to bottom */
1236 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1237 rc.right - rc.left, rc.bottom - rc.top,
1238 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1240 else if( rc.left > rcview.right)
1242 /* if it was to the right of the shell view
1243 * move to right */
1244 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1245 rc.right - rc.left, rc.bottom - rc.top,
1246 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1249 /* size the custom dialog at the end: some applications do some
1250 * control re-arranging at this point */
1251 GetClientRect(hwnd, &rc);
1252 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1253 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1255 EndDeferWindowPos( hdwp);
1256 /* should not be needed */
1257 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1258 return TRUE;
1261 /***********************************************************************
1262 * FileOpenDlgProc95
1264 * File open dialog procedure
1266 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1268 #if 0
1269 TRACE("%p 0x%04x\n", hwnd, uMsg);
1270 #endif
1272 switch(uMsg)
1274 case WM_INITDIALOG:
1276 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1277 RECT rc, rcstc;
1278 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1279 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1281 /* Adds the FileOpenDlgInfos in the property list of the dialog
1282 so it will be easily accessible through a GetPropA(...) */
1283 SetPropA(hwnd, FileOpenDlgInfosStr, fodInfos);
1285 FILEDLG95_InitControls(hwnd);
1287 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1289 GetWindowRect( hwnd, &rc);
1290 fodInfos->DlgInfos.hwndGrip =
1291 CreateWindowExA( 0, "SCROLLBAR", NULL,
1292 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1293 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1294 rc.right - gripx, rc.bottom - gripy,
1295 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1298 fodInfos->DlgInfos.hwndCustomDlg =
1299 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1301 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1302 FILEDLG95_FillControls(hwnd, wParam, lParam);
1304 if( fodInfos->DlgInfos.hwndCustomDlg)
1305 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1307 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1308 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1309 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1312 /* if the app has changed the position of the invisible listbox,
1313 * change that of the listview (browser) as well */
1314 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1315 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1316 if( !EqualRect( &rc, &rcstc))
1318 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1319 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1320 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1321 SWP_NOACTIVATE | SWP_NOZORDER);
1324 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1326 GetWindowRect( hwnd, &rc);
1327 fodInfos->sizedlg.cx = rc.right - rc.left;
1328 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1329 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1330 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1331 GetClientRect( hwnd, &rc);
1332 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1333 rc.right - gripx, rc.bottom - gripy,
1334 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1335 /* resize the dialog to the previous invocation */
1336 if( MemDialogSize.cx && MemDialogSize.cy)
1337 SetWindowPos( hwnd, NULL,
1338 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1339 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1342 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1343 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1345 return 0;
1347 case WM_SIZE:
1348 return FILEDLG95_OnWMSize(hwnd, wParam);
1349 case WM_GETMINMAXINFO:
1350 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1351 case WM_COMMAND:
1352 return FILEDLG95_OnWMCommand(hwnd, wParam);
1353 case WM_DRAWITEM:
1355 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1357 case IDC_LOOKIN:
1358 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1359 return TRUE;
1362 return FALSE;
1364 case WM_GETISHELLBROWSER:
1365 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1367 case WM_DESTROY:
1369 FileOpenDlgInfos * fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1370 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1371 MemDialogSize = fodInfos->sizedlg;
1372 RemovePropA(hwnd, FileOpenDlgInfosStr);
1373 return FALSE;
1375 case WM_NOTIFY:
1377 LPNMHDR lpnmh = (LPNMHDR)lParam;
1378 UINT stringId = -1;
1380 /* set up the button tooltips strings */
1381 if(TTN_GETDISPINFOA == lpnmh->code )
1383 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1384 switch(lpnmh->idFrom )
1386 /* Up folder button */
1387 case FCIDM_TB_UPFOLDER:
1388 stringId = IDS_UPFOLDER;
1389 break;
1390 /* New folder button */
1391 case FCIDM_TB_NEWFOLDER:
1392 stringId = IDS_NEWFOLDER;
1393 break;
1394 /* List option button */
1395 case FCIDM_TB_SMALLICON:
1396 stringId = IDS_LISTVIEW;
1397 break;
1398 /* Details option button */
1399 case FCIDM_TB_REPORTVIEW:
1400 stringId = IDS_REPORTVIEW;
1401 break;
1402 /* Desktop button */
1403 case FCIDM_TB_DESKTOP:
1404 stringId = IDS_TODESKTOP;
1405 break;
1406 default:
1407 stringId = 0;
1409 lpdi->hinst = COMDLG32_hInstance;
1410 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1412 return FALSE;
1414 default :
1415 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1416 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1417 return FALSE;
1421 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1423 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1424 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1427 /***********************************************************************
1428 * FILEDLG95_InitControls
1430 * WM_INITDIALOG message handler (before hook notification)
1432 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1434 int win2000plus = 0;
1435 int win98plus = 0;
1436 int handledPath = FALSE;
1437 OSVERSIONINFOW osVi;
1438 static const WCHAR szwSlash[] = { '\\', 0 };
1439 static const WCHAR szwStar[] = { '*',0 };
1441 static const TBBUTTON tbb[] =
1443 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1444 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1445 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1446 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1447 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1448 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1449 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1450 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1451 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1453 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1455 RECT rectTB;
1456 RECT rectlook;
1458 HIMAGELIST toolbarImageList;
1459 SHFILEINFOA shFileInfo;
1460 ITEMIDLIST *desktopPidl;
1462 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1464 TRACE("%p\n", fodInfos);
1466 /* Get windows version emulating */
1467 osVi.dwOSVersionInfoSize = sizeof(osVi);
1468 GetVersionExW(&osVi);
1469 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1470 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1471 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1472 win2000plus = (osVi.dwMajorVersion > 4);
1473 if (win2000plus) win98plus = TRUE;
1475 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1478 /* Use either the edit or the comboboxex for the filename control */
1479 if (filename_is_edit( fodInfos ))
1481 DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1482 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1484 else
1486 DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1487 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1490 /* Get the hwnd of the controls */
1491 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1492 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1494 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1495 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1497 /* construct the toolbar */
1498 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1499 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1501 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1502 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1503 rectTB.left = rectlook.right;
1504 rectTB.top = rectlook.top-1;
1506 if (fodInfos->unicode)
1507 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1508 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1509 rectTB.left, rectTB.top,
1510 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1511 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1512 else
1513 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1514 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1515 rectTB.left, rectTB.top,
1516 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1517 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1519 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1521 /* FIXME: use TB_LOADIMAGES when implemented */
1522 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1523 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1524 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1526 /* Retrieve and add desktop icon to the toolbar */
1527 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1528 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1529 SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1530 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1531 ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1533 DestroyIcon(shFileInfo.hIcon);
1534 CoTaskMemFree(desktopPidl);
1536 /* Finish Toolbar Construction */
1537 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1538 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1540 /* Set the window text with the text specified in the OPENFILENAME structure */
1541 if(fodInfos->title)
1543 SetWindowTextW(hwnd,fodInfos->title);
1545 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1547 WCHAR buf[64];
1548 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1549 SetWindowTextW(hwnd, buf);
1552 /* Initialise the file name edit control */
1553 handledPath = FALSE;
1554 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1556 if(fodInfos->filename)
1558 /* 1. If win2000 or higher and filename contains a path, use it
1559 in preference over the lpstrInitialDir */
1560 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1561 WCHAR tmpBuf[MAX_PATH];
1562 WCHAR *nameBit;
1563 DWORD result;
1565 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1566 if (result) {
1568 /* nameBit is always shorter than the original filename */
1569 lstrcpyW(fodInfos->filename,nameBit);
1571 *nameBit = 0x00;
1572 MemFree(fodInfos->initdir);
1573 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1574 lstrcpyW(fodInfos->initdir, tmpBuf);
1575 handledPath = TRUE;
1576 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1577 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1579 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1581 } else {
1582 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1586 /* 2. (All platforms) If initdir is not null, then use it */
1587 if ((handledPath == FALSE) && (fodInfos->initdir!=NULL) &&
1588 (*fodInfos->initdir!=0x00))
1590 /* Work out the proper path as supplied one might be relative */
1591 /* (Here because supplying '.' as dir browses to My Computer) */
1592 if (handledPath==FALSE) {
1593 WCHAR tmpBuf[MAX_PATH];
1594 WCHAR tmpBuf2[MAX_PATH];
1595 WCHAR *nameBit;
1596 DWORD result;
1598 lstrcpyW(tmpBuf, fodInfos->initdir);
1599 if( PathFileExistsW(tmpBuf) ) {
1600 /* initdir does not have to be a directory. If a file is
1601 * specified, the dir part is taken */
1602 if( PathIsDirectoryW(tmpBuf)) {
1603 if (tmpBuf[lstrlenW(tmpBuf)-1] != '\\') {
1604 lstrcatW(tmpBuf, szwSlash);
1606 lstrcatW(tmpBuf, szwStar);
1608 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1609 if (result) {
1610 *nameBit = 0x00;
1611 MemFree(fodInfos->initdir);
1612 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1)*sizeof(WCHAR));
1613 lstrcpyW(fodInfos->initdir, tmpBuf2);
1614 handledPath = TRUE;
1615 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1618 else if (fodInfos->initdir)
1620 MemFree(fodInfos->initdir);
1621 fodInfos->initdir = NULL;
1622 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1627 if ((handledPath == FALSE) && ((fodInfos->initdir==NULL) ||
1628 (*fodInfos->initdir==0x00)))
1630 /* 3. All except w2k+: if filename contains a path use it */
1631 if (!win2000plus && fodInfos->filename &&
1632 *fodInfos->filename &&
1633 strpbrkW(fodInfos->filename, szwSlash)) {
1634 WCHAR tmpBuf[MAX_PATH];
1635 WCHAR *nameBit;
1636 DWORD result;
1638 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1639 tmpBuf, &nameBit);
1640 if (result) {
1641 int len;
1643 /* nameBit is always shorter than the original filename */
1644 lstrcpyW(fodInfos->filename, nameBit);
1645 *nameBit = 0x00;
1647 len = lstrlenW(tmpBuf);
1648 MemFree(fodInfos->initdir);
1649 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1650 lstrcpyW(fodInfos->initdir, tmpBuf);
1652 handledPath = TRUE;
1653 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1654 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1656 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1659 /* 4. Win2000+: Recently used */
1660 if (handledPath == FALSE && win2000plus) {
1661 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1662 fodInfos->initdir[0] = '\0';
1664 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1666 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1667 handledPath = TRUE;
1668 }else{
1669 MemFree(fodInfos->initdir);
1670 fodInfos->initdir = NULL;
1674 /* 5. win98+ and win2000+ if any files of specified filter types in
1675 current directory, use it */
1676 if ( win98plus && handledPath == FALSE &&
1677 fodInfos->filter && *fodInfos->filter) {
1679 LPCWSTR lpstrPos = fodInfos->filter;
1680 WIN32_FIND_DATAW FindFileData;
1681 HANDLE hFind;
1683 while (1)
1685 /* filter is a list... title\0ext\0......\0\0 */
1687 /* Skip the title */
1688 if(! *lpstrPos) break; /* end */
1689 lpstrPos += lstrlenW(lpstrPos) + 1;
1691 /* See if any files exist in the current dir with this extension */
1692 if(! *lpstrPos) break; /* end */
1694 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1696 if (hFind == INVALID_HANDLE_VALUE) {
1697 /* None found - continue search */
1698 lpstrPos += lstrlenW(lpstrPos) + 1;
1700 } else {
1702 MemFree(fodInfos->initdir);
1703 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1704 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1706 handledPath = TRUE;
1707 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1708 debugstr_w(lpstrPos));
1709 FindClose(hFind);
1710 break;
1715 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1716 if (handledPath == FALSE && (win2000plus || win98plus)) {
1717 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1719 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1721 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1723 /* last fallback */
1724 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1725 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1726 } else {
1727 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1729 } else {
1730 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1732 handledPath = TRUE;
1733 } else if (handledPath==FALSE) {
1734 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1735 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1736 handledPath = TRUE;
1737 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1740 SetFocus( fodInfos->DlgInfos.hwndFileName );
1741 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1743 /* Must the open as read only check box be checked ?*/
1744 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1746 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1749 /* Must the open as read only check box be hidden? */
1750 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1752 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1753 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1756 /* Must the help button be hidden? */
1757 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1759 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1760 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1763 /* change Open to Save */
1764 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1766 WCHAR buf[16];
1767 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1768 SetDlgItemTextW(hwnd, IDOK, buf);
1769 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1770 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1773 /* Initialize the filter combo box */
1774 FILEDLG95_FILETYPE_Init(hwnd);
1776 return 0;
1779 /***********************************************************************
1780 * FILEDLG95_ResizeControls
1782 * WM_INITDIALOG message handler (after hook notification)
1784 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1786 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1788 if (fodInfos->DlgInfos.hwndCustomDlg)
1790 RECT rc;
1791 UINT flags = SWP_NOACTIVATE;
1793 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1794 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1796 /* resize the custom dialog to the parent size */
1797 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1798 GetClientRect(hwnd, &rc);
1799 else
1801 /* our own fake template is zero sized and doesn't have children, so
1802 * there is no need to resize it. Picasa depends on it.
1804 flags |= SWP_NOSIZE;
1805 SetRectEmpty(&rc);
1807 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1808 0, 0, rc.right, rc.bottom, flags);
1810 else
1812 /* Resize the height, if open as read only checkbox ad help button are
1813 * hidden and we are not using a custom template nor a customDialog
1815 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1816 (!(fodInfos->ofnInfos->Flags &
1817 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1819 RECT rectDlg, rectHelp, rectCancel;
1820 GetWindowRect(hwnd, &rectDlg);
1821 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1822 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1823 /* subtract the height of the help button plus the space between the help
1824 * button and the cancel button to the height of the dialog
1826 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1827 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1828 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1831 return TRUE;
1834 /***********************************************************************
1835 * FILEDLG95_FillControls
1837 * WM_INITDIALOG message handler (after hook notification)
1839 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1841 LPITEMIDLIST pidlItemId = NULL;
1843 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1845 TRACE("dir=%s file=%s\n",
1846 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1848 /* Get the initial directory pidl */
1850 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1852 WCHAR path[MAX_PATH];
1854 GetCurrentDirectoryW(MAX_PATH,path);
1855 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1858 /* Initialise shell objects */
1859 FILEDLG95_SHELL_Init(hwnd);
1861 /* Initialize the Look In combo box */
1862 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1864 /* Browse to the initial directory */
1865 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1867 /* Free pidlItem memory */
1868 COMDLG32_SHFree(pidlItemId);
1870 return TRUE;
1872 /***********************************************************************
1873 * FILEDLG95_Clean
1875 * Regroups all the cleaning functions of the filedlg
1877 void FILEDLG95_Clean(HWND hwnd)
1879 FILEDLG95_FILETYPE_Clean(hwnd);
1880 FILEDLG95_LOOKIN_Clean(hwnd);
1881 FILEDLG95_SHELL_Clean(hwnd);
1883 /***********************************************************************
1884 * FILEDLG95_OnWMCommand
1886 * WM_COMMAND message handler
1888 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1890 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1891 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1892 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1894 switch(wID)
1896 /* OK button */
1897 case IDOK:
1898 FILEDLG95_OnOpen(hwnd);
1899 break;
1900 /* Cancel button */
1901 case IDCANCEL:
1902 FILEDLG95_Clean(hwnd);
1903 EndDialog(hwnd, FALSE);
1904 break;
1905 /* Filetype combo box */
1906 case IDC_FILETYPE:
1907 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1908 break;
1909 /* LookIn combo box */
1910 case IDC_LOOKIN:
1911 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1912 break;
1914 /* --- toolbar --- */
1915 /* Up folder button */
1916 case FCIDM_TB_UPFOLDER:
1917 FILEDLG95_SHELL_UpFolder(hwnd);
1918 break;
1919 /* New folder button */
1920 case FCIDM_TB_NEWFOLDER:
1921 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1922 break;
1923 /* List option button */
1924 case FCIDM_TB_SMALLICON:
1925 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1926 break;
1927 /* Details option button */
1928 case FCIDM_TB_REPORTVIEW:
1929 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1930 break;
1931 /* Details option button */
1932 case FCIDM_TB_DESKTOP:
1933 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1934 break;
1936 case edt1:
1937 case cmb13:
1938 break;
1941 /* Do not use the listview selection anymore */
1942 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1943 return 0;
1946 /***********************************************************************
1947 * FILEDLG95_OnWMGetIShellBrowser
1949 * WM_GETISHELLBROWSER message handler
1951 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1953 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1955 TRACE("\n");
1957 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1959 return TRUE;
1963 /***********************************************************************
1964 * FILEDLG95_SendFileOK
1966 * Sends the CDN_FILEOK notification if required
1968 * RETURNS
1969 * TRUE if the dialog should close
1970 * FALSE if the dialog should not be closed
1972 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1974 /* ask the hook if we can close */
1975 if(IsHooked(fodInfos))
1977 LRESULT retval = 0;
1979 TRACE("---\n");
1980 /* First send CDN_FILEOK as MSDN doc says */
1981 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1982 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1983 if( retval)
1985 TRACE("canceled\n");
1986 return FALSE;
1989 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1990 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1991 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1992 if( retval)
1994 TRACE("canceled\n");
1995 return FALSE;
1998 return TRUE;
2001 /***********************************************************************
2002 * FILEDLG95_OnOpenMultipleFiles
2004 * Handles the opening of multiple files.
2006 * FIXME
2007 * check destination buffer size
2009 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
2011 WCHAR lpstrPathSpec[MAX_PATH] = {0};
2012 UINT nCount, nSizePath;
2013 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2015 TRACE("\n");
2017 if(fodInfos->unicode)
2019 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2020 ofn->lpstrFile[0] = '\0';
2022 else
2024 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
2025 ofn->lpstrFile[0] = '\0';
2028 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
2030 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2031 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
2032 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
2034 LPWSTR lpstrTemp = lpstrFileList;
2036 for ( nCount = 0; nCount < nFileCount; nCount++ )
2038 LPITEMIDLIST pidl;
2040 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
2041 if (!pidl)
2043 WCHAR lpstrNotFound[100];
2044 WCHAR lpstrMsg[100];
2045 WCHAR tmp[400];
2046 static const WCHAR nl[] = {'\n',0};
2048 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
2049 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
2051 lstrcpyW(tmp, lpstrTemp);
2052 lstrcatW(tmp, nl);
2053 lstrcatW(tmp, lpstrNotFound);
2054 lstrcatW(tmp, nl);
2055 lstrcatW(tmp, lpstrMsg);
2057 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
2058 return FALSE;
2061 /* move to the next file in the list of files */
2062 lpstrTemp += lstrlenW(lpstrTemp) + 1;
2063 COMDLG32_SHFree(pidl);
2067 nSizePath = lstrlenW(lpstrPathSpec) + 1;
2068 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2070 /* For "oldstyle" dialog the components have to
2071 be separated by blanks (not '\0'!) and short
2072 filenames have to be used! */
2073 FIXME("Components have to be separated by blanks\n");
2075 if(fodInfos->unicode)
2077 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2078 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2079 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2081 else
2083 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2085 if (ofn->lpstrFile != NULL)
2087 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2088 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2089 if (ofn->nMaxFile > nSizePath)
2091 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2092 ofn->lpstrFile + nSizePath,
2093 ofn->nMaxFile - nSizePath, NULL, NULL);
2098 fodInfos->ofnInfos->nFileOffset = nSizePath;
2099 fodInfos->ofnInfos->nFileExtension = 0;
2101 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2102 return FALSE;
2104 /* clean and exit */
2105 FILEDLG95_Clean(hwnd);
2106 return EndDialog(hwnd,TRUE);
2109 /* Returns the 'slot name' of the given module_name in the registry's
2110 * most-recently-used list. This will be an ASCII value in the
2111 * range ['a','z'). Returns zero on error.
2113 * The slot's value in the registry has the form:
2114 * module_name\0mru_path\0
2116 * If stored_path is given, then stored_path will contain the path name
2117 * stored in the registry's MRU list for the given module_name.
2119 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2120 * MRU list key for the given module_name.
2122 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2124 WCHAR mru_list[32], *cur_mru_slot;
2125 BOOL taken[25] = {0};
2126 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2127 HKEY hkey_tmp, *hkey;
2128 LONG ret;
2130 if(hkey_ret)
2131 hkey = hkey_ret;
2132 else
2133 hkey = &hkey_tmp;
2135 if(stored_path)
2136 *stored_path = '\0';
2138 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2139 if(ret){
2140 WARN("Unable to create MRU key: %d\n", ret);
2141 return 0;
2144 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2145 (LPBYTE)mru_list, &mru_list_size);
2146 if(ret || key_type != REG_SZ){
2147 if(ret == ERROR_FILE_NOT_FOUND)
2148 return 'a';
2150 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2151 RegCloseKey(*hkey);
2152 return 0;
2155 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2156 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2157 DWORD value_data_size = sizeof(value_data);
2159 *value_name = *cur_mru_slot;
2161 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2162 &key_type, (LPBYTE)value_data, &value_data_size);
2163 if(ret || key_type != REG_BINARY){
2164 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2165 continue;
2168 if(!strcmpiW(module_name, value_data)){
2169 if(!hkey_ret)
2170 RegCloseKey(*hkey);
2171 if(stored_path)
2172 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2173 return *value_name;
2177 if(!hkey_ret)
2178 RegCloseKey(*hkey);
2180 /* the module name isn't in the registry, so find the next open slot */
2181 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2182 taken[*cur_mru_slot - 'a'] = TRUE;
2183 for(i = 0; i < 25; ++i){
2184 if(!taken[i])
2185 return i + 'a';
2188 /* all slots are taken, so return the last one in MRUList */
2189 --cur_mru_slot;
2190 return *cur_mru_slot;
2193 /* save the given filename as most-recently-used path for this module */
2194 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2196 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2197 LONG ret;
2198 HKEY hkey;
2200 /* get the current executable's name */
2201 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2202 WARN("GotModuleFileName failed: %d\n", GetLastError());
2203 return;
2205 module_name = strrchrW(module_path, '\\');
2206 if(!module_name)
2207 module_name = module_path;
2208 else
2209 module_name += 1;
2211 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2212 if(!slot)
2213 return;
2214 *slot_name = slot;
2216 { /* update the slot's info */
2217 WCHAR *path_ends, *final;
2218 DWORD path_len, final_len;
2220 /* use only the path segment of `filename' */
2221 path_ends = strrchrW(filename, '\\');
2222 path_len = path_ends - filename;
2224 final_len = path_len + lstrlenW(module_name) + 2;
2226 final = MemAlloc(final_len * sizeof(WCHAR));
2227 if(!final)
2228 return;
2229 lstrcpyW(final, module_name);
2230 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2231 final[final_len-1] = '\0';
2233 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2234 final_len * sizeof(WCHAR));
2235 if(ret){
2236 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2237 MemFree(final);
2238 RegCloseKey(hkey);
2239 return;
2242 MemFree(final);
2245 { /* update MRUList value */
2246 WCHAR old_mru_list[32], new_mru_list[32];
2247 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2248 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2250 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2251 (LPBYTE)old_mru_list, &mru_list_size);
2252 if(ret || key_type != REG_SZ){
2253 if(ret == ERROR_FILE_NOT_FOUND){
2254 new_mru_list[0] = slot;
2255 new_mru_list[1] = '\0';
2256 }else{
2257 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2258 RegCloseKey(hkey);
2259 return;
2261 }else{
2262 /* copy old list data over so that the new slot is at the start
2263 * of the list */
2264 *new_mru_slot++ = slot;
2265 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2266 if(*old_mru_slot != slot)
2267 *new_mru_slot++ = *old_mru_slot;
2269 *new_mru_slot = '\0';
2272 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2273 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2274 if(ret){
2275 WARN("Error saving MRUList data: %d\n", ret);
2276 RegCloseKey(hkey);
2277 return;
2282 /* load the most-recently-used path for this module */
2283 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2285 WCHAR module_path[MAX_PATH], *module_name;
2287 /* get the current executable's name */
2288 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2289 WARN("GotModuleFileName failed: %d\n", GetLastError());
2290 return;
2292 module_name = strrchrW(module_path, '\\');
2293 if(!module_name)
2294 module_name = module_path;
2295 else
2296 module_name += 1;
2298 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2299 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2302 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2304 WCHAR strMsgTitle[MAX_PATH];
2305 WCHAR strMsgText [MAX_PATH];
2306 if (idCaption)
2307 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2308 else
2309 strMsgTitle[0] = '\0';
2310 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2311 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2314 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2315 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2317 int nOpenAction = defAction;
2318 LPWSTR lpszTemp, lpszTemp1;
2319 LPITEMIDLIST pidl = NULL;
2320 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2322 /* check for invalid chars */
2323 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2325 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2326 return FALSE;
2329 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2331 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2332 while (lpszTemp1)
2334 LPSHELLFOLDER lpsfChild;
2335 WCHAR lpwstrTemp[MAX_PATH];
2336 DWORD dwEaten, dwAttributes;
2337 LPWSTR p;
2339 lstrcpyW(lpwstrTemp, lpszTemp);
2340 p = PathFindNextComponentW(lpwstrTemp);
2342 if (!p) break; /* end of path */
2344 *p = 0;
2345 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2347 /* There are no wildcards when OFN_NOVALIDATE is set */
2348 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2350 static const WCHAR wszWild[] = { '*', '?', 0 };
2351 /* if the last element is a wildcard do a search */
2352 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2354 nOpenAction = ONOPEN_SEARCH;
2355 break;
2358 lpszTemp1 = lpszTemp;
2360 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2362 /* append a backslash to drive letters */
2363 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2364 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2365 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2367 PathAddBackslashW(lpwstrTemp);
2370 dwAttributes = SFGAO_FOLDER;
2371 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2373 /* the path component is valid, we have a pidl of the next path component */
2374 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2375 if(dwAttributes & SFGAO_FOLDER)
2377 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2379 ERR("bind to failed\n"); /* should not fail */
2380 break;
2382 IShellFolder_Release(*ppsf);
2383 *ppsf = lpsfChild;
2384 lpsfChild = NULL;
2386 else
2388 TRACE("value\n");
2390 /* end dialog, return value */
2391 nOpenAction = ONOPEN_OPEN;
2392 break;
2394 COMDLG32_SHFree(pidl);
2395 pidl = NULL;
2397 else if (!(flags & OFN_NOVALIDATE))
2399 if(*lpszTemp || /* points to trailing null for last path element */
2400 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2402 if(flags & OFN_PATHMUSTEXIST)
2404 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2405 break;
2408 else
2410 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2412 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2413 break;
2416 /* change to the current folder */
2417 nOpenAction = ONOPEN_OPEN;
2418 break;
2420 else
2422 nOpenAction = ONOPEN_OPEN;
2423 break;
2426 if(pidl) COMDLG32_SHFree(pidl);
2428 return nOpenAction;
2431 /***********************************************************************
2432 * FILEDLG95_OnOpen
2434 * Ok button WM_COMMAND message handler
2436 * If the function succeeds, the return value is nonzero.
2438 BOOL FILEDLG95_OnOpen(HWND hwnd)
2440 LPWSTR lpstrFileList;
2441 UINT nFileCount = 0;
2442 UINT sizeUsed = 0;
2443 BOOL ret = TRUE;
2444 WCHAR lpstrPathAndFile[MAX_PATH];
2445 LPSHELLFOLDER lpsf = NULL;
2446 int nOpenAction;
2447 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2449 TRACE("hwnd=%p\n", hwnd);
2451 /* try to browse the selected item */
2452 if(BrowseSelectedFolder(hwnd))
2453 return FALSE;
2455 /* get the files from the edit control */
2456 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2458 if(nFileCount == 0)
2459 return FALSE;
2461 if(nFileCount > 1)
2463 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2464 goto ret;
2467 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2470 Step 1: Build a complete path name from the current folder and
2471 the filename or path in the edit box.
2472 Special cases:
2473 - the path in the edit box is a root path
2474 (with or without drive letter)
2475 - the edit box contains ".." (or a path with ".." in it)
2478 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2479 MemFree(lpstrFileList);
2482 Step 2: here we have a cleaned up path
2484 We have to parse the path step by step to see if we have to browse
2485 to a folder if the path points to a directory or the last
2486 valid element is a directory.
2488 valid variables:
2489 lpstrPathAndFile: cleaned up path
2492 if (nFileCount &&
2493 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2494 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2495 nOpenAction = ONOPEN_OPEN;
2496 else
2497 nOpenAction = ONOPEN_BROWSE;
2499 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2500 fodInfos->ofnInfos->Flags,
2501 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2502 nOpenAction);
2503 if(!nOpenAction)
2504 goto ret;
2507 Step 3: here we have a cleaned up and validated path
2509 valid variables:
2510 lpsf: ShellFolder bound to the rightmost valid path component
2511 lpstrPathAndFile: cleaned up path
2512 nOpenAction: action to do
2514 TRACE("end validate sf=%p\n", lpsf);
2516 switch(nOpenAction)
2518 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2519 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2521 int iPos;
2522 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2523 DWORD len;
2525 /* replace the current filter */
2526 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2527 len = lstrlenW(lpszTemp)+1;
2528 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2529 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2531 /* set the filter cb to the extension when possible */
2532 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2533 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2535 /* fall through */
2536 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2537 TRACE("ONOPEN_BROWSE\n");
2539 IPersistFolder2 * ppf2;
2540 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2542 LPITEMIDLIST pidlCurrent;
2543 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2544 IPersistFolder2_Release(ppf2);
2545 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2547 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2548 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2550 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2553 else if( nOpenAction == ONOPEN_SEARCH )
2555 if (fodInfos->Shell.FOIShellView)
2556 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2558 COMDLG32_SHFree(pidlCurrent);
2559 if (filename_is_edit( fodInfos ))
2560 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2563 ret = FALSE;
2564 break;
2565 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2566 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2568 WCHAR *ext = NULL;
2570 /* update READONLY check box flag */
2571 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2572 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2573 else
2574 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2576 /* Attach the file extension with file name*/
2577 ext = PathFindExtensionW(lpstrPathAndFile);
2578 if (! *ext && fodInfos->defext)
2580 /* if no extension is specified with file name, then */
2581 /* attach the extension from file filter or default one */
2583 WCHAR *filterExt = NULL;
2584 LPWSTR lpstrFilter = NULL;
2585 static const WCHAR szwDot[] = {'.',0};
2586 int PathLength = lstrlenW(lpstrPathAndFile);
2588 /*Get the file extension from file type filter*/
2589 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2590 fodInfos->ofnInfos->nFilterIndex-1);
2592 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2594 WCHAR* filterSearchIndex;
2595 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2596 strcpyW(filterExt, lpstrFilter);
2598 /* if a semicolon-separated list of file extensions was given, do not include the
2599 semicolon or anything after it in the extension.
2600 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2601 filterSearchIndex = strchrW(filterExt, ';');
2602 if (filterSearchIndex)
2604 filterSearchIndex[0] = '\0';
2607 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2608 /* if the extension is invalid or contains a glob, ignore it */
2609 filterSearchIndex = PathFindExtensionW(filterExt);
2610 if (*filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2612 strcpyW(filterExt, filterSearchIndex);
2614 else
2616 HeapFree(GetProcessHeap(), 0, filterExt);
2617 filterExt = NULL;
2621 if (!filterExt)
2623 /* use the default file extension */
2624 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2625 strcpyW(filterExt, fodInfos->defext);
2628 if (*filterExt) /* ignore filterExt="" */
2630 /* Attach the dot*/
2631 lstrcatW(lpstrPathAndFile, szwDot);
2632 /* Attach the extension */
2633 lstrcatW(lpstrPathAndFile, filterExt);
2636 HeapFree(GetProcessHeap(), 0, filterExt);
2638 /* In Open dialog: if file does not exist try without extension */
2639 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2640 lpstrPathAndFile[PathLength] = '\0';
2642 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2643 if (*ext)
2644 ext++;
2645 if (!lstrcmpiW(fodInfos->defext, ext))
2646 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2647 else
2648 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2651 /* In Save dialog: check if the file already exists */
2652 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2653 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2654 && PathFileExistsW(lpstrPathAndFile))
2656 WCHAR lpstrOverwrite[100];
2657 int answer;
2659 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2660 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2661 MB_YESNO | MB_ICONEXCLAMATION);
2662 if (answer == IDNO || answer == IDCANCEL)
2664 ret = FALSE;
2665 goto ret;
2669 /* In Open dialog: check if it should be created if it doesn't exist */
2670 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2671 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2672 && !PathFileExistsW(lpstrPathAndFile))
2674 WCHAR lpstrCreate[100];
2675 int answer;
2677 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2678 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2679 MB_YESNO | MB_ICONEXCLAMATION);
2680 if (answer == IDNO || answer == IDCANCEL)
2682 ret = FALSE;
2683 goto ret;
2687 /* Check that the size of the file does not exceed buffer size.
2688 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2689 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2690 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2693 /* fill destination buffer */
2694 if (fodInfos->ofnInfos->lpstrFile)
2696 if(fodInfos->unicode)
2698 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2700 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2701 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2702 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2704 else
2706 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2708 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2709 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2710 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2711 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2715 if(fodInfos->unicode)
2717 LPWSTR lpszTemp;
2719 /* set filename offset */
2720 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2721 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2723 /* set extension offset */
2724 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2725 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2727 else
2729 LPSTR lpszTemp;
2730 CHAR tempFileA[MAX_PATH];
2732 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2733 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2734 tempFileA, sizeof(tempFileA), NULL, NULL);
2736 /* set filename offset */
2737 lpszTemp = PathFindFileNameA(tempFileA);
2738 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2740 /* set extension offset */
2741 lpszTemp = PathFindExtensionA(tempFileA);
2742 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2745 /* set the lpstrFileTitle */
2746 if(fodInfos->ofnInfos->lpstrFileTitle)
2748 LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2749 if(fodInfos->unicode)
2751 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2752 lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2754 else
2756 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2757 WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2758 ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2762 /* copy currently selected filter to lpstrCustomFilter */
2763 if (fodInfos->ofnInfos->lpstrCustomFilter)
2765 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2766 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2767 NULL, 0, NULL, NULL);
2768 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2770 LPSTR s = ofn->lpstrCustomFilter;
2771 s += strlen(ofn->lpstrCustomFilter)+1;
2772 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2773 s, len, NULL, NULL);
2778 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2779 goto ret;
2781 FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2783 TRACE("close\n");
2784 FILEDLG95_Clean(hwnd);
2785 ret = EndDialog(hwnd, TRUE);
2787 else
2789 WORD size;
2791 size = lstrlenW(lpstrPathAndFile) + 1;
2792 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2793 size += 1;
2794 /* return needed size in first two bytes of lpstrFile */
2795 if(fodInfos->ofnInfos->lpstrFile)
2796 *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2797 FILEDLG95_Clean(hwnd);
2798 ret = EndDialog(hwnd, FALSE);
2799 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2802 break;
2805 ret:
2806 if(lpsf) IShellFolder_Release(lpsf);
2807 return ret;
2810 /***********************************************************************
2811 * FILEDLG95_SHELL_Init
2813 * Initialisation of the shell objects
2815 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2817 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2819 TRACE("\n");
2822 * Initialisation of the FileOpenDialogInfos structure
2825 /* Shell */
2827 /*ShellInfos */
2828 fodInfos->ShellInfos.hwndOwner = hwnd;
2830 /* Disable multi-select if flag not set */
2831 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2833 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2835 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2836 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2838 /* Construct the IShellBrowser interface */
2839 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2841 return NOERROR;
2844 /***********************************************************************
2845 * FILEDLG95_SHELL_ExecuteCommand
2847 * Change the folder option and refresh the view
2848 * If the function succeeds, the return value is nonzero.
2850 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2852 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2853 IContextMenu * pcm;
2855 TRACE("(%p,%p)\n", hwnd, lpVerb);
2857 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2858 SVGIO_BACKGROUND,
2859 &IID_IContextMenu,
2860 (LPVOID*)&pcm)))
2862 CMINVOKECOMMANDINFO ci;
2863 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2864 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2865 ci.lpVerb = lpVerb;
2866 ci.hwnd = hwnd;
2868 IContextMenu_InvokeCommand(pcm, &ci);
2869 IContextMenu_Release(pcm);
2872 return FALSE;
2875 /***********************************************************************
2876 * FILEDLG95_SHELL_UpFolder
2878 * Browse to the specified object
2879 * If the function succeeds, the return value is nonzero.
2881 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2883 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2885 TRACE("\n");
2887 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2888 NULL,
2889 SBSP_PARENT)))
2891 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2892 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2893 return TRUE;
2895 return FALSE;
2898 /***********************************************************************
2899 * FILEDLG95_SHELL_BrowseToDesktop
2901 * Browse to the Desktop
2902 * If the function succeeds, the return value is nonzero.
2904 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2906 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2907 LPITEMIDLIST pidl;
2908 HRESULT hres;
2910 TRACE("\n");
2912 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2913 hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2914 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2915 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2916 COMDLG32_SHFree(pidl);
2917 return SUCCEEDED(hres);
2919 /***********************************************************************
2920 * FILEDLG95_SHELL_Clean
2922 * Cleans the memory used by shell objects
2924 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2926 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2928 TRACE("\n");
2930 COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2932 /* clean Shell interfaces */
2933 if (fodInfos->Shell.FOIShellView)
2935 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2936 IShellView_Release(fodInfos->Shell.FOIShellView);
2938 IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2939 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2940 if (fodInfos->Shell.FOIDataObject)
2941 IDataObject_Release(fodInfos->Shell.FOIDataObject);
2944 /***********************************************************************
2945 * FILEDLG95_FILETYPE_Init
2947 * Initialisation of the file type combo box
2949 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2951 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2952 int nFilters = 0; /* number of filters */
2953 int nFilterIndexCB;
2955 TRACE("\n");
2957 if(fodInfos->customfilter)
2959 /* customfilter has one entry... title\0ext\0
2960 * Set first entry of combo box item with customfilter
2962 LPWSTR lpstrExt;
2963 LPCWSTR lpstrPos = fodInfos->customfilter;
2965 /* Get the title */
2966 lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2968 /* Copy the extensions */
2969 if (! *lpstrPos) return E_FAIL; /* malformed filter */
2970 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2971 lstrcpyW(lpstrExt,lpstrPos);
2973 /* Add the item at the end of the combo */
2974 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2975 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2976 nFilters++;
2978 if(fodInfos->filter)
2980 LPCWSTR lpstrPos = fodInfos->filter;
2982 for(;;)
2984 /* filter is a list... title\0ext\0......\0\0
2985 * Set the combo item text to the title and the item data
2986 * to the ext
2988 LPCWSTR lpstrDisplay;
2989 LPWSTR lpstrExt;
2991 /* Get the title */
2992 if(! *lpstrPos) break; /* end */
2993 lpstrDisplay = lpstrPos;
2994 lpstrPos += lstrlenW(lpstrPos) + 1;
2996 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2998 nFilters++;
3000 /* Copy the extensions */
3001 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
3002 lstrcpyW(lpstrExt,lpstrPos);
3003 lpstrPos += lstrlenW(lpstrPos) + 1;
3005 /* Add the item at the end of the combo */
3006 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
3008 /* malformed filters are added anyway... */
3009 if (!*lpstrExt) break;
3014 * Set the current filter to the one specified
3015 * in the initialisation structure
3017 if (fodInfos->filter || fodInfos->customfilter)
3019 LPWSTR lpstrFilter;
3021 /* Check to make sure our index isn't out of bounds. */
3022 if ( fodInfos->ofnInfos->nFilterIndex >
3023 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
3024 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
3026 /* set default filter index */
3027 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
3028 fodInfos->ofnInfos->nFilterIndex = 1;
3030 /* calculate index of Combo Box item */
3031 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
3032 if (fodInfos->customfilter == NULL)
3033 nFilterIndexCB--;
3035 /* Set the current index selection. */
3036 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
3038 /* Get the corresponding text string from the combo box. */
3039 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3040 nFilterIndexCB);
3042 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
3043 lpstrFilter = NULL;
3045 if(lpstrFilter)
3047 DWORD len;
3048 CharLowerW(lpstrFilter); /* lowercase */
3049 len = lstrlenW(lpstrFilter)+1;
3050 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3051 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3053 } else
3054 fodInfos->ofnInfos->nFilterIndex = 0;
3055 return S_OK;
3058 /***********************************************************************
3059 * FILEDLG95_FILETYPE_OnCommand
3061 * WM_COMMAND of the file type combo box
3062 * If the function succeeds, the return value is nonzero.
3064 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
3066 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3068 switch(wNotifyCode)
3070 case CBN_SELENDOK:
3072 LPWSTR lpstrFilter;
3074 /* Get the current item of the filetype combo box */
3075 int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
3077 /* set the current filter index */
3078 fodInfos->ofnInfos->nFilterIndex = iItem +
3079 (fodInfos->customfilter == NULL ? 1 : 0);
3081 /* Set the current filter with the current selection */
3082 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3084 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3085 iItem);
3086 if((INT_PTR)lpstrFilter != CB_ERR)
3088 DWORD len;
3089 CharLowerW(lpstrFilter); /* lowercase */
3090 len = lstrlenW(lpstrFilter)+1;
3091 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3092 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3093 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3094 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3097 /* Refresh the actual view to display the included items*/
3098 if (fodInfos->Shell.FOIShellView)
3099 IShellView_Refresh(fodInfos->Shell.FOIShellView);
3102 return FALSE;
3104 /***********************************************************************
3105 * FILEDLG95_FILETYPE_SearchExt
3107 * searches for an extension in the filetype box
3109 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3111 int i, iCount = CBGetCount(hwnd);
3113 TRACE("%s\n", debugstr_w(lpstrExt));
3115 if(iCount != CB_ERR)
3117 for(i=0;i<iCount;i++)
3119 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3120 return i;
3123 return -1;
3126 /***********************************************************************
3127 * FILEDLG95_FILETYPE_Clean
3129 * Clean the memory used by the filetype combo box
3131 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3133 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3134 int iPos;
3135 int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3137 TRACE("\n");
3139 /* Delete each string of the combo and their associated data */
3140 if(iCount != CB_ERR)
3142 for(iPos = iCount-1;iPos>=0;iPos--)
3144 MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3145 CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3148 /* Current filter */
3149 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3153 /***********************************************************************
3154 * FILEDLG95_LOOKIN_Init
3156 * Initialisation of the look in combo box
3159 /* Small helper function, to determine if the unixfs shell extension is rooted
3160 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3162 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3163 HKEY hKey;
3164 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3165 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3166 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3167 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3168 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3169 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3170 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3172 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3173 return FALSE;
3175 RegCloseKey(hKey);
3176 return TRUE;
3179 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3181 IShellFolder *psfRoot, *psfDrives;
3182 IEnumIDList *lpeRoot, *lpeDrives;
3183 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3184 HDC hdc;
3185 TEXTMETRICW tm;
3186 LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3188 TRACE("\n");
3190 liInfos->iMaxIndentation = 0;
3192 SetPropA(hwndCombo, LookInInfosStr, liInfos);
3194 hdc = GetDC( hwndCombo );
3195 SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3196 GetTextMetricsW( hdc, &tm );
3197 ReleaseDC( hwndCombo, hdc );
3199 /* set item height for both text field and listbox */
3200 CBSetItemHeight( hwndCombo, -1, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3201 CBSetItemHeight( hwndCombo, 0, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3203 /* Turn on the extended UI for the combo box like Windows does */
3204 CBSetExtendedUI(hwndCombo, TRUE);
3206 /* Initialise data of Desktop folder */
3207 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3208 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3209 COMDLG32_SHFree(pidlTmp);
3211 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3213 SHGetDesktopFolder(&psfRoot);
3215 if (psfRoot)
3217 /* enumerate the contents of the desktop */
3218 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3220 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3222 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3224 /* If the unixfs extension is rooted, we don't expand the drives by default */
3225 if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3227 /* special handling for CSIDL_DRIVES */
3228 if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3230 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3232 /* enumerate the drives */
3233 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3235 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3237 pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3238 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3239 COMDLG32_SHFree(pidlAbsTmp);
3240 COMDLG32_SHFree(pidlTmp1);
3242 IEnumIDList_Release(lpeDrives);
3244 IShellFolder_Release(psfDrives);
3249 COMDLG32_SHFree(pidlTmp);
3251 IEnumIDList_Release(lpeRoot);
3253 IShellFolder_Release(psfRoot);
3256 COMDLG32_SHFree(pidlDrives);
3259 /***********************************************************************
3260 * FILEDLG95_LOOKIN_DrawItem
3262 * WM_DRAWITEM message handler
3264 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3266 COLORREF crWin = GetSysColor(COLOR_WINDOW);
3267 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3268 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3269 RECT rectText;
3270 RECT rectIcon;
3271 SHFILEINFOW sfi;
3272 HIMAGELIST ilItemImage;
3273 int iIndentation;
3274 TEXTMETRICW tm;
3275 LPSFOLDER tmpFolder;
3276 LookInInfos *liInfos = GetPropA(pDIStruct->hwndItem,LookInInfosStr);
3277 UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3278 UINT icon_width, icon_height;
3280 TRACE("\n");
3282 if(pDIStruct->itemID == -1)
3283 return 0;
3285 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3286 pDIStruct->itemID)))
3287 return 0;
3290 icon_width = GetSystemMetrics(SM_CXICON);
3291 icon_height = GetSystemMetrics(SM_CYICON);
3292 if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3294 icon_width = GetSystemMetrics(SM_CXSMICON);
3295 icon_height = GetSystemMetrics(SM_CYSMICON);
3296 shgfi_flags |= SHGFI_SMALLICON;
3299 if(pDIStruct->itemID == liInfos->uSelectedItem)
3301 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3302 0, &sfi, sizeof (sfi), shgfi_flags );
3304 else
3306 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3307 0, &sfi, sizeof (sfi), shgfi_flags );
3310 /* Is this item selected ? */
3311 if(pDIStruct->itemState & ODS_SELECTED)
3313 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3314 SetBkColor(pDIStruct->hDC,crHighLight);
3315 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3317 else
3319 SetTextColor(pDIStruct->hDC,crText);
3320 SetBkColor(pDIStruct->hDC,crWin);
3321 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3324 /* Do not indent item if drawing in the edit of the combo */
3325 if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3327 iIndentation = 0;
3328 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3329 0, &sfi, sizeof (sfi), shgfi_flags );
3332 else
3334 iIndentation = tmpFolder->m_iIndent;
3336 /* Draw text and icon */
3338 /* Initialise the icon display area */
3339 rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3340 rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3341 rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3342 rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3344 /* Initialise the text display area */
3345 GetTextMetricsW(pDIStruct->hDC, &tm);
3346 rectText.left = rectIcon.right;
3347 rectText.top =
3348 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3349 rectText.right = pDIStruct->rcItem.right;
3350 rectText.bottom =
3351 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3353 /* Draw the icon from the image list */
3354 ImageList_Draw(ilItemImage,
3355 sfi.iIcon,
3356 pDIStruct->hDC,
3357 rectIcon.left,
3358 rectIcon.top,
3359 ILD_TRANSPARENT );
3361 /* Draw the associated text */
3362 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3363 return NOERROR;
3366 /***********************************************************************
3367 * FILEDLG95_LOOKIN_OnCommand
3369 * LookIn combo box WM_COMMAND message handler
3370 * If the function succeeds, the return value is nonzero.
3372 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3374 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3376 TRACE("%p\n", fodInfos);
3378 switch(wNotifyCode)
3380 case CBN_SELENDOK:
3382 LPSFOLDER tmpFolder;
3383 int iItem;
3385 iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3387 if( iItem == CB_ERR) return FALSE;
3389 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3390 iItem)))
3391 return FALSE;
3394 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3395 tmpFolder->pidlItem,
3396 SBSP_ABSOLUTE)))
3398 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3399 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3400 return TRUE;
3402 break;
3406 return FALSE;
3409 /***********************************************************************
3410 * FILEDLG95_LOOKIN_AddItem
3412 * Adds an absolute pidl item to the lookin combo box
3413 * returns the index of the inserted item
3415 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3417 LPITEMIDLIST pidlNext;
3418 SHFILEINFOW sfi;
3419 SFOLDER *tmpFolder;
3420 LookInInfos *liInfos;
3422 TRACE("%08x\n", iInsertId);
3424 if(!pidl)
3425 return -1;
3427 if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3428 return -1;
3430 tmpFolder = MemAlloc(sizeof(SFOLDER));
3431 tmpFolder->m_iIndent = 0;
3433 /* Calculate the indentation of the item in the lookin*/
3434 pidlNext = pidl;
3435 while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3437 tmpFolder->m_iIndent++;
3440 tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3442 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3443 liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3445 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3446 SHGetFileInfoW((LPCWSTR)pidl,
3448 &sfi,
3449 sizeof(sfi),
3450 SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX
3451 | SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3453 TRACE("-- Add %s attr=%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3455 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3457 int iItemID;
3459 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3461 /* Add the item at the end of the list */
3462 if(iInsertId < 0)
3464 iItemID = CBAddString(hwnd,sfi.szDisplayName);
3466 /* Insert the item at the iInsertId position*/
3467 else
3469 iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3472 CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3473 return iItemID;
3476 COMDLG32_SHFree( tmpFolder->pidlItem );
3477 MemFree( tmpFolder );
3478 return -1;
3482 /***********************************************************************
3483 * FILEDLG95_LOOKIN_InsertItemAfterParent
3485 * Insert an item below its parent
3487 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3490 LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3491 int iParentPos;
3493 TRACE("\n");
3495 if (pidl == pidlParent)
3496 return -1;
3498 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3500 if(iParentPos < 0)
3502 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3505 /* Free pidlParent memory */
3506 COMDLG32_SHFree(pidlParent);
3508 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3511 /***********************************************************************
3512 * FILEDLG95_LOOKIN_SelectItem
3514 * Adds an absolute pidl item to the lookin combo box
3515 * returns the index of the inserted item
3517 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3519 int iItemPos;
3520 LookInInfos *liInfos;
3522 TRACE("\n");
3524 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3526 liInfos = GetPropA(hwnd,LookInInfosStr);
3528 if(iItemPos < 0)
3530 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3531 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3534 else
3536 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3537 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3539 int iRemovedItem;
3541 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3542 break;
3543 if(iRemovedItem < iItemPos)
3544 iItemPos--;
3548 CBSetCurSel(hwnd,iItemPos);
3549 liInfos->uSelectedItem = iItemPos;
3551 return 0;
3555 /***********************************************************************
3556 * FILEDLG95_LOOKIN_RemoveMostExpandedItem
3558 * Remove the item with an expansion level over iExpansionLevel
3560 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3562 int iItemPos;
3563 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3565 TRACE("\n");
3567 if(liInfos->iMaxIndentation <= 2)
3568 return -1;
3570 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3572 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3573 COMDLG32_SHFree(tmpFolder->pidlItem);
3574 MemFree(tmpFolder);
3575 CBDeleteString(hwnd,iItemPos);
3576 liInfos->iMaxIndentation--;
3578 return iItemPos;
3581 return -1;
3584 /***********************************************************************
3585 * FILEDLG95_LOOKIN_SearchItem
3587 * Search for pidl in the lookin combo box
3588 * returns the index of the found item
3590 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3592 int i = 0;
3593 int iCount = CBGetCount(hwnd);
3595 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3597 if (iCount != CB_ERR)
3599 for(;i<iCount;i++)
3601 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3603 if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3604 return i;
3605 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3606 return i;
3610 return -1;
3613 /***********************************************************************
3614 * FILEDLG95_LOOKIN_Clean
3616 * Clean the memory used by the lookin combo box
3618 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3620 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3621 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3622 int iPos;
3623 int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3625 TRACE("\n");
3627 /* Delete each string of the combo and their associated data */
3628 if (iCount != CB_ERR)
3630 for(iPos = iCount-1;iPos>=0;iPos--)
3632 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3633 COMDLG32_SHFree(tmpFolder->pidlItem);
3634 MemFree(tmpFolder);
3635 CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3639 /* LookInInfos structure */
3640 MemFree(liInfos);
3641 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3644 /***********************************************************************
3645 * FILEDLG95_FILENAME_FillFromSelection
3647 * fills the edit box from the cached DataObject
3649 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3651 FileOpenDlgInfos *fodInfos;
3652 LPITEMIDLIST pidl;
3653 UINT nFiles = 0, nFileToOpen, nFileSelected, nLength = 0;
3654 WCHAR lpstrTemp[MAX_PATH];
3655 LPWSTR lpstrAllFile, lpstrCurrFile;
3657 TRACE("\n");
3658 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3660 /* Count how many files we have */
3661 nFileSelected = GetNumSelected( fodInfos->Shell.FOIDataObject );
3663 /* calculate the string length, count files */
3664 if (nFileSelected >= 1)
3666 nLength += 3; /* first and last quotes, trailing \0 */
3667 for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ )
3669 pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 );
3671 if (pidl)
3673 /* get the total length of the selected file names */
3674 lpstrTemp[0] = '\0';
3675 GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp );
3677 if ( ! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl) ) /* Ignore folders */
3679 nLength += lstrlenW( lpstrTemp ) + 3;
3680 nFiles++;
3682 COMDLG32_SHFree( pidl );
3687 /* allocate the buffer */
3688 if (nFiles <= 1) nLength = MAX_PATH;
3689 lpstrAllFile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength * sizeof(WCHAR));
3691 /* Generate the string for the edit control */
3692 if(nFiles >= 1)
3694 lpstrCurrFile = lpstrAllFile;
3695 for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ )
3697 pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 );
3699 if (pidl)
3701 /* get the file name */
3702 lpstrTemp[0] = '\0';
3703 GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp );
3705 if (! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl)) /* Ignore folders */
3707 if ( nFiles > 1)
3709 *lpstrCurrFile++ = '\"';
3710 lstrcpyW( lpstrCurrFile, lpstrTemp );
3711 lpstrCurrFile += lstrlenW( lpstrTemp );
3712 *lpstrCurrFile++ = '\"';
3713 *lpstrCurrFile++ = ' ';
3714 *lpstrCurrFile = 0;
3716 else
3718 lstrcpyW( lpstrAllFile, lpstrTemp );
3721 COMDLG32_SHFree( pidl );
3724 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrAllFile );
3726 /* Select the file name like Windows does */
3727 if (filename_is_edit( fodInfos ))
3728 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3730 HeapFree(GetProcessHeap(),0, lpstrAllFile );
3734 /* copied from shell32 to avoid linking to it
3735 * Although shell32 is already linked the behaviour of exported StrRetToStrN
3736 * is dependent on whether emulated OS is unicode or not.
3738 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3740 switch (src->uType)
3742 case STRRET_WSTR:
3743 lstrcpynW(dest, src->u.pOleStr, len);
3744 COMDLG32_SHFree(src->u.pOleStr);
3745 break;
3747 case STRRET_CSTR:
3748 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3749 dest[len-1] = 0;
3750 break;
3752 case STRRET_OFFSET:
3753 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3754 dest[len-1] = 0;
3755 break;
3757 default:
3758 FIXME("unknown type %x!\n", src->uType);
3759 if (len) *dest = '\0';
3760 return E_FAIL;
3762 return S_OK;
3765 /***********************************************************************
3766 * FILEDLG95_FILENAME_GetFileNames
3768 * Copies the filenames to a delimited string list.
3770 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3772 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3773 UINT nFileCount = 0; /* number of files */
3774 UINT nStrLen = 0; /* length of string in edit control */
3775 LPWSTR lpstrEdit; /* buffer for string from edit control */
3777 TRACE("\n");
3779 /* get the filenames from the filename control */
3780 nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
3781 lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3782 GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
3784 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3786 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3787 MemFree(lpstrEdit);
3788 return nFileCount;
3791 #define SETDefFormatEtc(fe,cf,med) \
3793 (fe).cfFormat = cf;\
3794 (fe).dwAspect = DVASPECT_CONTENT; \
3795 (fe).ptd =NULL;\
3796 (fe).tymed = med;\
3797 (fe).lindex = -1;\
3801 * DATAOBJECT Helper functions
3804 /***********************************************************************
3805 * COMCTL32_ReleaseStgMedium
3807 * like ReleaseStgMedium from ole32
3809 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3811 if(medium.pUnkForRelease)
3813 IUnknown_Release(medium.pUnkForRelease);
3815 else
3817 GlobalUnlock(medium.u.hGlobal);
3818 GlobalFree(medium.u.hGlobal);
3822 /***********************************************************************
3823 * GetPidlFromDataObject
3825 * Return pidl(s) by number from the cached DataObject
3827 * nPidlIndex=0 gets the fully qualified root path
3829 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3832 STGMEDIUM medium;
3833 FORMATETC formatetc;
3834 LPITEMIDLIST pidl = NULL;
3836 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3838 if (!doSelected)
3839 return NULL;
3841 /* Set the FORMATETC structure*/
3842 SETDefFormatEtc(formatetc, RegisterClipboardFormatA(CFSTR_SHELLIDLISTA), TYMED_HGLOBAL);
3844 /* Get the pidls from IDataObject */
3845 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3847 LPIDA cida = GlobalLock(medium.u.hGlobal);
3848 if(nPidlIndex <= cida->cidl)
3850 pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3852 COMCTL32_ReleaseStgMedium(medium);
3854 return pidl;
3857 /***********************************************************************
3858 * GetNumSelected
3860 * Return the number of selected items in the DataObject.
3863 static UINT GetNumSelected( IDataObject *doSelected )
3865 UINT retVal = 0;
3866 STGMEDIUM medium;
3867 FORMATETC formatetc;
3869 TRACE("sv=%p\n", doSelected);
3871 if (!doSelected) return 0;
3873 /* Set the FORMATETC structure*/
3874 SETDefFormatEtc(formatetc, RegisterClipboardFormatA(CFSTR_SHELLIDLISTA), TYMED_HGLOBAL);
3876 /* Get the pidls from IDataObject */
3877 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3879 LPIDA cida = GlobalLock(medium.u.hGlobal);
3880 retVal = cida->cidl;
3881 COMCTL32_ReleaseStgMedium(medium);
3882 return retVal;
3884 return 0;
3888 * TOOLS
3891 /***********************************************************************
3892 * GetName
3894 * Get the pidl's display name (relative to folder) and
3895 * put it in lpstrFileName.
3897 * Return NOERROR on success,
3898 * E_FAIL otherwise
3901 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3903 STRRET str;
3904 HRESULT hRes;
3906 TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3908 if(!lpsf)
3910 SHGetDesktopFolder(&lpsf);
3911 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3912 IShellFolder_Release(lpsf);
3913 return hRes;
3916 /* Get the display name of the pidl relative to the folder */
3917 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3919 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3921 return E_FAIL;
3924 /***********************************************************************
3925 * GetShellFolderFromPidl
3927 * pidlRel is the item pidl relative
3928 * Return the IShellFolder of the absolute pidl
3930 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3932 IShellFolder *psf = NULL,*psfParent;
3934 TRACE("%p\n", pidlAbs);
3936 if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3938 psf = psfParent;
3939 if(pidlAbs && pidlAbs->mkid.cb)
3941 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3943 IShellFolder_Release(psfParent);
3944 return psf;
3947 /* return the desktop */
3948 return psfParent;
3950 return NULL;
3953 /***********************************************************************
3954 * GetParentPidl
3956 * Return the LPITEMIDLIST to the parent of the pidl in the list
3958 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3960 LPITEMIDLIST pidlParent;
3962 TRACE("%p\n", pidl);
3964 pidlParent = COMDLG32_PIDL_ILClone(pidl);
3965 COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3967 return pidlParent;
3970 /***********************************************************************
3971 * GetPidlFromName
3973 * returns the pidl of the file name relative to folder
3974 * NULL if an error occurred
3976 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3978 LPITEMIDLIST pidl = NULL;
3979 ULONG ulEaten;
3981 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3983 if(!lpcstrFileName) return NULL;
3984 if(!*lpcstrFileName) return NULL;
3986 if(!lpsf)
3988 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3989 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3990 IShellFolder_Release(lpsf);
3993 else
3995 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3997 return pidl;
4002 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
4004 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
4005 HRESULT ret;
4007 TRACE("%p, %p\n", psf, pidl);
4009 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
4011 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
4012 /* see documentation shell 4.1*/
4013 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
4016 /***********************************************************************
4017 * BrowseSelectedFolder
4019 static BOOL BrowseSelectedFolder(HWND hwnd)
4021 BOOL bBrowseSelFolder = FALSE;
4022 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
4024 TRACE("\n");
4026 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
4028 LPITEMIDLIST pidlSelection;
4030 /* get the file selected */
4031 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
4032 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
4034 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
4035 pidlSelection, SBSP_RELATIVE ) ) )
4037 static const WCHAR notexist[] = {'P','a','t','h',' ','d','o','e','s',
4038 ' ','n','o','t',' ','e','x','i','s','t',0};
4039 MessageBoxW( hwnd, notexist, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
4041 bBrowseSelFolder = TRUE;
4042 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
4043 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
4045 COMDLG32_SHFree( pidlSelection );
4048 return bBrowseSelFolder;
4052 * Memory allocation methods */
4053 static void *MemAlloc(UINT size)
4055 return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
4058 static void MemFree(void *mem)
4060 HeapFree(GetProcessHeap(),0,mem);
4063 static inline BOOL valid_struct_size( DWORD size )
4065 return (size == OPENFILENAME_SIZE_VERSION_400W) ||
4066 (size == sizeof( OPENFILENAMEW ));
4069 static inline BOOL is_win16_looks(DWORD flags)
4071 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
4072 !(flags & OFN_EXPLORER));
4075 /* ------------------ APIs ---------------------- */
4077 /***********************************************************************
4078 * GetOpenFileNameA (COMDLG32.@)
4080 * Creates a dialog box for the user to select a file to open.
4082 * RETURNS
4083 * TRUE on success: user enters a valid file
4084 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4087 BOOL WINAPI GetOpenFileNameA(
4088 LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
4090 TRACE("flags %08x\n", ofn->Flags);
4092 if (!valid_struct_size( ofn->lStructSize ))
4094 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4095 return FALSE;
4098 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4099 if (ofn->Flags & OFN_FILEMUSTEXIST)
4100 ofn->Flags |= OFN_PATHMUSTEXIST;
4102 if (is_win16_looks(ofn->Flags))
4103 return GetFileName31A(ofn, OPEN_DIALOG);
4104 else
4105 return GetFileDialog95A(ofn, OPEN_DIALOG);
4108 /***********************************************************************
4109 * GetOpenFileNameW (COMDLG32.@)
4111 * Creates a dialog box for the user to select a file to open.
4113 * RETURNS
4114 * TRUE on success: user enters a valid file
4115 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4118 BOOL WINAPI GetOpenFileNameW(
4119 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4121 TRACE("flags %08x\n", ofn->Flags);
4123 if (!valid_struct_size( ofn->lStructSize ))
4125 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4126 return FALSE;
4129 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4130 if (ofn->Flags & OFN_FILEMUSTEXIST)
4131 ofn->Flags |= OFN_PATHMUSTEXIST;
4133 if (is_win16_looks(ofn->Flags))
4134 return GetFileName31W(ofn, OPEN_DIALOG);
4135 else
4136 return GetFileDialog95W(ofn, OPEN_DIALOG);
4140 /***********************************************************************
4141 * GetSaveFileNameA (COMDLG32.@)
4143 * Creates a dialog box for the user to select a file to save.
4145 * RETURNS
4146 * TRUE on success: user enters a valid file
4147 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4150 BOOL WINAPI GetSaveFileNameA(
4151 LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
4153 if (!valid_struct_size( ofn->lStructSize ))
4155 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4156 return FALSE;
4159 if (is_win16_looks(ofn->Flags))
4160 return GetFileName31A(ofn, SAVE_DIALOG);
4161 else
4162 return GetFileDialog95A(ofn, SAVE_DIALOG);
4165 /***********************************************************************
4166 * GetSaveFileNameW (COMDLG32.@)
4168 * Creates a dialog box for the user to select a file to save.
4170 * RETURNS
4171 * TRUE on success: user enters a valid file
4172 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4175 BOOL WINAPI GetSaveFileNameW(
4176 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4178 if (!valid_struct_size( ofn->lStructSize ))
4180 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4181 return FALSE;
4184 if (is_win16_looks(ofn->Flags))
4185 return GetFileName31W(ofn, SAVE_DIALOG);
4186 else
4187 return GetFileDialog95W(ofn, SAVE_DIALOG);
4190 /***********************************************************************
4191 * GetFileTitleA (COMDLG32.@)
4193 * See GetFileTitleW.
4195 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4197 int ret;
4198 UNICODE_STRING strWFile;
4199 LPWSTR lpWTitle;
4201 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4202 lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4203 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4204 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4205 RtlFreeUnicodeString( &strWFile );
4206 RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4207 return ret;
4211 /***********************************************************************
4212 * GetFileTitleW (COMDLG32.@)
4214 * Get the name of a file.
4216 * PARAMS
4217 * lpFile [I] name and location of file
4218 * lpTitle [O] returned file name
4219 * cbBuf [I] buffer size of lpTitle
4221 * RETURNS
4222 * Success: zero
4223 * Failure: negative number.
4225 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4227 int i, len;
4228 static const WCHAR brkpoint[] = {'*','[',']',0};
4229 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4231 if(lpFile == NULL || lpTitle == NULL)
4232 return -1;
4234 len = lstrlenW(lpFile);
4236 if (len == 0)
4237 return -1;
4239 if(strpbrkW(lpFile, brkpoint))
4240 return -1;
4242 len--;
4244 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4245 return -1;
4247 for(i = len; i >= 0; i--)
4249 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':')
4251 i++;
4252 break;
4256 if(i == -1)
4257 i++;
4259 TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4261 len = lstrlenW(lpFile+i)+1;
4262 if(cbBuf < len)
4263 return len;
4265 lstrcpyW(lpTitle, &lpFile[i]);
4266 return 0;