xref: /reactos/dll/win32/comdlg32/filedlg.c (revision c2c66aff)
1 /*
2  * COMMDLG - File Open Dialogs Win95 look and feel
3  *
4  * Copyright 1999 Francois Boisvert
5  * Copyright 1999, 2000 Juergen Schmied
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * FIXME: The whole concept of handling unicode is badly broken.
22  *	many hook-messages expect a pointer to a
23  *	OPENFILENAMEA or W structure. With the current architecture
24  *	we would have to convert the beast at every call to a hook.
25  *	we have to find a better solution but it would likely cause
26  *	a complete rewrite after which we should handle the
27  *	OPENFILENAME structure without any converting (jsch).
28  *
29  * FIXME: any hook gets a OPENFILENAMEA structure
30  *
31  * FIXME: CDN_FILEOK is wrong implemented, other CDN_ messages likely too
32  *
33  * FIXME: old style hook messages are not implemented (except FILEOKSTRING)
34  *
35  * FIXME: algorithm for selecting the initial directory is too simple
36  *
37  * FIXME: add to recent docs
38  *
39  * FIXME: flags not implemented: OFN_DONTADDTORECENT,
40  * OFN_NODEREFERENCELINKS, OFN_NOREADONLYRETURN,
41  * OFN_NOTESTFILECREATE, OFN_USEMONIKERS
42  *
43  * FIXME: lCustData for lpfnHook (WM_INITDIALOG)
44  *
45  *
46  */
47 
48 #include "cdlg.h"
49 
50 #include <shellapi.h>
51 
52 #define UNIMPLEMENTED_FLAGS \
53 (OFN_DONTADDTORECENT |\
54 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
55 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
56 
57 #define IsHooked(fodInfos) \
58 	((fodInfos->ofnInfos->Flags & OFN_ENABLEHOOK) && fodInfos->ofnInfos->lpfnHook)
59 /***********************************************************************
60  * Data structure and global variables
61  */
62 typedef struct SFolder
63 {
64   int m_iImageIndex;    /* Index of picture in image list */
65   HIMAGELIST hImgList;
66   int m_iIndent;      /* Indentation index */
67   LPITEMIDLIST pidlItem;  /* absolute pidl of the item */
68 
69 } SFOLDER,*LPSFOLDER;
70 
71 typedef struct tagLookInInfo
72 {
73   int iMaxIndentation;
74   UINT uSelectedItem;
75 } LookInInfos;
76 
77 
78 /***********************************************************************
79  * Defines and global variables
80  */
81 
82 /* Draw item constant */
83 #define XTEXTOFFSET 3
84 
85 /* AddItem flags*/
86 #define LISTEND -1
87 
88 /* SearchItem methods */
89 #define SEARCH_PIDL 1
90 #define SEARCH_EXP  2
91 #define ITEM_NOTFOUND -1
92 
93 /* Undefined windows message sent by CreateViewObject*/
94 #define WM_GETISHELLBROWSER  WM_USER+7
95 
96 /* NOTE
97  * Those macros exist in windowsx.h. However, you can't really use them since
98  * they rely on the UNICODE defines and can't be used inside Wine itself.
99  */
100 
101 /* Combo box macros */
102 #define CBAddString(hwnd,str) \
103     SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)(str));
104 
105 #define CBInsertString(hwnd,str,pos) \
106     SendMessageW(hwnd, CB_INSERTSTRING, (WPARAM)(pos), (LPARAM)(str));
107 
108 #define CBDeleteString(hwnd,pos) \
109     SendMessageW(hwnd, CB_DELETESTRING, (WPARAM)(pos), 0);
110 
111 #define CBSetItemDataPtr(hwnd,iItemId,dataPtr) \
112     SendMessageW(hwnd, CB_SETITEMDATA, (WPARAM)(iItemId), (LPARAM)(dataPtr));
113 
114 #define CBGetItemDataPtr(hwnd,iItemId) \
115     SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
116 
117 #define CBGetLBText(hwnd,iItemId,str) \
118     SendMessageW(hwnd, CB_GETLBTEXT, (WPARAM)(iItemId), (LPARAM)(str));
119 
120 #define CBGetCurSel(hwnd) \
121     SendMessageW(hwnd, CB_GETCURSEL, 0, 0);
122 
123 #define CBSetCurSel(hwnd,pos) \
124     SendMessageW(hwnd, CB_SETCURSEL, (WPARAM)(pos), 0);
125 
126 #define CBGetCount(hwnd) \
127     SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
128 #define CBShowDropDown(hwnd,show) \
129     SendMessageW(hwnd, CB_SHOWDROPDOWN, (WPARAM)(show), 0);
130 #define CBSetItemHeight(hwnd,index,height) \
131     SendMessageW(hwnd, CB_SETITEMHEIGHT, (WPARAM)(index), (LPARAM)(height));
132 
133 #define CBSetExtendedUI(hwnd,flag) \
134     SendMessageW(hwnd, CB_SETEXTENDEDUI, (WPARAM)(flag), 0)
135 
136 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
137 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
138 
139 static const WCHAR LastVisitedMRUW[] =
140     {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
141         'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
142         'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
143         'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
144 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
145 
146 static const WCHAR filedlg_info_propnameW[] = {'F','i','l','e','O','p','e','n','D','l','g','I','n','f','o','s',0};
147 
148 FileOpenDlgInfos *get_filedlg_infoptr(HWND hwnd)
149 {
150     return GetPropW(hwnd, filedlg_info_propnameW);
151 }
152 
153 /***********************************************************************
154  * Prototypes
155  */
156 
157 /* Internal functions used by the dialog */
158 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
159 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
160 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
161 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
162 static BOOL    FILEDLG95_OnOpen(HWND hwnd);
163 static LRESULT FILEDLG95_InitControls(HWND hwnd);
164 static void    FILEDLG95_Clean(HWND hwnd);
165 
166 /* Functions used by the shell navigation */
167 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
168 static BOOL    FILEDLG95_SHELL_UpFolder(HWND hwnd);
169 static BOOL    FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
170 static void    FILEDLG95_SHELL_Clean(HWND hwnd);
171 static BOOL    FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd);
172 
173 /* Functions used by the EDIT box */
174 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
175 
176 /* Functions used by the filetype combo box */
177 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
178 static BOOL    FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
179 static int     FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
180 static void    FILEDLG95_FILETYPE_Clean(HWND hwnd);
181 
182 /* Functions used by the Look In combo box */
183 static void    FILEDLG95_LOOKIN_Init(HWND hwndCombo);
184 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
185 static BOOL    FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
186 static int     FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
187 static int     FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
188 static int     FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
189 static int     FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
190        int     FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
191 static void    FILEDLG95_LOOKIN_Clean(HWND hwnd);
192 
193 /* Functions for dealing with the most-recently-used registry keys */
194 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
195 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
196 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
197 
198 /* Miscellaneous tool functions */
199 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
200 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
201 LPITEMIDLIST  GetParentPidl(LPITEMIDLIST pidl);
202 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
203 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
204 static UINT GetNumSelected( IDataObject *doSelected );
205 static void COMCTL32_ReleaseStgMedium(STGMEDIUM medium);
206 
207 /* Shell memory allocation */
208 static void *MemAlloc(UINT size);
209 static void MemFree(void *mem);
210 
211 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
212 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
213 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
214 static BOOL BrowseSelectedFolder(HWND hwnd);
215 
216 /***********************************************************************
217  *      GetFileName95
218  *
219  * Creates an Open common dialog box that lets the user select
220  * the drive, directory, and the name of a file or set of files to open.
221  *
222  * IN  : The FileOpenDlgInfos structure associated with the dialog
223  * OUT : TRUE on success
224  *       FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
225  */
226 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
227 {
228     LRESULT lRes;
229     void *template;
230     HRSRC hRes;
231     HANDLE hDlgTmpl = 0;
232 
233     /* test for missing functionality */
234     if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
235     {
236       FIXME("Flags 0x%08x not yet implemented\n",
237          fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
238     }
239 
240     /* Create the dialog from a template */
241 
242     if(!(hRes = FindResourceW(COMDLG32_hInstance,MAKEINTRESOURCEW(NEWFILEOPENORD),(LPCWSTR)RT_DIALOG)))
243     {
244         COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
245         return FALSE;
246     }
247     if (!(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes )) ||
248         !(template = LockResource( hDlgTmpl )))
249     {
250         COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
251         return FALSE;
252     }
253 
254     /* msdn: explorer style dialogs permit sizing by default.
255      * The OFN_ENABLESIZING flag is only needed when a hook or
256      * custom template is provided */
257     if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
258             !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
259         fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
260 
261     if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
262     {
263         fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
264         fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
265     }
266 
267     /* old style hook messages */
268     if (IsHooked(fodInfos))
269     {
270       fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
271       fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
272       fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
273       fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
274     }
275 
276     if (fodInfos->unicode)
277       lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
278                                      template,
279                                      fodInfos->ofnInfos->hwndOwner,
280                                      FileOpenDlgProc95,
281                                      (LPARAM) fodInfos);
282     else
283       lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
284                                      template,
285                                      fodInfos->ofnInfos->hwndOwner,
286                                      FileOpenDlgProc95,
287                                      (LPARAM) fodInfos);
288     if (fodInfos->ole_initialized)
289         OleUninitialize();
290 
291     /* Unable to create the dialog */
292     if( lRes == -1)
293         return FALSE;
294 
295     return lRes;
296 }
297 
298 static WCHAR *heap_strdupAtoW(const char *str)
299 {
300     WCHAR *ret;
301     INT len;
302 
303     if (!str)
304         return NULL;
305 
306     len = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
307     ret = MemAlloc(len * sizeof(WCHAR));
308     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
309 
310     return ret;
311 }
312 
313 static void init_filedlg_infoW(OPENFILENAMEW *ofn, FileOpenDlgInfos *info)
314 {
315     INITCOMMONCONTROLSEX icc;
316 
317     /* Initialize ComboBoxEx32 */
318     icc.dwSize = sizeof(icc);
319     icc.dwICC = ICC_USEREX_CLASSES;
320     InitCommonControlsEx(&icc);
321 
322     /* Initialize CommDlgExtendedError() */
323     COMDLG32_SetCommDlgExtendedError(0);
324 
325     memset(info, 0, sizeof(*info));
326 
327     /* Pass in the original ofn */
328     info->ofnInfos = ofn;
329 
330     info->title = ofn->lpstrTitle;
331     info->defext = ofn->lpstrDefExt;
332     info->filter = ofn->lpstrFilter;
333     info->customfilter = ofn->lpstrCustomFilter;
334 
335     if (ofn->lpstrFile)
336     {
337         info->filename = MemAlloc(ofn->nMaxFile * sizeof(WCHAR));
338         lstrcpynW(info->filename, ofn->lpstrFile, ofn->nMaxFile);
339     }
340 
341     if (ofn->lpstrInitialDir)
342     {
343         DWORD len = ExpandEnvironmentStringsW(ofn->lpstrInitialDir, NULL, 0);
344         if (len)
345         {
346             info->initdir = MemAlloc(len * sizeof(WCHAR));
347             ExpandEnvironmentStringsW(ofn->lpstrInitialDir, info->initdir, len);
348         }
349     }
350 
351     info->unicode = TRUE;
352 }
353 
354 static void init_filedlg_infoA(OPENFILENAMEA *ofn, FileOpenDlgInfos *info)
355 {
356     OPENFILENAMEW ofnW;
357     int len;
358 
359     ofnW = *(OPENFILENAMEW *)ofn;
360 
361     ofnW.lpstrInitialDir = heap_strdupAtoW(ofn->lpstrInitialDir);
362     ofnW.lpstrDefExt = heap_strdupAtoW(ofn->lpstrDefExt);
363     ofnW.lpstrTitle = heap_strdupAtoW(ofn->lpstrTitle);
364 
365     if (ofn->lpstrFile)
366     {
367         len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, NULL, 0);
368         ofnW.lpstrFile = MemAlloc(len * sizeof(WCHAR));
369         MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, ofnW.lpstrFile, len);
370         ofnW.nMaxFile = len;
371     }
372 
373     if (ofn->lpstrFilter)
374     {
375         LPCSTR s;
376         int n;
377 
378         /* filter is a list...  title\0ext\0......\0\0 */
379         s = ofn->lpstrFilter;
380         while (*s) s = s+strlen(s)+1;
381         s++;
382         n = s - ofn->lpstrFilter;
383         len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0);
384         ofnW.lpstrFilter = MemAlloc(len * sizeof(WCHAR));
385         MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, (WCHAR *)ofnW.lpstrFilter, len);
386     }
387 
388     /* convert lpstrCustomFilter */
389     if (ofn->lpstrCustomFilter)
390     {
391         int n, len;
392         LPCSTR s;
393 
394         /* customfilter contains a pair of strings...  title\0ext\0 */
395         s = ofn->lpstrCustomFilter;
396         if (*s) s = s+strlen(s)+1;
397         if (*s) s = s+strlen(s)+1;
398         n = s - ofn->lpstrCustomFilter;
399         len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0);
400         ofnW.lpstrCustomFilter = MemAlloc(len * sizeof(WCHAR));
401         MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, ofnW.lpstrCustomFilter, len);
402     }
403 
404     init_filedlg_infoW(&ofnW, info);
405 
406     /* fixup A-specific fields */
407     info->ofnInfos = (OPENFILENAMEW *)ofn;
408     info->unicode = FALSE;
409 
410     /* free what was duplicated */
411     MemFree((WCHAR *)ofnW.lpstrInitialDir);
412     MemFree((WCHAR *)ofnW.lpstrFile);
413 }
414 
415 /***********************************************************************
416  *      GetFileDialog95
417  *
418  * Call GetFileName95 with this structure and clean the memory.
419  */
420 static BOOL GetFileDialog95(FileOpenDlgInfos *info, UINT dlg_type)
421 {
422     WCHAR *current_dir = NULL;
423     BOOL ret;
424 
425     /* save current directory */
426     if (info->ofnInfos->Flags & OFN_NOCHANGEDIR)
427     {
428         current_dir = MemAlloc(MAX_PATH * sizeof(WCHAR));
429         GetCurrentDirectoryW(MAX_PATH, current_dir);
430     }
431 
432     switch (dlg_type)
433     {
434     case OPEN_DIALOG:
435         ret = GetFileName95(info);
436         break;
437     case SAVE_DIALOG:
438         info->DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
439         ret = GetFileName95(info);
440         break;
441     default:
442         ret = FALSE;
443     }
444 
445     /* set the lpstrFileTitle */
446     if (ret && info->ofnInfos->lpstrFile && info->ofnInfos->lpstrFileTitle)
447     {
448         if (info->unicode)
449         {
450             LPOPENFILENAMEW ofn = info->ofnInfos;
451             WCHAR *file_title = PathFindFileNameW(ofn->lpstrFile);
452             lstrcpynW(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
453         }
454         else
455         {
456             LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)info->ofnInfos;
457             char *file_title = PathFindFileNameA(ofn->lpstrFile);
458             lstrcpynA(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
459         }
460     }
461 
462     if (current_dir)
463     {
464         SetCurrentDirectoryW(current_dir);
465         MemFree(current_dir);
466     }
467 
468     if (!info->unicode)
469     {
470         MemFree((WCHAR *)info->defext);
471         MemFree((WCHAR *)info->title);
472         MemFree((WCHAR *)info->filter);
473         MemFree((WCHAR *)info->customfilter);
474     }
475 
476     MemFree(info->filename);
477     MemFree(info->initdir);
478     return ret;
479 }
480 
481 /******************************************************************************
482  * COMDLG32_GetDisplayNameOf [internal]
483  *
484  * Helper function to get the display name for a pidl.
485  */
486 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
487     LPSHELLFOLDER psfDesktop;
488     STRRET strret;
489 
490     if (FAILED(SHGetDesktopFolder(&psfDesktop)))
491         return FALSE;
492 
493     if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
494         IShellFolder_Release(psfDesktop);
495         return FALSE;
496     }
497 
498     IShellFolder_Release(psfDesktop);
499     return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
500 }
501 
502 /******************************************************************************
503  * COMDLG32_GetCanonicalPath [internal]
504  *
505  * Helper function to get the canonical path.
506  */
507 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
508                                LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
509 {
510   WCHAR lpstrTemp[MAX_PATH];
511 
512   /* Get the current directory name */
513   if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
514   {
515     /* last fallback */
516     GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
517   }
518   PathAddBackslashW(lpstrPathAndFile);
519 
520   TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile));
521 
522   /* if the user specified a fully qualified path use it */
523   if(PathIsRelativeW(lpstrFile))
524   {
525     lstrcatW(lpstrPathAndFile, lpstrFile);
526   }
527   else
528   {
529     /* does the path have a drive letter? */
530     if (PathGetDriveNumberW(lpstrFile) == -1)
531       lstrcpyW(lpstrPathAndFile+2, lpstrFile);
532     else
533       lstrcpyW(lpstrPathAndFile, lpstrFile);
534   }
535 
536   /* resolve "." and ".." */
537   PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
538   lstrcpyW(lpstrPathAndFile, lpstrTemp);
539   TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
540 }
541 
542 /***********************************************************************
543  *      COMDLG32_SplitFileNames [internal]
544  *
545  * Creates a delimited list of filenames.
546  */
547 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
548 {
549 	UINT nStrCharCount = 0;	/* index in src buffer */
550 	UINT nFileIndex = 0;	/* index in dest buffer */
551 	UINT nFileCount = 0;	/* number of files */
552 
553 	/* we might get single filename without any '"',
554 	 * so we need nStrLen + terminating \0 + end-of-list \0 */
555 	*lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
556 	*sizeUsed = 0;
557 
558 	/* build delimited file list from filenames */
559 	while ( nStrCharCount <= nStrLen )
560 	{
561 	  if ( lpstrEdit[nStrCharCount]=='"' )
562 	  {
563 	    nStrCharCount++;
564 	    while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
565 	    {
566 	      (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
567 	      nStrCharCount++;
568 	    }
569 	    (*lpstrFileList)[nFileIndex++] = 0;
570 	    nFileCount++;
571 	  }
572 	  nStrCharCount++;
573 	}
574 
575 	/* single, unquoted string */
576 	if ((nStrLen > 0) && (nFileIndex == 0) )
577 	{
578 	  lstrcpyW(*lpstrFileList, lpstrEdit);
579 	  nFileIndex = lstrlenW(lpstrEdit) + 1;
580 	  nFileCount = 1;
581 	}
582 
583         /* trailing \0 */
584         (*lpstrFileList)[nFileIndex++] = '\0';
585 
586         *sizeUsed = nFileIndex;
587 	return nFileCount;
588 }
589 
590 /***********************************************************************
591  *      ArrangeCtrlPositions [internal]
592  *
593  * NOTE: Make sure to add testcases for any changes made here.
594  */
595 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
596 {
597     HWND hwndChild, hwndStc32;
598     RECT rectParent, rectChild, rectStc32;
599     INT help_fixup = 0;
600     int chgx, chgy;
601 
602     /* Take into account if open as read only checkbox and help button
603      * are hidden
604      */
605      if (hide_help)
606      {
607          RECT rectHelp, rectCancel;
608          GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
609          GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
610          /* subtract the height of the help button plus the space between
611           * the help button and the cancel button to the height of the dialog
612           */
613           help_fixup = rectHelp.bottom - rectCancel.bottom;
614     }
615 
616     /*
617       There are two possibilities to add components to the default file dialog box.
618 
619       By default, all the new components are added below the standard dialog box (the else case).
620 
621       However, if there is a static text component with the stc32 id, a special case happens.
622       The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
623       in the window and the cx and cy indicate how to size the window.
624       Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
625       of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
626 
627      */
628 
629     GetClientRect(hwndParentDlg, &rectParent);
630 
631     /* when arranging controls we have to use fixed parent size */
632     rectParent.bottom -= help_fixup;
633 
634     hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
635     if (hwndStc32)
636     {
637         GetWindowRect(hwndStc32, &rectStc32);
638         MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
639 
640         /* set the size of the stc32 control according to the size of
641          * client area of the parent dialog
642          */
643         SetWindowPos(hwndStc32, 0,
644                      0, 0,
645                      rectParent.right, rectParent.bottom,
646                      SWP_NOMOVE | SWP_NOZORDER);
647     }
648     else
649         SetRectEmpty(&rectStc32);
650 
651     /* this part moves controls of the child dialog */
652     hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
653     while (hwndChild)
654     {
655         if (hwndChild != hwndStc32)
656         {
657             GetWindowRect(hwndChild, &rectChild);
658             MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
659 
660             /* move only if stc32 exist */
661             if (hwndStc32 && rectChild.left > rectStc32.right)
662             {
663                 /* move to the right of visible controls of the parent dialog */
664                 rectChild.left += rectParent.right;
665                 rectChild.left -= rectStc32.right;
666             }
667             /* move even if stc32 doesn't exist */
668             if (rectChild.top >= rectStc32.bottom)
669             {
670                 /* move below visible controls of the parent dialog */
671                 rectChild.top += rectParent.bottom;
672                 rectChild.top -= rectStc32.bottom - rectStc32.top;
673             }
674 
675             SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
676                          0, 0, SWP_NOSIZE | SWP_NOZORDER);
677         }
678         hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
679     }
680 
681     /* this part moves controls of the parent dialog */
682     hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
683     while (hwndChild)
684     {
685         if (hwndChild != hwndChildDlg)
686         {
687             GetWindowRect(hwndChild, &rectChild);
688             MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
689 
690             /* left,top of stc32 marks the position of controls
691              * from the parent dialog
692              */
693             rectChild.left += rectStc32.left;
694             rectChild.top += rectStc32.top;
695 
696             SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
697                          0, 0, SWP_NOSIZE | SWP_NOZORDER);
698         }
699         hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
700     }
701 
702     /* calculate the size of the resulting dialog */
703 
704     /* here we have to use original parent size */
705     GetClientRect(hwndParentDlg, &rectParent);
706     GetClientRect(hwndChildDlg, &rectChild);
707     TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
708             wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
709 
710     if (hwndStc32)
711     {
712         /* width */
713         if (rectParent.right > rectStc32.right - rectStc32.left)
714             chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
715         else
716             chgx = rectChild.right - rectParent.right;
717         /* height */
718         if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
719             chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
720         else
721             /* Unconditionally set new dialog
722              * height to that of the child
723              */
724             chgy = rectChild.bottom - rectParent.bottom;
725     }
726     else
727     {
728         chgx = 0;
729         chgy = rectChild.bottom - help_fixup;
730     }
731     /* set the size of the parent dialog */
732     GetWindowRect(hwndParentDlg, &rectParent);
733     SetWindowPos(hwndParentDlg, 0,
734                  0, 0,
735                  rectParent.right - rectParent.left + chgx,
736                  rectParent.bottom - rectParent.top + chgy,
737                  SWP_NOMOVE | SWP_NOZORDER);
738 }
739 
740 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
741 {
742     switch(uMsg) {
743     case WM_INITDIALOG:
744         return TRUE;
745     }
746     return FALSE;
747 }
748 
749 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
750 {
751     LPCVOID template;
752     HRSRC hRes;
753     HANDLE hDlgTmpl = 0;
754     HWND hChildDlg = 0;
755 
756     TRACE("%p, %p\n", fodInfos, hwnd);
757 
758     /*
759      * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
760      * structure's hInstance parameter is not a HINSTANCE, but
761      * instead a pointer to a template resource to use.
762      */
763     if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
764     {
765       HINSTANCE hinst;
766       if (fodInfos->ofnInfos->Flags  & OFN_ENABLETEMPLATEHANDLE)
767       {
768         hinst = COMDLG32_hInstance;
769         if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
770         {
771           COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
772           return NULL;
773         }
774       }
775       else
776       {
777         hinst = fodInfos->ofnInfos->hInstance;
778         if(fodInfos->unicode)
779         {
780             LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
781             hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
782         }
783         else
784         {
785             LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
786             hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
787         }
788         if (!hRes)
789         {
790           COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
791           return NULL;
792         }
793         if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
794             !(template = LockResource( hDlgTmpl )))
795         {
796           COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
797           return NULL;
798     	}
799       }
800       if (fodInfos->unicode)
801           hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
802               IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
803               (LPARAM)fodInfos->ofnInfos);
804       else
805           hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
806               IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
807               (LPARAM)fodInfos->ofnInfos);
808       return hChildDlg;
809     }
810     else if( IsHooked(fodInfos))
811     {
812       RECT rectHwnd;
813       struct  {
814          DLGTEMPLATE tmplate;
815          WORD menu,class,title;
816          } temp;
817       GetClientRect(hwnd,&rectHwnd);
818       temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
819       temp.tmplate.dwExtendedStyle = 0;
820       temp.tmplate.cdit = 0;
821       temp.tmplate.x = 0;
822       temp.tmplate.y = 0;
823       temp.tmplate.cx = 0;
824       temp.tmplate.cy = 0;
825       temp.menu = temp.class = temp.title = 0;
826 
827       hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
828                   hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
829 
830       return hChildDlg;
831     }
832     return NULL;
833 }
834 
835 /***********************************************************************
836 *          SendCustomDlgNotificationMessage
837 *
838 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
839 */
840 
841 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
842 {
843     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwndParentDlg);
844     LRESULT hook_result;
845     OFNOTIFYW ofnNotify;
846 
847     TRACE("%p %d\n", hwndParentDlg, uCode);
848 
849     if (!fodInfos || !fodInfos->DlgInfos.hwndCustomDlg)
850         return 0;
851 
852     TRACE("CALL NOTIFY for %d\n", uCode);
853 
854     ofnNotify.hdr.hwndFrom = hwndParentDlg;
855     ofnNotify.hdr.idFrom = 0;
856     ofnNotify.hdr.code = uCode;
857     ofnNotify.lpOFN = fodInfos->ofnInfos;
858     ofnNotify.pszFile = NULL;
859 
860     if (fodInfos->unicode)
861         hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
862     else
863         hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
864 
865     TRACE("RET NOTIFY retval %#lx\n", hook_result);
866 
867     return hook_result;
868 }
869 
870 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
871 {
872     UINT len, total;
873     WCHAR *p, *buffer;
874     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
875 
876     TRACE("CDM_GETFILEPATH:\n");
877 
878     if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
879         return -1;
880 
881     /* get path and filenames */
882     len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
883     buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
884     COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
885     if (len)
886     {
887         p = buffer + strlenW(buffer);
888         *p++ = '\\';
889         SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
890     }
891     if (fodInfos->unicode)
892     {
893         total = strlenW( buffer) + 1;
894         if (result) lstrcpynW( result, buffer, size );
895         TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
896     }
897     else
898     {
899         total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
900         if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
901         TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
902     }
903     HeapFree( GetProcessHeap(), 0, buffer );
904     return total;
905 }
906 
907 /***********************************************************************
908 *         FILEDLG95_HandleCustomDialogMessages
909 *
910 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
911 */
912 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
913 {
914     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
915     WCHAR lpstrPath[MAX_PATH];
916     INT_PTR retval;
917 
918     if(!fodInfos) return FALSE;
919 
920     switch(uMsg)
921     {
922         case CDM_GETFILEPATH:
923             retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
924             break;
925 
926         case CDM_GETFOLDERPATH:
927             TRACE("CDM_GETFOLDERPATH:\n");
928             COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
929             if (lParam)
930             {
931                 if (fodInfos->unicode)
932                     lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
933                 else
934                     WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
935                                         (LPSTR)lParam, (int)wParam, NULL, NULL);
936             }
937             retval = lstrlenW(lpstrPath) + 1;
938             break;
939 
940         case CDM_GETFOLDERIDLIST:
941             retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
942             if (retval <= wParam)
943                 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
944             break;
945 
946         case CDM_GETSPEC:
947             TRACE("CDM_GETSPEC:\n");
948             retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
949             if (lParam)
950             {
951                 if (fodInfos->unicode)
952                     SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
953                 else
954                     SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
955             }
956             break;
957 
958         case CDM_SETCONTROLTEXT:
959             TRACE("CDM_SETCONTROLTEXT:\n");
960 	    if ( lParam )
961             {
962                 if( fodInfos->unicode )
963 	            SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
964                 else
965 	            SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
966             }
967             retval = TRUE;
968             break;
969 
970         case CDM_HIDECONTROL:
971             /* MSDN states that it should fail for not OFN_EXPLORER case */
972             if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
973             {
974                 HWND control = GetDlgItem( hwnd, wParam );
975                 if (control) ShowWindow( control, SW_HIDE );
976                 retval = TRUE;
977             }
978             else retval = FALSE;
979             break;
980 
981         default:
982             if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
983                 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
984             return FALSE;
985     }
986     SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
987     return TRUE;
988 }
989 
990 /***********************************************************************
991  *          FILEDLG95_OnWMGetMMI
992  *
993  * WM_GETMINMAXINFO message handler for resizable dialogs
994  */
995 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
996 {
997     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
998     if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
999     if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1000     {
1001         mmiptr->ptMinTrackSize = fodInfos->initial_size;
1002     }
1003     return TRUE;
1004 }
1005 
1006 /***********************************************************************
1007  *          FILEDLG95_OnWMSize
1008  *
1009  * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1010  *
1011  * FIXME: this could be made more elaborate. Now use a simple scheme
1012  * where the file view is enlarged and the controls are either moved
1013  * vertically or horizontally to get out of the way. Only the "grip"
1014  * is moved in both directions to stay in the corner.
1015  */
1016 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1017 {
1018     RECT rc, rcview;
1019     int chgx, chgy;
1020     HWND ctrl;
1021     HDWP hdwp;
1022     FileOpenDlgInfos *fodInfos;
1023 
1024     if( wParam != SIZE_RESTORED) return FALSE;
1025     fodInfos = get_filedlg_infoptr(hwnd);
1026     if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1027     /* get the new dialog rectangle */
1028     GetWindowRect( hwnd, &rc);
1029     TRACE("%p, size from %d,%d to %d,%d\n", hwnd, fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1030             rc.right -rc.left, rc.bottom -rc.top);
1031     /* not initialized yet */
1032     if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1033         ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1034              (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1035         return FALSE;
1036     chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1037     chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1038     fodInfos->sizedlg.cx = rc.right - rc.left;
1039     fodInfos->sizedlg.cy = rc.bottom - rc.top;
1040     /* change the size of the view window */
1041     GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1042     MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1043     hdwp = BeginDeferWindowPos( 10);
1044     DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1045             rcview.right - rcview.left + chgx,
1046             rcview.bottom - rcview.top + chgy,
1047             SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1048     /* change position and sizes of the controls */
1049     for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1050     {
1051         int ctrlid = GetDlgCtrlID( ctrl);
1052         GetWindowRect( ctrl, &rc);
1053         MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1054         if( ctrl == fodInfos->DlgInfos.hwndGrip)
1055         {
1056             DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1057                     0, 0,
1058                     SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1059         }
1060         else if( rc.top > rcview.bottom)
1061         {
1062             /* if it was below the shell view
1063              * move to bottom */
1064             switch( ctrlid)
1065             {
1066                 /* file name (edit or comboboxex) and file types combo change also width */
1067                 case edt1:
1068                 case cmb13:
1069                 case cmb1:
1070                     DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1071                             rc.right - rc.left + chgx, rc.bottom - rc.top,
1072                             SWP_NOACTIVATE | SWP_NOZORDER);
1073                     break;
1074                     /* then these buttons must move out of the way */
1075                 case IDOK:
1076                 case IDCANCEL:
1077                 case pshHelp:
1078                     DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1079                             0, 0,
1080                             SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1081                     break;
1082                 default:
1083                 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1084                         0, 0,
1085                         SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1086             }
1087         }
1088         else if( rc.left > rcview.right)
1089         {
1090             /* if it was to the right of the shell view
1091              * move to right */
1092             DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1093                     0, 0,
1094                     SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1095         }
1096         else
1097             /* special cases */
1098         {
1099             switch( ctrlid)
1100             {
1101 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1102                 case IDC_LOOKIN:
1103                     DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1104                             rc.right - rc.left + chgx, rc.bottom - rc.top,
1105                             SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1106                     break;
1107                 case IDC_TOOLBARSTATIC:
1108                 case IDC_TOOLBAR:
1109                     DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1110                             0, 0,
1111                             SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1112                     break;
1113 #endif
1114                 /* not resized in windows. Since wine uses this invisible control
1115                  * to size the browser view it needs to be resized */
1116                 case IDC_SHELLSTATIC:
1117                     DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1118                             rc.right - rc.left + chgx,
1119                             rc.bottom - rc.top + chgy,
1120                             SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1121                     break;
1122             }
1123         }
1124     }
1125     if(fodInfos->DlgInfos.hwndCustomDlg &&
1126         (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1127     {
1128         for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1129                 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1130         {
1131             GetWindowRect( ctrl, &rc);
1132             MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1133             if( rc.top > rcview.bottom)
1134             {
1135                 /* if it was below the shell view
1136                  * move to bottom */
1137                 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1138                         rc.right - rc.left, rc.bottom - rc.top,
1139                         SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1140             }
1141             else if( rc.left > rcview.right)
1142             {
1143                 /* if it was to the right of the shell view
1144                  * move to right */
1145                 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1146                         rc.right - rc.left, rc.bottom - rc.top,
1147                         SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1148             }
1149         }
1150         /* size the custom dialog at the end: some applications do some
1151          * control re-arranging at this point */
1152         GetClientRect(hwnd, &rc);
1153         DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1154             0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1155     }
1156     EndDeferWindowPos( hdwp);
1157     /* should not be needed */
1158     RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1159     return TRUE;
1160 }
1161 
1162 /***********************************************************************
1163  *          FileOpenDlgProc95
1164  *
1165  * File open dialog procedure
1166  */
1167 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1168 {
1169 #if 0
1170   TRACE("%p 0x%04x\n", hwnd, uMsg);
1171 #endif
1172 
1173   switch(uMsg)
1174   {
1175     case WM_INITDIALOG:
1176       {
1177          FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1178          RECT rc, rcstc;
1179          int gripx = GetSystemMetrics( SM_CYHSCROLL);
1180          int gripy = GetSystemMetrics( SM_CYVSCROLL);
1181 
1182          /* Some shell namespace extensions depend on COM being initialized. */
1183          if (SUCCEEDED(OleInitialize(NULL)))
1184              fodInfos->ole_initialized = TRUE;
1185 
1186          SetPropW(hwnd, filedlg_info_propnameW, fodInfos);
1187 
1188          FILEDLG95_InitControls(hwnd);
1189 
1190          if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1191          {
1192              DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1193              DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1194              RECT client, client_adjusted;
1195 
1196              if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1197              {
1198                  style |= WS_SIZEBOX;
1199                  ex_style |= WS_EX_WINDOWEDGE;
1200              }
1201              else
1202                  style &= ~WS_SIZEBOX;
1203              SetWindowLongW(hwnd, GWL_STYLE, style);
1204              SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1205 
1206              GetClientRect( hwnd, &client );
1207              GetClientRect( hwnd, &client_adjusted );
1208              AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1209 
1210              GetWindowRect( hwnd, &rc );
1211              rc.right += client_adjusted.right - client.right;
1212              rc.bottom += client_adjusted.bottom - client.bottom;
1213              SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1214                  SWP_NOZORDER | SWP_NOMOVE);
1215 
1216              GetWindowRect( hwnd, &rc );
1217              fodInfos->DlgInfos.hwndGrip =
1218                  CreateWindowExA( 0, "SCROLLBAR", NULL,
1219                      WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1220                      SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1221                      rc.right - gripx, rc.bottom - gripy,
1222                      gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1223          }
1224 
1225       	 fodInfos->DlgInfos.hwndCustomDlg =
1226      	   CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1227 
1228          FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1229       	 FILEDLG95_FillControls(hwnd, wParam, lParam);
1230 
1231          if( fodInfos->DlgInfos.hwndCustomDlg)
1232              ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1233 
1234          if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1235              SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1236              SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1237          }
1238 
1239          /* if the app has changed the position of the invisible listbox,
1240           * change that of the listview (browser) as well */
1241          GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1242          GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1243          if( !EqualRect( &rc, &rcstc))
1244          {
1245              MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1246              SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1247                      rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1248                      SWP_NOACTIVATE | SWP_NOZORDER);
1249          }
1250 
1251          if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1252          {
1253              GetWindowRect( hwnd, &rc);
1254              fodInfos->sizedlg.cx = rc.right - rc.left;
1255              fodInfos->sizedlg.cy = rc.bottom - rc.top;
1256              fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1257              fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1258              GetClientRect( hwnd, &rc);
1259              SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1260                      rc.right - gripx, rc.bottom - gripy,
1261                      0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1262              /* resize the dialog to the previous invocation */
1263              if( MemDialogSize.cx && MemDialogSize.cy)
1264                  SetWindowPos( hwnd, NULL,
1265                          0, 0, MemDialogSize.cx, MemDialogSize.cy,
1266                          SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1267          }
1268 
1269          if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1270              SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1271 
1272          return 0;
1273        }
1274     case WM_SIZE:
1275       return FILEDLG95_OnWMSize(hwnd, wParam);
1276     case WM_GETMINMAXINFO:
1277       return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1278     case WM_COMMAND:
1279       return FILEDLG95_OnWMCommand(hwnd, wParam);
1280     case WM_DRAWITEM:
1281       {
1282         switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1283         {
1284         case IDC_LOOKIN:
1285           FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1286           return TRUE;
1287         }
1288       }
1289       return FALSE;
1290 
1291     case WM_GETISHELLBROWSER:
1292       return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1293 
1294     case WM_DESTROY:
1295       {
1296           FileOpenDlgInfos * fodInfos = get_filedlg_infoptr(hwnd);
1297           if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1298               MemDialogSize = fodInfos->sizedlg;
1299           RemovePropW(hwnd, filedlg_info_propnameW);
1300           return FALSE;
1301       }
1302     case WM_NOTIFY:
1303     {
1304 	LPNMHDR lpnmh = (LPNMHDR)lParam;
1305 	UINT stringId = -1;
1306 
1307 	/* set up the button tooltips strings */
1308 	if(TTN_GETDISPINFOA == lpnmh->code )
1309 	{
1310 	    LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1311 	    switch(lpnmh->idFrom )
1312 	    {
1313 		/* Up folder button */
1314 		case FCIDM_TB_UPFOLDER:
1315 		    stringId = IDS_UPFOLDER;
1316 		    break;
1317 		/* New folder button */
1318 		case FCIDM_TB_NEWFOLDER:
1319 		    stringId = IDS_NEWFOLDER;
1320 		    break;
1321 		/* List option button */
1322 		case FCIDM_TB_SMALLICON:
1323 		    stringId = IDS_LISTVIEW;
1324 		    break;
1325 		/* Details option button */
1326 		case FCIDM_TB_REPORTVIEW:
1327 		    stringId = IDS_REPORTVIEW;
1328 		    break;
1329 		/* Desktop button */
1330 		case FCIDM_TB_DESKTOP:
1331 		    stringId = IDS_TODESKTOP;
1332 		    break;
1333 		default:
1334 		    stringId = 0;
1335 	    }
1336 	    lpdi->hinst = COMDLG32_hInstance;
1337 	    lpdi->lpszText =  MAKEINTRESOURCEA(stringId);
1338 	}
1339         return FALSE;
1340     }
1341     default :
1342       if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1343         return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1344       return FALSE;
1345   }
1346 }
1347 
1348 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1349 {
1350     return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1351         (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1352 }
1353 
1354 /***********************************************************************
1355  *      FILEDLG95_InitControls
1356  *
1357  * WM_INITDIALOG message handler (before hook notification)
1358  */
1359 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1360 {
1361   BOOL win2000plus = FALSE;
1362   BOOL win98plus   = FALSE;
1363   BOOL handledPath = FALSE;
1364   OSVERSIONINFOW osVi;
1365   static const WCHAR szwSlash[] = { '\\', 0 };
1366   static const WCHAR szwStar[] = { '*',0 };
1367 
1368   static const TBBUTTON tbb[] =
1369   {
1370    {0,                 0,                   TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1371    {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER,   TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1372    {0,                 0,                   TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1373    {VIEW_NEWFOLDER+1,  FCIDM_TB_DESKTOP,    TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1374    {0,                 0,                   TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1375    {VIEW_NEWFOLDER,    FCIDM_TB_NEWFOLDER,  TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1376    {0,                 0,                   TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1377    {VIEW_LIST,         FCIDM_TB_SMALLICON,  TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1378    {VIEW_DETAILS,      FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1379   };
1380   static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1381 
1382   RECT rectTB;
1383   RECT rectlook;
1384 
1385   HIMAGELIST toolbarImageList;
1386   SHFILEINFOA shFileInfo;
1387   ITEMIDLIST *desktopPidl;
1388 
1389   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1390 
1391   TRACE("%p\n", fodInfos);
1392 
1393   /* Get windows version emulating */
1394   osVi.dwOSVersionInfoSize = sizeof(osVi);
1395   GetVersionExW(&osVi);
1396   if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1397     win98plus   = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1398   } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1399     win2000plus = (osVi.dwMajorVersion > 4);
1400     if (win2000plus) win98plus = TRUE;
1401   }
1402   TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1403 
1404 
1405   /* Use either the edit or the comboboxex for the filename control */
1406   if (filename_is_edit( fodInfos ))
1407   {
1408       DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1409       fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1410   }
1411   else
1412   {
1413       DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1414       fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1415   }
1416 
1417   /* Get the hwnd of the controls */
1418   fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1419   fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1420 
1421   GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1422   MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1423 
1424   /* construct the toolbar */
1425   GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1426   MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1427 
1428   rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1429   rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1430   rectTB.left = rectlook.right;
1431   rectTB.top = rectlook.top-1;
1432 
1433   if (fodInfos->unicode)
1434       fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1435           WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1436           rectTB.left, rectTB.top,
1437           rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1438           hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1439   else
1440       fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1441           WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1442           rectTB.left, rectTB.top,
1443           rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1444           hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1445 
1446   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1447 
1448 /* FIXME: use TB_LOADIMAGES when implemented */
1449 /*  SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1450   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1451   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1452 
1453   /* Retrieve and add desktop icon to the toolbar */
1454   toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1455   SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1456   SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1457     SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1458   ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1459 
1460   DestroyIcon(shFileInfo.hIcon);
1461   CoTaskMemFree(desktopPidl);
1462 
1463   /* Finish Toolbar Construction */
1464   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1465   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1466 
1467   /* Set the window text with the text specified in the OPENFILENAME structure */
1468   if(fodInfos->title)
1469   {
1470       SetWindowTextW(hwnd,fodInfos->title);
1471   }
1472   else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1473   {
1474       WCHAR buf[64];
1475       LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1476       SetWindowTextW(hwnd, buf);
1477   }
1478 
1479   /* Initialise the file name edit control */
1480   handledPath = FALSE;
1481   TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1482 
1483   if(fodInfos->filename)
1484   {
1485       /* 1. If win2000 or higher and filename contains a path, use it
1486          in preference over the lpstrInitialDir                       */
1487       if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1488          WCHAR tmpBuf[MAX_PATH];
1489          WCHAR *nameBit;
1490          DWORD result;
1491 
1492          result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1493          if (result) {
1494 
1495             /* nameBit is always shorter than the original filename. It may be NULL
1496              * when the filename contains only a drive name instead of file name */
1497             if (nameBit)
1498             {
1499                 lstrcpyW(fodInfos->filename,nameBit);
1500                 *nameBit = 0x00;
1501             }
1502             else
1503                 *fodInfos->filename = '\0';
1504 
1505             MemFree(fodInfos->initdir);
1506             fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1507             lstrcpyW(fodInfos->initdir, tmpBuf);
1508             handledPath = TRUE;
1509             TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1510                     debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1511          }
1512          SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1513 
1514       } else {
1515          SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1516       }
1517   }
1518 
1519   /* 2. (All platforms) If initdir is not null, then use it */
1520   if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1521   {
1522         /* Work out the proper path as supplied one might be relative          */
1523         /* (Here because supplying '.' as dir browses to My Computer)          */
1524         WCHAR tmpBuf[MAX_PATH];
1525         WCHAR tmpBuf2[MAX_PATH];
1526         WCHAR *nameBit;
1527         DWORD result;
1528 
1529         lstrcpyW(tmpBuf, fodInfos->initdir);
1530         if (PathFileExistsW(tmpBuf)) {
1531             /* initdir does not have to be a directory. If a file is
1532              * specified, the dir part is taken */
1533             if (PathIsDirectoryW(tmpBuf)) {
1534                 PathAddBackslashW(tmpBuf);
1535                 lstrcatW(tmpBuf, szwStar);
1536             }
1537             result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1538             if (result) {
1539                 *nameBit = 0x00;
1540                 MemFree(fodInfos->initdir);
1541                 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1542                 lstrcpyW(fodInfos->initdir, tmpBuf2);
1543                 handledPath = TRUE;
1544                 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1545             }
1546         }
1547         else if (fodInfos->initdir)
1548         {
1549             MemFree(fodInfos->initdir);
1550             fodInfos->initdir = NULL;
1551             TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1552         }
1553   }
1554 
1555   if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1556   {
1557       /* 3. All except w2k+: if filename contains a path use it */
1558       if (!win2000plus && fodInfos->filename &&
1559           *fodInfos->filename &&
1560           strpbrkW(fodInfos->filename, szwSlash)) {
1561          WCHAR tmpBuf[MAX_PATH];
1562          WCHAR *nameBit;
1563          DWORD result;
1564 
1565          result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1566                                   tmpBuf, &nameBit);
1567          if (result) {
1568             int len;
1569 
1570             /* nameBit is always shorter than the original filename */
1571             lstrcpyW(fodInfos->filename, nameBit);
1572             *nameBit = 0x00;
1573 
1574             len = lstrlenW(tmpBuf);
1575             MemFree(fodInfos->initdir);
1576             fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1577             lstrcpyW(fodInfos->initdir, tmpBuf);
1578 
1579             handledPath = TRUE;
1580             TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1581                  debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1582          }
1583          SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1584       }
1585 
1586       /* 4. Win2000+: Recently used */
1587       if (!handledPath && win2000plus) {
1588           fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1589           fodInfos->initdir[0] = '\0';
1590 
1591           FILEDLG95_MRU_load_filename(fodInfos->initdir);
1592 
1593           if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1594              handledPath = TRUE;
1595           }else{
1596              MemFree(fodInfos->initdir);
1597              fodInfos->initdir = NULL;
1598           }
1599       }
1600 
1601       /* 5. win98+ and win2000+ if any files of specified filter types in
1602             current directory, use it                                      */
1603       if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1604 
1605          LPCWSTR lpstrPos = fodInfos->filter;
1606          WIN32_FIND_DATAW FindFileData;
1607          HANDLE hFind;
1608 
1609          while (1)
1610          {
1611            /* filter is a list...  title\0ext\0......\0\0 */
1612 
1613            /* Skip the title */
1614            if(! *lpstrPos) break;	/* end */
1615            lpstrPos += lstrlenW(lpstrPos) + 1;
1616 
1617            /* See if any files exist in the current dir with this extension */
1618            if(! *lpstrPos) break;	/* end */
1619 
1620            hFind = FindFirstFileW(lpstrPos, &FindFileData);
1621 
1622            if (hFind == INVALID_HANDLE_VALUE) {
1623                /* None found - continue search */
1624                lpstrPos += lstrlenW(lpstrPos) + 1;
1625 
1626            } else {
1627 
1628                MemFree(fodInfos->initdir);
1629                fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1630                GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1631 
1632                handledPath = TRUE;
1633                TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1634                  debugstr_w(lpstrPos));
1635                FindClose(hFind);
1636                break;
1637            }
1638          }
1639       }
1640 
1641       /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1642       if (!handledPath && (win2000plus || win98plus)) {
1643           fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1644 
1645           if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1646           {
1647             if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1648             {
1649                 /* last fallback */
1650                 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1651                 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1652             } else {
1653                 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1654             }
1655           } else {
1656             TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1657           }
1658           handledPath = TRUE;
1659       } else if (!handledPath) {
1660           fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1661           GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1662           handledPath = TRUE;
1663           TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1664       }
1665   }
1666   SetFocus( fodInfos->DlgInfos.hwndFileName );
1667   TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1668 
1669   /* Must the open as read only check box be checked ?*/
1670   if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1671   {
1672     SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1673   }
1674 
1675   /* Must the open as read only check box be hidden? */
1676   if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1677   {
1678     ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1679     EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1680   }
1681 
1682   /* Must the help button be hidden? */
1683   if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1684   {
1685     ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1686     EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1687   }
1688 
1689   /* change Open to Save */
1690   if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1691   {
1692       WCHAR buf[16];
1693       LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1694       SetDlgItemTextW(hwnd, IDOK, buf);
1695       LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1696       SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1697   }
1698 
1699   /* Initialize the filter combo box */
1700   FILEDLG95_FILETYPE_Init(hwnd);
1701 
1702   return 0;
1703 }
1704 
1705 /***********************************************************************
1706  *      FILEDLG95_ResizeControls
1707  *
1708  * WM_INITDIALOG message handler (after hook notification)
1709  */
1710 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1711 {
1712   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1713 
1714   if (fodInfos->DlgInfos.hwndCustomDlg)
1715   {
1716     RECT rc;
1717     UINT flags = SWP_NOACTIVATE;
1718 
1719     ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1720         (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1721 
1722     /* resize the custom dialog to the parent size */
1723     if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1724       GetClientRect(hwnd, &rc);
1725     else
1726     {
1727       /* our own fake template is zero sized and doesn't have children, so
1728        * there is no need to resize it. Picasa depends on it.
1729        */
1730       flags |= SWP_NOSIZE;
1731       SetRectEmpty(&rc);
1732     }
1733     SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1734                  0, 0, rc.right, rc.bottom, flags);
1735   }
1736   else
1737   {
1738     /* Resize the height; if opened as read-only, checkbox and help button are
1739      * hidden and we are not using a custom template nor a customDialog
1740      */
1741     if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1742                 (!(fodInfos->ofnInfos->Flags &
1743                    (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1744     {
1745       RECT rectDlg, rectHelp, rectCancel;
1746       GetWindowRect(hwnd, &rectDlg);
1747       GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1748       GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1749       /* subtract the height of the help button plus the space between the help
1750        * button and the cancel button to the height of the dialog
1751        */
1752       SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1753           (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1754           SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1755     }
1756   }
1757   return TRUE;
1758 }
1759 
1760 /***********************************************************************
1761  *      FILEDLG95_FillControls
1762  *
1763  * WM_INITDIALOG message handler (after hook notification)
1764  */
1765 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1766 {
1767   LPITEMIDLIST pidlItemId = NULL;
1768 
1769   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1770 
1771   TRACE("dir=%s file=%s\n",
1772   debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1773 
1774   /* Get the initial directory pidl */
1775 
1776   if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1777   {
1778     WCHAR path[MAX_PATH];
1779 
1780     GetCurrentDirectoryW(MAX_PATH,path);
1781     pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1782   }
1783 
1784   /* Initialise shell objects */
1785   FILEDLG95_SHELL_Init(hwnd);
1786 
1787   /* Initialize the Look In combo box */
1788   FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1789 
1790   /* Browse to the initial directory */
1791   IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1792 
1793   /* Free pidlItem memory */
1794   COMDLG32_SHFree(pidlItemId);
1795 
1796   return TRUE;
1797 }
1798 /***********************************************************************
1799  *      FILEDLG95_Clean
1800  *
1801  * Regroups all the cleaning functions of the filedlg
1802  */
1803 void FILEDLG95_Clean(HWND hwnd)
1804 {
1805       FILEDLG95_FILETYPE_Clean(hwnd);
1806       FILEDLG95_LOOKIN_Clean(hwnd);
1807       FILEDLG95_SHELL_Clean(hwnd);
1808 }
1809 /***********************************************************************
1810  *      FILEDLG95_OnWMCommand
1811  *
1812  * WM_COMMAND message handler
1813  */
1814 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1815 {
1816   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1817   WORD wNotifyCode = HIWORD(wParam); /* notification code */
1818   WORD wID = LOWORD(wParam);         /* item, control, or accelerator identifier */
1819 
1820   switch(wID)
1821   {
1822     /* OK button */
1823   case IDOK:
1824     FILEDLG95_OnOpen(hwnd);
1825     break;
1826     /* Cancel button */
1827   case IDCANCEL:
1828     FILEDLG95_Clean(hwnd);
1829     EndDialog(hwnd, FALSE);
1830     break;
1831     /* Filetype combo box */
1832   case IDC_FILETYPE:
1833     FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1834     break;
1835     /* LookIn combo box */
1836   case IDC_LOOKIN:
1837     FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1838     break;
1839 
1840   /* --- toolbar --- */
1841     /* Up folder button */
1842   case FCIDM_TB_UPFOLDER:
1843     FILEDLG95_SHELL_UpFolder(hwnd);
1844     break;
1845     /* New folder button */
1846   case FCIDM_TB_NEWFOLDER:
1847     FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1848     break;
1849     /* List option button */
1850   case FCIDM_TB_SMALLICON:
1851     FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1852     break;
1853     /* Details option button */
1854   case FCIDM_TB_REPORTVIEW:
1855     FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1856     break;
1857     /* Details option button */
1858   case FCIDM_TB_DESKTOP:
1859     FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1860     break;
1861 
1862   case edt1:
1863   case cmb13:
1864     break;
1865 
1866   }
1867   /* Do not use the listview selection anymore */
1868   fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1869   return 0;
1870 }
1871 
1872 /***********************************************************************
1873  *      FILEDLG95_OnWMGetIShellBrowser
1874  *
1875  * WM_GETISHELLBROWSER message handler
1876  */
1877 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1878 {
1879   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1880 
1881   TRACE("\n");
1882 
1883   SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1884 
1885   return TRUE;
1886 }
1887 
1888 
1889 /***********************************************************************
1890  *      FILEDLG95_SendFileOK
1891  *
1892  * Sends the CDN_FILEOK notification if required
1893  *
1894  * RETURNS
1895  *  TRUE if the dialog should close
1896  *  FALSE if the dialog should not be closed
1897  */
1898 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1899 {
1900     /* ask the hook if we can close */
1901     if(IsHooked(fodInfos))
1902     {
1903         LRESULT retval = 0;
1904 
1905         TRACE("---\n");
1906         /* First send CDN_FILEOK as MSDN doc says */
1907         if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1908             retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1909         if( retval)
1910         {
1911             TRACE("canceled\n");
1912             return FALSE;
1913         }
1914 
1915         /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1916         retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1917                               fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1918         if( retval)
1919         {
1920             TRACE("canceled\n");
1921             return FALSE;
1922         }
1923     }
1924     return TRUE;
1925 }
1926 
1927 /***********************************************************************
1928  *      FILEDLG95_OnOpenMultipleFiles
1929  *
1930  * Handles the opening of multiple files.
1931  *
1932  * FIXME
1933  *  check destination buffer size
1934  */
1935 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
1936 {
1937   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1938   WCHAR   lpstrPathSpec[MAX_PATH] = {0};
1939   UINT   nCount, nSizePath;
1940 
1941   TRACE("\n");
1942 
1943   if(fodInfos->unicode)
1944   {
1945      LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1946      ofn->lpstrFile[0] = '\0';
1947   }
1948   else
1949   {
1950      LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
1951      ofn->lpstrFile[0] = '\0';
1952   }
1953 
1954   COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
1955 
1956   if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
1957       ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
1958        ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
1959   {
1960     LPWSTR lpstrTemp = lpstrFileList;
1961 
1962     for ( nCount = 0; nCount < nFileCount; nCount++ )
1963     {
1964       LPITEMIDLIST pidl;
1965 
1966       pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
1967       if (!pidl)
1968       {
1969         WCHAR lpstrNotFound[100];
1970         WCHAR lpstrMsg[100];
1971         WCHAR tmp[400];
1972         static const WCHAR nl[] = {'\n',0};
1973 
1974         LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
1975         LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
1976 
1977         lstrcpyW(tmp, lpstrTemp);
1978         lstrcatW(tmp, nl);
1979         lstrcatW(tmp, lpstrNotFound);
1980         lstrcatW(tmp, nl);
1981         lstrcatW(tmp, lpstrMsg);
1982 
1983         MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
1984         return FALSE;
1985       }
1986 
1987       /* move to the next file in the list of files */
1988       lpstrTemp += lstrlenW(lpstrTemp) + 1;
1989       COMDLG32_SHFree(pidl);
1990     }
1991   }
1992 
1993   nSizePath = lstrlenW(lpstrPathSpec) + 1;
1994   if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
1995   {
1996     /* For "oldstyle" dialog the components have to
1997        be separated by blanks (not '\0'!) and short
1998        filenames have to be used! */
1999     FIXME("Components have to be separated by blanks\n");
2000   }
2001   if(fodInfos->unicode)
2002   {
2003     LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2004     lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2005     memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2006   }
2007   else
2008   {
2009     LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2010 
2011     if (ofn->lpstrFile != NULL)
2012     {
2013       nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2014 			  ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2015       if (ofn->nMaxFile > nSizePath)
2016       {
2017 	WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2018 			    ofn->lpstrFile + nSizePath,
2019 			    ofn->nMaxFile - nSizePath, NULL, NULL);
2020       }
2021     }
2022   }
2023 
2024   fodInfos->ofnInfos->nFileOffset = nSizePath;
2025   fodInfos->ofnInfos->nFileExtension = 0;
2026 
2027   if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2028     return FALSE;
2029 
2030   /* clean and exit */
2031   FILEDLG95_Clean(hwnd);
2032   return EndDialog(hwnd,TRUE);
2033 }
2034 
2035 /* Returns the 'slot name' of the given module_name in the registry's
2036  * most-recently-used list.  This will be an ASCII value in the
2037  * range ['a','z'). Returns zero on error.
2038  *
2039  * The slot's value in the registry has the form:
2040  *   module_name\0mru_path\0
2041  *
2042  * If stored_path is given, then stored_path will contain the path name
2043  * stored in the registry's MRU list for the given module_name.
2044  *
2045  * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2046  * MRU list key for the given module_name.
2047  */
2048 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2049 {
2050     WCHAR mru_list[32], *cur_mru_slot;
2051     BOOL taken[25] = {0};
2052     DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2053     HKEY hkey_tmp, *hkey;
2054     LONG ret;
2055 
2056     if(hkey_ret)
2057         hkey = hkey_ret;
2058     else
2059         hkey = &hkey_tmp;
2060 
2061     if(stored_path)
2062         *stored_path = '\0';
2063 
2064     ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2065     if(ret){
2066         WARN("Unable to create MRU key: %d\n", ret);
2067         return 0;
2068     }
2069 
2070     ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2071             (LPBYTE)mru_list, &mru_list_size);
2072     if(ret || key_type != REG_SZ){
2073         if(ret == ERROR_FILE_NOT_FOUND)
2074             return 'a';
2075 
2076         WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2077         RegCloseKey(*hkey);
2078         return 0;
2079     }
2080 
2081     for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2082         WCHAR value_data[MAX_PATH], value_name[2] = {0};
2083         DWORD value_data_size = sizeof(value_data);
2084 
2085         *value_name = *cur_mru_slot;
2086 
2087         ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2088                 &key_type, (LPBYTE)value_data, &value_data_size);
2089         if(ret || key_type != REG_BINARY){
2090             WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2091             continue;
2092         }
2093 
2094         if(!strcmpiW(module_name, value_data)){
2095             if(!hkey_ret)
2096                 RegCloseKey(*hkey);
2097             if(stored_path)
2098                 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2099             return *value_name;
2100         }
2101     }
2102 
2103     if(!hkey_ret)
2104         RegCloseKey(*hkey);
2105 
2106     /* the module name isn't in the registry, so find the next open slot */
2107     for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2108         taken[*cur_mru_slot - 'a'] = TRUE;
2109     for(i = 0; i < 25; ++i){
2110         if(!taken[i])
2111             return i + 'a';
2112     }
2113 
2114     /* all slots are taken, so return the last one in MRUList */
2115     --cur_mru_slot;
2116     return *cur_mru_slot;
2117 }
2118 
2119 /* save the given filename as most-recently-used path for this module */
2120 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2121 {
2122     WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2123     LONG ret;
2124     HKEY hkey;
2125 
2126     /* get the current executable's name */
2127     if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2128         WARN("GotModuleFileName failed: %d\n", GetLastError());
2129         return;
2130     }
2131     module_name = strrchrW(module_path, '\\');
2132     if(!module_name)
2133         module_name = module_path;
2134     else
2135         module_name += 1;
2136 
2137     slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2138     if(!slot)
2139         return;
2140     *slot_name = slot;
2141 
2142     { /* update the slot's info */
2143         WCHAR *path_ends, *final;
2144         DWORD path_len, final_len;
2145 
2146         /* use only the path segment of `filename' */
2147         path_ends = strrchrW(filename, '\\');
2148         path_len = path_ends - filename;
2149 
2150         final_len = path_len + lstrlenW(module_name) + 2;
2151 
2152         final = MemAlloc(final_len * sizeof(WCHAR));
2153         if(!final)
2154             return;
2155         lstrcpyW(final, module_name);
2156         memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2157         final[final_len-1] = '\0';
2158 
2159         ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2160                 final_len * sizeof(WCHAR));
2161         if(ret){
2162             WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2163             MemFree(final);
2164             RegCloseKey(hkey);
2165             return;
2166         }
2167 
2168         MemFree(final);
2169     }
2170 
2171     { /* update MRUList value */
2172         WCHAR old_mru_list[32], new_mru_list[32];
2173         WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2174         DWORD mru_list_size = sizeof(old_mru_list), key_type;
2175 
2176         ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2177                 (LPBYTE)old_mru_list, &mru_list_size);
2178         if(ret || key_type != REG_SZ){
2179             if(ret == ERROR_FILE_NOT_FOUND){
2180                 new_mru_list[0] = slot;
2181                 new_mru_list[1] = '\0';
2182             }else{
2183                 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2184                 RegCloseKey(hkey);
2185                 return;
2186             }
2187         }else{
2188             /* copy old list data over so that the new slot is at the start
2189              * of the list */
2190             *new_mru_slot++ = slot;
2191             for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2192                 if(*old_mru_slot != slot)
2193                     *new_mru_slot++ = *old_mru_slot;
2194             }
2195             *new_mru_slot = '\0';
2196         }
2197 
2198         ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2199                 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2200         if(ret){
2201             WARN("Error saving MRUList data: %d\n", ret);
2202             RegCloseKey(hkey);
2203             return;
2204         }
2205     }
2206 }
2207 
2208 /* load the most-recently-used path for this module */
2209 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2210 {
2211     WCHAR module_path[MAX_PATH], *module_name;
2212 
2213     /* get the current executable's name */
2214     if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2215         WARN("GotModuleFileName failed: %d\n", GetLastError());
2216         return;
2217     }
2218     module_name = strrchrW(module_path, '\\');
2219     if(!module_name)
2220         module_name = module_path;
2221     else
2222         module_name += 1;
2223 
2224     FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2225     TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2226 }
2227 
2228 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2229 {
2230   WCHAR strMsgTitle[MAX_PATH];
2231   WCHAR strMsgText [MAX_PATH];
2232   if (idCaption)
2233     LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2234   else
2235     strMsgTitle[0] = '\0';
2236   LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2237   MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2238 }
2239 
2240 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2241                                  HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2242 {
2243     int nOpenAction = defAction;
2244     LPWSTR lpszTemp, lpszTemp1;
2245     LPITEMIDLIST pidl = NULL;
2246     static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2247 
2248     /* check for invalid chars */
2249     if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2250     {
2251         FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2252         return FALSE;
2253     }
2254 
2255     if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2256 
2257     lpszTemp1 = lpszTemp = lpstrPathAndFile;
2258     while (lpszTemp1)
2259     {
2260         LPSHELLFOLDER lpsfChild;
2261         WCHAR lpwstrTemp[MAX_PATH];
2262         DWORD dwEaten, dwAttributes;
2263         LPWSTR p;
2264 
2265         lstrcpyW(lpwstrTemp, lpszTemp);
2266         p = PathFindNextComponentW(lpwstrTemp);
2267 
2268         if (!p) break; /* end of path */
2269 
2270         *p = 0;
2271         lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2272 
2273         /* There are no wildcards when OFN_NOVALIDATE is set */
2274         if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2275         {
2276             static const WCHAR wszWild[] = { '*', '?', 0 };
2277             /* if the last element is a wildcard do a search */
2278             if(strpbrkW(lpszTemp1, wszWild) != NULL)
2279             {
2280                 nOpenAction = ONOPEN_SEARCH;
2281                 break;
2282             }
2283         }
2284         lpszTemp1 = lpszTemp;
2285 
2286         TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2287 
2288         /* append a backslash to drive letters */
2289         if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2290            ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2291             (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2292         {
2293             PathAddBackslashW(lpwstrTemp);
2294         }
2295 
2296         dwAttributes = SFGAO_FOLDER;
2297         if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2298         {
2299             /* the path component is valid, we have a pidl of the next path component */
2300             TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2301             if(dwAttributes & SFGAO_FOLDER)
2302             {
2303                 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2304                 {
2305                     ERR("bind to failed\n"); /* should not fail */
2306                     break;
2307                 }
2308                 IShellFolder_Release(*ppsf);
2309                 *ppsf = lpsfChild;
2310                 lpsfChild = NULL;
2311             }
2312             else
2313             {
2314                 TRACE("value\n");
2315 
2316                 /* end dialog, return value */
2317                 nOpenAction = ONOPEN_OPEN;
2318                 break;
2319             }
2320             COMDLG32_SHFree(pidl);
2321             pidl = NULL;
2322         }
2323         else if (!(flags & OFN_NOVALIDATE))
2324         {
2325             if(*lpszTemp ||	/* points to trailing null for last path element */
2326                (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2327             {
2328                 if(flags & OFN_PATHMUSTEXIST)
2329                 {
2330                     FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2331                     break;
2332                 }
2333             }
2334             else
2335             {
2336                 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2337                 {
2338                     FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2339                     break;
2340                 }
2341             }
2342             /* change to the current folder */
2343             nOpenAction = ONOPEN_OPEN;
2344             break;
2345         }
2346         else
2347         {
2348             nOpenAction = ONOPEN_OPEN;
2349             break;
2350         }
2351     }
2352     if(pidl) COMDLG32_SHFree(pidl);
2353 
2354     return nOpenAction;
2355 }
2356 
2357 /***********************************************************************
2358  *      FILEDLG95_OnOpen
2359  *
2360  * Ok button WM_COMMAND message handler
2361  *
2362  * If the function succeeds, the return value is nonzero.
2363  */
2364 BOOL FILEDLG95_OnOpen(HWND hwnd)
2365 {
2366   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2367   LPWSTR lpstrFileList;
2368   UINT nFileCount = 0;
2369   UINT sizeUsed = 0;
2370   BOOL ret = TRUE;
2371   WCHAR lpstrPathAndFile[MAX_PATH];
2372   LPSHELLFOLDER lpsf = NULL;
2373   int nOpenAction;
2374 
2375   TRACE("hwnd=%p\n", hwnd);
2376 
2377   /* try to browse the selected item */
2378   if(BrowseSelectedFolder(hwnd))
2379       return FALSE;
2380 
2381   /* get the files from the edit control */
2382   nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2383 
2384   if(nFileCount == 0)
2385       return FALSE;
2386 
2387   if(nFileCount > 1)
2388   {
2389       ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2390       goto ret;
2391   }
2392 
2393   TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2394 
2395 /*
2396   Step 1:  Build a complete path name from the current folder and
2397   the filename or path in the edit box.
2398   Special cases:
2399   - the path in the edit box is a root path
2400     (with or without drive letter)
2401   - the edit box contains ".." (or a path with ".." in it)
2402 */
2403 
2404   COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2405   MemFree(lpstrFileList);
2406 
2407 /*
2408   Step 2: here we have a cleaned up path
2409 
2410   We have to parse the path step by step to see if we have to browse
2411   to a folder if the path points to a directory or the last
2412   valid element is a directory.
2413 
2414   valid variables:
2415     lpstrPathAndFile: cleaned up path
2416  */
2417 
2418   if (nFileCount &&
2419       (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2420       !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2421     nOpenAction = ONOPEN_OPEN;
2422   else
2423     nOpenAction = ONOPEN_BROWSE;
2424 
2425   nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2426                                              fodInfos->ofnInfos->Flags,
2427                                              fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2428                                              nOpenAction);
2429   if(!nOpenAction)
2430       goto ret;
2431 
2432 /*
2433   Step 3: here we have a cleaned up and validated path
2434 
2435   valid variables:
2436    lpsf:             ShellFolder bound to the rightmost valid path component
2437    lpstrPathAndFile: cleaned up path
2438    nOpenAction:      action to do
2439 */
2440   TRACE("end validate sf=%p\n", lpsf);
2441 
2442   switch(nOpenAction)
2443   {
2444     case ONOPEN_SEARCH:   /* set the current filter to the file mask and refresh */
2445       TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2446       {
2447         int iPos;
2448         LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2449         DWORD len;
2450 
2451         /* replace the current filter */
2452         MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2453         len = lstrlenW(lpszTemp)+1;
2454         fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2455         lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2456 
2457         /* set the filter cb to the extension when possible */
2458         if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2459         CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2460       }
2461       /* fall through */
2462     case ONOPEN_BROWSE:   /* browse to the highest folder we could bind to */
2463       TRACE("ONOPEN_BROWSE\n");
2464       {
2465 	IPersistFolder2 * ppf2;
2466         if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2467         {
2468           LPITEMIDLIST pidlCurrent;
2469           IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2470           IPersistFolder2_Release(ppf2);
2471 	  if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2472 	  {
2473             if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2474                 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2475             {
2476               SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2477               SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2478             }
2479 	  }
2480 	  else if( nOpenAction == ONOPEN_SEARCH )
2481 	  {
2482             if (fodInfos->Shell.FOIShellView)
2483               IShellView_Refresh(fodInfos->Shell.FOIShellView);
2484 	  }
2485           COMDLG32_SHFree(pidlCurrent);
2486           if (filename_is_edit( fodInfos ))
2487               SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2488           else
2489           {
2490               HWND hwnd;
2491 
2492               hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2493               SendMessageW(hwnd, EM_SETSEL, 0, -1);
2494           }
2495         }
2496       }
2497       ret = FALSE;
2498       break;
2499     case ONOPEN_OPEN:   /* fill in the return struct and close the dialog */
2500       TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2501       {
2502         WCHAR *ext = NULL;
2503 
2504         /* update READONLY check box flag */
2505 	if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2506 	  fodInfos->ofnInfos->Flags |= OFN_READONLY;
2507 	else
2508 	  fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2509 
2510         /* Attach the file extension with file name*/
2511         ext = PathFindExtensionW(lpstrPathAndFile);
2512         if (! *ext && fodInfos->defext)
2513         {
2514             /* if no extension is specified with file name, then */
2515             /* attach the extension from file filter or default one */
2516 
2517             WCHAR *filterExt = NULL;
2518             LPWSTR lpstrFilter = NULL;
2519             static const WCHAR szwDot[] = {'.',0};
2520             int PathLength = lstrlenW(lpstrPathAndFile);
2521 
2522             /*Get the file extension from file type filter*/
2523             lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2524                                              fodInfos->ofnInfos->nFilterIndex-1);
2525 
2526             if (lpstrFilter != (LPWSTR)CB_ERR)  /* control is not empty */
2527             {
2528                 WCHAR* filterSearchIndex;
2529                 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2530                 strcpyW(filterExt, lpstrFilter);
2531 
2532                 /* if a semicolon-separated list of file extensions was given, do not include the
2533                    semicolon or anything after it in the extension.
2534                    example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2535                 filterSearchIndex = strchrW(filterExt, ';');
2536                 if (filterSearchIndex)
2537                 {
2538                     filterSearchIndex[0] = '\0';
2539                 }
2540 
2541                 /* find the file extension by searching for the first dot in filterExt */
2542                 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2543                 /* if the extension is invalid or contains a glob, ignore it */
2544                 filterSearchIndex = strchrW(filterExt, '.');
2545                 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2546                 {
2547                     strcpyW(filterExt, filterSearchIndex);
2548                 }
2549                 else
2550                 {
2551                     HeapFree(GetProcessHeap(), 0, filterExt);
2552                     filterExt = NULL;
2553                 }
2554             }
2555 
2556             if (!filterExt)
2557             {
2558                 /* use the default file extension */
2559                 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2560                 strcpyW(filterExt, fodInfos->defext);
2561             }
2562 
2563             if (*filterExt) /* ignore filterExt="" */
2564             {
2565                 /* Attach the dot*/
2566                 lstrcatW(lpstrPathAndFile, szwDot);
2567                 /* Attach the extension */
2568                 lstrcatW(lpstrPathAndFile, filterExt);
2569             }
2570 
2571             HeapFree(GetProcessHeap(), 0, filterExt);
2572 
2573             /* In Open dialog: if file does not exist try without extension */
2574             if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2575                   lpstrPathAndFile[PathLength] = '\0';
2576 
2577             /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2578             if (*ext)
2579                 ext++;
2580             if (!lstrcmpiW(fodInfos->defext, ext))
2581                 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2582             else
2583                 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2584 	}
2585 
2586 	/* In Save dialog: check if the file already exists */
2587 	if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2588 	    && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2589 	    && PathFileExistsW(lpstrPathAndFile))
2590 	{
2591 	  WCHAR lpstrOverwrite[100];
2592 	  int answer;
2593 
2594 	  LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2595 	  answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2596 			       MB_YESNO | MB_ICONEXCLAMATION);
2597 	  if (answer == IDNO || answer == IDCANCEL)
2598 	  {
2599 	    ret = FALSE;
2600 	    goto ret;
2601 	  }
2602 	}
2603 
2604         /* In Open dialog: check if it should be created if it doesn't exist */
2605         if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2606             && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2607             && !PathFileExistsW(lpstrPathAndFile))
2608         {
2609           WCHAR lpstrCreate[100];
2610           int answer;
2611 
2612           LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2613           answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2614                                MB_YESNO | MB_ICONEXCLAMATION);
2615           if (answer == IDNO || answer == IDCANCEL)
2616           {
2617             ret = FALSE;
2618             goto ret;
2619           }
2620         }
2621 
2622         /* Check that the size of the file does not exceed buffer size.
2623              (Allow for extra \0 if OFN_MULTISELECT is set.) */
2624         if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2625             ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2626         {
2627 
2628           /* fill destination buffer */
2629           if (fodInfos->ofnInfos->lpstrFile)
2630           {
2631              if(fodInfos->unicode)
2632              {
2633                LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2634 
2635                lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2636                if (ofn->Flags & OFN_ALLOWMULTISELECT)
2637                  ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2638              }
2639              else
2640              {
2641                LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2642 
2643                WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2644                                    ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2645                if (ofn->Flags & OFN_ALLOWMULTISELECT)
2646                  ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2647              }
2648           }
2649 
2650           if(fodInfos->unicode)
2651           {
2652               LPWSTR lpszTemp;
2653 
2654               /* set filename offset */
2655               lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2656               fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2657 
2658               /* set extension offset */
2659               lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2660               fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2661           }
2662           else
2663           {
2664               LPSTR lpszTemp;
2665               CHAR tempFileA[MAX_PATH];
2666 
2667               /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2668               WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2669                                   tempFileA, sizeof(tempFileA), NULL, NULL);
2670 
2671               /* set filename offset */
2672               lpszTemp = PathFindFileNameA(tempFileA);
2673               fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2674 
2675               /* set extension offset */
2676               lpszTemp = PathFindExtensionA(tempFileA);
2677               fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2678           }
2679 
2680           /* copy currently selected filter to lpstrCustomFilter */
2681           if (fodInfos->ofnInfos->lpstrCustomFilter)
2682           {
2683             LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2684             int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2685                                           NULL, 0, NULL, NULL);
2686             if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2687             {
2688               LPSTR s = ofn->lpstrCustomFilter;
2689               s += strlen(ofn->lpstrCustomFilter)+1;
2690               WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2691                                   s, len, NULL, NULL);
2692             }
2693           }
2694 
2695 
2696           if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2697 	      goto ret;
2698 
2699           FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2700 
2701           TRACE("close\n");
2702 	  FILEDLG95_Clean(hwnd);
2703           ret = EndDialog(hwnd, TRUE);
2704 	}
2705 	else
2706         {
2707           WORD size;
2708 
2709           size = lstrlenW(lpstrPathAndFile) + 1;
2710           if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2711              size += 1;
2712           /* return needed size in first two bytes of lpstrFile */
2713           if(fodInfos->ofnInfos->lpstrFile)
2714               *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2715           FILEDLG95_Clean(hwnd);
2716           ret = EndDialog(hwnd, FALSE);
2717           COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2718         }
2719       }
2720       break;
2721   }
2722 
2723 ret:
2724   if(lpsf) IShellFolder_Release(lpsf);
2725   return ret;
2726 }
2727 
2728 /***********************************************************************
2729  *      FILEDLG95_SHELL_Init
2730  *
2731  * Initialisation of the shell objects
2732  */
2733 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2734 {
2735   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2736 
2737   TRACE("%p\n", hwnd);
2738 
2739   /*
2740    * Initialisation of the FileOpenDialogInfos structure
2741    */
2742 
2743   /* Shell */
2744 
2745   /*ShellInfos */
2746   fodInfos->ShellInfos.hwndOwner = hwnd;
2747 
2748   /* Disable multi-select if flag not set */
2749   if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2750   {
2751      fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2752   }
2753   fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2754   fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2755 
2756   /* Construct the IShellBrowser interface */
2757   fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2758 
2759   return NOERROR;
2760 }
2761 
2762 /***********************************************************************
2763  *      FILEDLG95_SHELL_ExecuteCommand
2764  *
2765  * Change the folder option and refresh the view
2766  * If the function succeeds, the return value is nonzero.
2767  */
2768 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2769 {
2770   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2771   IContextMenu * pcm;
2772 
2773   TRACE("(%p,%p)\n", hwnd, lpVerb);
2774 
2775   if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2776 					SVGIO_BACKGROUND,
2777 					&IID_IContextMenu,
2778 					(LPVOID*)&pcm)))
2779   {
2780     CMINVOKECOMMANDINFO ci;
2781     ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2782     ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2783     ci.lpVerb = lpVerb;
2784     ci.hwnd = hwnd;
2785 
2786     IContextMenu_InvokeCommand(pcm, &ci);
2787     IContextMenu_Release(pcm);
2788   }
2789 
2790   return FALSE;
2791 }
2792 
2793 /***********************************************************************
2794  *      FILEDLG95_SHELL_UpFolder
2795  *
2796  * Browse to the specified object
2797  * If the function succeeds, the return value is nonzero.
2798  */
2799 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2800 {
2801   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2802 
2803   TRACE("\n");
2804 
2805   if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2806                                           NULL,
2807                                           SBSP_PARENT)))
2808   {
2809     if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2810         SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2811     return TRUE;
2812   }
2813   return FALSE;
2814 }
2815 
2816 /***********************************************************************
2817  *      FILEDLG95_SHELL_BrowseToDesktop
2818  *
2819  * Browse to the Desktop
2820  * If the function succeeds, the return value is nonzero.
2821  */
2822 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2823 {
2824   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2825   LPITEMIDLIST pidl;
2826   HRESULT hres;
2827 
2828   TRACE("\n");
2829 
2830   SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2831   hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2832   if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2833       SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2834   COMDLG32_SHFree(pidl);
2835   return SUCCEEDED(hres);
2836 }
2837 /***********************************************************************
2838  *      FILEDLG95_SHELL_Clean
2839  *
2840  * Cleans the memory used by shell objects
2841  */
2842 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2843 {
2844     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2845 
2846     TRACE("\n");
2847 
2848     COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2849 
2850     /* clean Shell interfaces */
2851     if (fodInfos->Shell.FOIShellView)
2852     {
2853       IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2854       IShellView_Release(fodInfos->Shell.FOIShellView);
2855     }
2856     if (fodInfos->Shell.FOIShellFolder)
2857       IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2858     IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2859     if (fodInfos->Shell.FOIDataObject)
2860       IDataObject_Release(fodInfos->Shell.FOIDataObject);
2861 }
2862 
2863 /***********************************************************************
2864  *      FILEDLG95_FILETYPE_Init
2865  *
2866  * Initialisation of the file type combo box
2867  */
2868 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2869 {
2870   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2871   int nFilters = 0;  /* number of filters */
2872   int nFilterIndexCB;
2873 
2874   TRACE("%p\n", hwnd);
2875 
2876   if(fodInfos->customfilter)
2877   {
2878       /* customfilter has one entry...  title\0ext\0
2879        * Set first entry of combo box item with customfilter
2880        */
2881       LPWSTR  lpstrExt;
2882       LPCWSTR lpstrPos = fodInfos->customfilter;
2883 
2884       /* Get the title */
2885       lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2886 
2887       /* Copy the extensions */
2888       if (! *lpstrPos) return E_FAIL;	/* malformed filter */
2889       if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2890       lstrcpyW(lpstrExt,lpstrPos);
2891 
2892       /* Add the item at the end of the combo */
2893       CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2894       CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2895       nFilters++;
2896   }
2897   if(fodInfos->filter)
2898   {
2899     LPCWSTR lpstrPos = fodInfos->filter;
2900 
2901     for(;;)
2902     {
2903       /* filter is a list...  title\0ext\0......\0\0
2904        * Set the combo item text to the title and the item data
2905        *  to the ext
2906        */
2907       LPCWSTR lpstrDisplay;
2908       LPWSTR lpstrExt;
2909 
2910       /* Get the title */
2911       if(! *lpstrPos) break;	/* end */
2912       lpstrDisplay = lpstrPos;
2913       lpstrPos += lstrlenW(lpstrPos) + 1;
2914 
2915       CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2916 
2917       nFilters++;
2918 
2919       /* Copy the extensions */
2920       if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2921       lstrcpyW(lpstrExt,lpstrPos);
2922       lpstrPos += lstrlenW(lpstrPos) + 1;
2923 
2924       /* Add the item at the end of the combo */
2925       CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
2926 
2927       /* malformed filters are added anyway... */
2928       if (!*lpstrExt) break;
2929     }
2930   }
2931 
2932   /*
2933    * Set the current filter to the one specified
2934    * in the initialisation structure
2935    */
2936   if (fodInfos->filter || fodInfos->customfilter)
2937   {
2938     LPWSTR lpstrFilter;
2939 
2940     /* Check to make sure our index isn't out of bounds. */
2941     if ( fodInfos->ofnInfos->nFilterIndex >
2942          nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
2943       fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
2944 
2945     /* set default filter index */
2946     if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
2947       fodInfos->ofnInfos->nFilterIndex = 1;
2948 
2949     /* calculate index of Combo Box item */
2950     nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
2951     if (fodInfos->customfilter == NULL)
2952       nFilterIndexCB--;
2953 
2954     /* Set the current index selection. */
2955     CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
2956 
2957     /* Get the corresponding text string from the combo box. */
2958     lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2959                                              nFilterIndexCB);
2960 
2961     if ((INT_PTR)lpstrFilter == CB_ERR)  /* control is empty */
2962       lpstrFilter = NULL;
2963 
2964     if(lpstrFilter)
2965     {
2966       DWORD len;
2967       CharLowerW(lpstrFilter); /* lowercase */
2968       len = lstrlenW(lpstrFilter)+1;
2969       fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
2970       lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
2971     }
2972   } else
2973       fodInfos->ofnInfos->nFilterIndex = 0;
2974   return S_OK;
2975 }
2976 
2977 /***********************************************************************
2978  *      FILEDLG95_FILETYPE_OnCommand
2979  *
2980  * WM_COMMAND of the file type combo box
2981  * If the function succeeds, the return value is nonzero.
2982  */
2983 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
2984 {
2985   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2986 
2987   switch(wNotifyCode)
2988   {
2989     case CBN_SELENDOK:
2990     {
2991       LPWSTR lpstrFilter;
2992 
2993       /* Get the current item of the filetype combo box */
2994       int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
2995 
2996       /* set the current filter index */
2997       fodInfos->ofnInfos->nFilterIndex = iItem +
2998         (fodInfos->customfilter == NULL ? 1 : 0);
2999 
3000       /* Set the current filter with the current selection */
3001       MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3002 
3003       lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3004                                              iItem);
3005       if((INT_PTR)lpstrFilter != CB_ERR)
3006       {
3007           DWORD len;
3008           CharLowerW(lpstrFilter); /* lowercase */
3009           len = lstrlenW(lpstrFilter)+1;
3010           fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3011           lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3012           if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3013               SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3014       }
3015 
3016       /* Refresh the actual view to display the included items*/
3017       if (fodInfos->Shell.FOIShellView)
3018         IShellView_Refresh(fodInfos->Shell.FOIShellView);
3019     }
3020   }
3021   return FALSE;
3022 }
3023 /***********************************************************************
3024  *      FILEDLG95_FILETYPE_SearchExt
3025  *
3026  * searches for an extension in the filetype box
3027  */
3028 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3029 {
3030   int i, iCount = CBGetCount(hwnd);
3031 
3032   TRACE("%s\n", debugstr_w(lpstrExt));
3033 
3034   if(iCount != CB_ERR)
3035   {
3036     for(i=0;i<iCount;i++)
3037     {
3038       if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3039           return i;
3040     }
3041   }
3042   return -1;
3043 }
3044 
3045 /***********************************************************************
3046  *      FILEDLG95_FILETYPE_Clean
3047  *
3048  * Clean the memory used by the filetype combo box
3049  */
3050 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3051 {
3052   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3053   int iPos;
3054   int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3055 
3056   TRACE("\n");
3057 
3058   /* Delete each string of the combo and their associated data */
3059   if(iCount != CB_ERR)
3060   {
3061     for(iPos = iCount-1;iPos>=0;iPos--)
3062     {
3063       MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3064       CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3065     }
3066   }
3067   /* Current filter */
3068   MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3069 
3070 }
3071 
3072 /***********************************************************************
3073  *      FILEDLG95_LOOKIN_Init
3074  *
3075  * Initialisation of the look in combo box
3076  */
3077 
3078 /* Small helper function, to determine if the unixfs shell extension is rooted
3079  * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3080  */
3081 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3082     HKEY hKey;
3083     static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3084         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3085         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3086         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3087         'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3088         '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3089         '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3090 
3091     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3092         return FALSE;
3093 
3094     RegCloseKey(hKey);
3095     return TRUE;
3096 }
3097 
3098 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3099 {
3100   IShellFolder	*psfRoot, *psfDrives;
3101   IEnumIDList	*lpeRoot, *lpeDrives;
3102   LPITEMIDLIST	pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3103   HDC hdc;
3104   TEXTMETRICW tm;
3105   LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3106 
3107   TRACE("%p\n", hwndCombo);
3108 
3109   liInfos->iMaxIndentation = 0;
3110 
3111   SetPropA(hwndCombo, LookInInfosStr, liInfos);
3112 
3113   hdc = GetDC( hwndCombo );
3114   SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3115   GetTextMetricsW( hdc, &tm );
3116   ReleaseDC( hwndCombo, hdc );
3117 
3118   /* set item height for both text field and listbox */
3119   CBSetItemHeight( hwndCombo, -1, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3120   CBSetItemHeight( hwndCombo, 0, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3121 
3122   /* Turn on the extended UI for the combo box like Windows does */
3123   CBSetExtendedUI(hwndCombo, TRUE);
3124 
3125   /* Initialise data of Desktop folder */
3126   SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3127   FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3128   COMDLG32_SHFree(pidlTmp);
3129 
3130   SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3131 
3132   SHGetDesktopFolder(&psfRoot);
3133 
3134   if (psfRoot)
3135   {
3136     /* enumerate the contents of the desktop */
3137     if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3138     {
3139       while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3140       {
3141 	FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3142 
3143 	/* If the unixfs extension is rooted, we don't expand the drives by default */
3144 	if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3145 	{
3146 	  /* special handling for CSIDL_DRIVES */
3147 	  if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3148 	  {
3149 	    if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3150 	    {
3151 	      /* enumerate the drives */
3152 	      if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3153 	      {
3154 	        while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3155 	        {
3156 	          pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3157 	          FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3158 	          COMDLG32_SHFree(pidlAbsTmp);
3159 	          COMDLG32_SHFree(pidlTmp1);
3160 	        }
3161 	        IEnumIDList_Release(lpeDrives);
3162 	      }
3163 	      IShellFolder_Release(psfDrives);
3164 	    }
3165 	  }
3166 	}
3167 
3168         COMDLG32_SHFree(pidlTmp);
3169       }
3170       IEnumIDList_Release(lpeRoot);
3171     }
3172     IShellFolder_Release(psfRoot);
3173   }
3174 
3175   COMDLG32_SHFree(pidlDrives);
3176 }
3177 
3178 /***********************************************************************
3179  *      FILEDLG95_LOOKIN_DrawItem
3180  *
3181  * WM_DRAWITEM message handler
3182  */
3183 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3184 {
3185   COLORREF crWin = GetSysColor(COLOR_WINDOW);
3186   COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3187   COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3188   RECT rectText;
3189   RECT rectIcon;
3190   SHFILEINFOW sfi;
3191   HIMAGELIST ilItemImage;
3192   int iIndentation;
3193   TEXTMETRICW tm;
3194   LPSFOLDER tmpFolder;
3195   UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3196   UINT icon_width, icon_height;
3197 
3198   TRACE("\n");
3199 
3200   if(pDIStruct->itemID == -1)
3201     return 0;
3202 
3203   if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3204                             pDIStruct->itemID)))
3205     return 0;
3206 
3207 
3208   icon_width = GetSystemMetrics(SM_CXICON);
3209   icon_height = GetSystemMetrics(SM_CYICON);
3210   if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3211   {
3212       icon_width = GetSystemMetrics(SM_CXSMICON);
3213       icon_height = GetSystemMetrics(SM_CYSMICON);
3214       shgfi_flags |= SHGFI_SMALLICON;
3215   }
3216 
3217   ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3218                                              0, &sfi, sizeof (sfi), shgfi_flags );
3219 
3220   /* Is this item selected ? */
3221   if(pDIStruct->itemState & ODS_SELECTED)
3222   {
3223     SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3224     SetBkColor(pDIStruct->hDC,crHighLight);
3225     FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3226   }
3227   else
3228   {
3229     SetTextColor(pDIStruct->hDC,crText);
3230     SetBkColor(pDIStruct->hDC,crWin);
3231     FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3232   }
3233 
3234   /* Do not indent item if drawing in the edit of the combo */
3235   if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3236     iIndentation = 0;
3237   else
3238     iIndentation = tmpFolder->m_iIndent;
3239 
3240   /* Draw text and icon */
3241 
3242   /* Initialise the icon display area */
3243   rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3244   rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3245   rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3246   rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3247 
3248   /* Initialise the text display area */
3249   GetTextMetricsW(pDIStruct->hDC, &tm);
3250   rectText.left = rectIcon.right;
3251   rectText.top =
3252 	  (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3253   rectText.right = pDIStruct->rcItem.right;
3254   rectText.bottom =
3255 	  (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3256 
3257   /* Draw the icon from the image list */
3258   ImageList_Draw(ilItemImage,
3259                  sfi.iIcon,
3260                  pDIStruct->hDC,
3261                  rectIcon.left,
3262                  rectIcon.top,
3263                  ILD_TRANSPARENT );
3264 
3265   /* Draw the associated text */
3266   TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3267   return NOERROR;
3268 }
3269 
3270 /***********************************************************************
3271  *      FILEDLG95_LOOKIN_OnCommand
3272  *
3273  * LookIn combo box WM_COMMAND message handler
3274  * If the function succeeds, the return value is nonzero.
3275  */
3276 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3277 {
3278   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3279 
3280   TRACE("%p\n", fodInfos);
3281 
3282   switch(wNotifyCode)
3283   {
3284     case CBN_SELENDOK:
3285     {
3286       LPSFOLDER tmpFolder;
3287       int iItem;
3288 
3289       iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3290 
3291       if( iItem == CB_ERR) return FALSE;
3292 
3293       if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3294                                                iItem)))
3295 	return FALSE;
3296 
3297 
3298       if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3299                                               tmpFolder->pidlItem,
3300                                               SBSP_ABSOLUTE)))
3301       {
3302         if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3303             SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3304         return TRUE;
3305       }
3306       break;
3307     }
3308 
3309   }
3310   return FALSE;
3311 }
3312 
3313 /***********************************************************************
3314  *      FILEDLG95_LOOKIN_AddItem
3315  *
3316  * Adds an absolute pidl item to the lookin combo box
3317  * returns the index of the inserted item
3318  */
3319 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3320 {
3321   LPITEMIDLIST pidlNext;
3322   SHFILEINFOW sfi;
3323   SFOLDER *tmpFolder;
3324   LookInInfos *liInfos;
3325 
3326   TRACE("%p, %p, %d\n", hwnd, pidl, iInsertId);
3327 
3328   if(!pidl)
3329     return -1;
3330 
3331   if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3332     return -1;
3333 
3334   tmpFolder = MemAlloc(sizeof(SFOLDER));
3335   tmpFolder->m_iIndent = 0;
3336 
3337   /* Calculate the indentation of the item in the lookin*/
3338   pidlNext = pidl;
3339   while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3340   {
3341     tmpFolder->m_iIndent++;
3342   }
3343 
3344   tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3345 
3346   if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3347     liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3348 
3349   sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3350   SHGetFileInfoW((LPCWSTR)pidl,
3351                   0,
3352                   &sfi,
3353                   sizeof(sfi),
3354                   SHGFI_DISPLAYNAME | SHGFI_PIDL | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3355 
3356   TRACE("-- Add %s attr=0x%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3357 
3358   if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3359   {
3360     int iItemID;
3361 
3362     TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3363 
3364     /* Add the item at the end of the list */
3365     if(iInsertId < 0)
3366     {
3367       iItemID = CBAddString(hwnd,sfi.szDisplayName);
3368     }
3369     /* Insert the item at the iInsertId position*/
3370     else
3371     {
3372       iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3373     }
3374 
3375     CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3376     return iItemID;
3377   }
3378 
3379   COMDLG32_SHFree( tmpFolder->pidlItem );
3380   MemFree( tmpFolder );
3381   return -1;
3382 
3383 }
3384 
3385 /***********************************************************************
3386  *      FILEDLG95_LOOKIN_InsertItemAfterParent
3387  *
3388  * Insert an item below its parent
3389  */
3390 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3391 {
3392 
3393   LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3394   int iParentPos;
3395 
3396   TRACE("\n");
3397 
3398   if (pidl == pidlParent)
3399     return -1;
3400 
3401   iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3402 
3403   if(iParentPos < 0)
3404   {
3405     iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3406   }
3407 
3408   /* Free pidlParent memory */
3409   COMDLG32_SHFree(pidlParent);
3410 
3411   return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3412 }
3413 
3414 /***********************************************************************
3415  *      FILEDLG95_LOOKIN_SelectItem
3416  *
3417  * Adds an absolute pidl item to the lookin combo box
3418  * returns the index of the inserted item
3419  */
3420 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3421 {
3422   int iItemPos;
3423   LookInInfos *liInfos;
3424 
3425   TRACE("%p, %p\n", hwnd, pidl);
3426 
3427   iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3428 
3429   liInfos = GetPropA(hwnd,LookInInfosStr);
3430 
3431   if(iItemPos < 0)
3432   {
3433     while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3434     iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3435   }
3436 
3437   else
3438   {
3439     SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3440     while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3441     {
3442       int iRemovedItem;
3443 
3444       if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3445         break;
3446       if(iRemovedItem < iItemPos)
3447         iItemPos--;
3448     }
3449   }
3450 
3451   CBSetCurSel(hwnd,iItemPos);
3452   liInfos->uSelectedItem = iItemPos;
3453 
3454   return 0;
3455 
3456 }
3457 
3458 /***********************************************************************
3459  *      FILEDLG95_LOOKIN_RemoveMostExpandedItem
3460  *
3461  * Remove the item with an expansion level over iExpansionLevel
3462  */
3463 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3464 {
3465   int iItemPos;
3466   LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3467 
3468   TRACE("\n");
3469 
3470   if(liInfos->iMaxIndentation <= 2)
3471     return -1;
3472 
3473   if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3474   {
3475     SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3476     COMDLG32_SHFree(tmpFolder->pidlItem);
3477     MemFree(tmpFolder);
3478     CBDeleteString(hwnd,iItemPos);
3479     liInfos->iMaxIndentation--;
3480 
3481     return iItemPos;
3482   }
3483 
3484   return -1;
3485 }
3486 
3487 /***********************************************************************
3488  *      FILEDLG95_LOOKIN_SearchItem
3489  *
3490  * Search for pidl in the lookin combo box
3491  * returns the index of the found item
3492  */
3493 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3494 {
3495   int i = 0;
3496   int iCount = CBGetCount(hwnd);
3497 
3498   TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3499 
3500   if (iCount != CB_ERR)
3501   {
3502     for(;i<iCount;i++)
3503     {
3504       LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3505 
3506       if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3507         return i;
3508       if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3509         return i;
3510     }
3511   }
3512 
3513   return -1;
3514 }
3515 
3516 /***********************************************************************
3517  *      FILEDLG95_LOOKIN_Clean
3518  *
3519  * Clean the memory used by the lookin combo box
3520  */
3521 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3522 {
3523     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3524     LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3525     int iPos;
3526     int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3527 
3528     TRACE("\n");
3529 
3530     /* Delete each string of the combo and their associated data */
3531     if (iCount != CB_ERR)
3532     {
3533       for(iPos = iCount-1;iPos>=0;iPos--)
3534       {
3535         SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3536         COMDLG32_SHFree(tmpFolder->pidlItem);
3537         MemFree(tmpFolder);
3538         CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3539       }
3540     }
3541 
3542     /* LookInInfos structure */
3543     MemFree(liInfos);
3544     RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3545 }
3546 
3547 /***********************************************************************
3548  *          get_def_format
3549  *
3550  * Fill the FORMATETC used in the shell id list
3551  */
3552 static FORMATETC get_def_format(void)
3553 {
3554     static CLIPFORMAT cfFormat;
3555     FORMATETC formatetc;
3556 
3557     if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA);
3558     formatetc.cfFormat = cfFormat;
3559     formatetc.ptd = 0;
3560     formatetc.dwAspect = DVASPECT_CONTENT;
3561     formatetc.lindex = -1;
3562     formatetc.tymed = TYMED_HGLOBAL;
3563     return formatetc;
3564 }
3565 
3566 /***********************************************************************
3567  * FILEDLG95_FILENAME_FillFromSelection
3568  *
3569  * fills the edit box from the cached DataObject
3570  */
3571 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3572 {
3573     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3574     LPITEMIDLIST      pidl;
3575     LPWSTR            lpstrAllFiles, lpstrTmp;
3576     UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength;
3577     STGMEDIUM medium;
3578     LPIDA cida;
3579     FORMATETC formatetc = get_def_format();
3580 
3581     TRACE("\n");
3582 
3583     if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium)))
3584         return;
3585 
3586     cida = GlobalLock(medium.u.hGlobal);
3587     nFileSelected = cida->cidl;
3588 
3589     /* Allocate a buffer */
3590     nAllFilesMaxLength = MAX_PATH + 3;
3591     lpstrAllFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nAllFilesMaxLength * sizeof(WCHAR));
3592     if (!lpstrAllFiles)
3593         goto ret;
3594 
3595     /* Loop through the selection, handle only files (not folders) */
3596     for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++)
3597     {
3598         pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]);
3599         if (pidl)
3600         {
3601             if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl))
3602             {
3603                 if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength)
3604                 {
3605                     nAllFilesMaxLength *= 2;
3606                     lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR));
3607                     if (!lpstrTmp)
3608                         goto ret;
3609                     lpstrAllFiles = lpstrTmp;
3610                 }
3611                 nFiles += 1;
3612                 lpstrAllFiles[nAllFilesLength++] = '"';
3613                 GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength);
3614                 nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength);
3615                 nAllFilesLength += nThisFileLength;
3616                 lpstrAllFiles[nAllFilesLength++] = '"';
3617                 lpstrAllFiles[nAllFilesLength++] = ' ';
3618             }
3619         }
3620     }
3621 
3622     if (nFiles != 0)
3623     {
3624         /* If there's only one file, use the name as-is without quotes */
3625         lpstrTmp = lpstrAllFiles;
3626         if (nFiles == 1)
3627         {
3628             lpstrTmp += 1;
3629             lpstrTmp[nThisFileLength] = 0;
3630         }
3631         SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp);
3632         /* Select the file name like Windows does */
3633         if (filename_is_edit(fodInfos))
3634             SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3635     }
3636 
3637 ret:
3638     HeapFree(GetProcessHeap(), 0, lpstrAllFiles);
3639     COMCTL32_ReleaseStgMedium(medium);
3640 }
3641 
3642 
3643 /* copied from shell32 to avoid linking to it
3644  * Although shell32 is already linked the behaviour of exported StrRetToStrN
3645  * is dependent on whether emulated OS is unicode or not.
3646  */
3647 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3648 {
3649 	switch (src->uType)
3650 	{
3651 	  case STRRET_WSTR:
3652 	    lstrcpynW(dest, src->u.pOleStr, len);
3653 	    COMDLG32_SHFree(src->u.pOleStr);
3654 	    break;
3655 
3656 	  case STRRET_CSTR:
3657             if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3658                   dest[len-1] = 0;
3659 	    break;
3660 
3661 	  case STRRET_OFFSET:
3662             if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3663                   dest[len-1] = 0;
3664 	    break;
3665 
3666 	  default:
3667 	    FIXME("unknown type %x!\n", src->uType);
3668 	    if (len) *dest = '\0';
3669 	    return E_FAIL;
3670 	}
3671 	return S_OK;
3672 }
3673 
3674 /***********************************************************************
3675  * FILEDLG95_FILENAME_GetFileNames
3676  *
3677  * Copies the filenames to a delimited string list.
3678  */
3679 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3680 {
3681 	FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3682 	UINT nFileCount = 0;	/* number of files */
3683 	UINT nStrLen = 0;	/* length of string in edit control */
3684 	LPWSTR lpstrEdit;	/* buffer for string from edit control */
3685 
3686 	TRACE("\n");
3687 
3688 	/* get the filenames from the filename control */
3689 	nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
3690 	lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3691 	GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
3692 
3693 	TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3694 
3695 	nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3696 	MemFree(lpstrEdit);
3697 	return nFileCount;
3698 }
3699 
3700 /*
3701  * DATAOBJECT Helper functions
3702  */
3703 
3704 /***********************************************************************
3705  * COMCTL32_ReleaseStgMedium
3706  *
3707  * like ReleaseStgMedium from ole32
3708  */
3709 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3710 {
3711       if(medium.pUnkForRelease)
3712       {
3713         IUnknown_Release(medium.pUnkForRelease);
3714       }
3715       else
3716       {
3717         GlobalUnlock(medium.u.hGlobal);
3718         GlobalFree(medium.u.hGlobal);
3719       }
3720 }
3721 
3722 /***********************************************************************
3723  *          GetPidlFromDataObject
3724  *
3725  * Return pidl(s) by number from the cached DataObject
3726  *
3727  * nPidlIndex=0 gets the fully qualified root path
3728  */
3729 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3730 {
3731 
3732     STGMEDIUM medium;
3733     FORMATETC formatetc = get_def_format();
3734     LPITEMIDLIST pidl = NULL;
3735 
3736     TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3737 
3738     if (!doSelected)
3739         return NULL;
3740 
3741     /* Get the pidls from IDataObject */
3742     if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3743     {
3744       LPIDA cida = GlobalLock(medium.u.hGlobal);
3745       if(nPidlIndex <= cida->cidl)
3746       {
3747         pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3748       }
3749       COMCTL32_ReleaseStgMedium(medium);
3750     }
3751     return pidl;
3752 }
3753 
3754 /***********************************************************************
3755  *          GetNumSelected
3756  *
3757  * Return the number of selected items in the DataObject.
3758  *
3759 */
3760 static UINT GetNumSelected( IDataObject *doSelected )
3761 {
3762     UINT retVal = 0;
3763     STGMEDIUM medium;
3764     FORMATETC formatetc = get_def_format();
3765 
3766     TRACE("sv=%p\n", doSelected);
3767 
3768     if (!doSelected) return 0;
3769 
3770     /* Get the pidls from IDataObject */
3771     if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3772     {
3773       LPIDA cida = GlobalLock(medium.u.hGlobal);
3774       retVal = cida->cidl;
3775       COMCTL32_ReleaseStgMedium(medium);
3776       return retVal;
3777     }
3778     return 0;
3779 }
3780 
3781 /*
3782  * TOOLS
3783  */
3784 
3785 /***********************************************************************
3786  *      GetName
3787  *
3788  * Get the pidl's display name (relative to folder) and
3789  * put it in lpstrFileName.
3790  *
3791  * Return NOERROR on success,
3792  * E_FAIL otherwise
3793  */
3794 
3795 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3796 {
3797   STRRET str;
3798   HRESULT hRes;
3799 
3800   TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3801 
3802   if(!lpsf)
3803   {
3804     SHGetDesktopFolder(&lpsf);
3805     hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3806     IShellFolder_Release(lpsf);
3807     return hRes;
3808   }
3809 
3810   /* Get the display name of the pidl relative to the folder */
3811   if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3812   {
3813       return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3814   }
3815   return E_FAIL;
3816 }
3817 
3818 /***********************************************************************
3819  *      GetShellFolderFromPidl
3820  *
3821  * pidlRel is the item pidl relative
3822  * Return the IShellFolder of the absolute pidl
3823  */
3824 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3825 {
3826   IShellFolder *psf = NULL,*psfParent;
3827 
3828   TRACE("%p\n", pidlAbs);
3829 
3830   if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3831   {
3832     psf = psfParent;
3833     if(pidlAbs && pidlAbs->mkid.cb)
3834     {
3835       if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3836       {
3837 	IShellFolder_Release(psfParent);
3838         return psf;
3839       }
3840     }
3841     /* return the desktop */
3842     return psfParent;
3843   }
3844   return NULL;
3845 }
3846 
3847 /***********************************************************************
3848  *      GetParentPidl
3849  *
3850  * Return the LPITEMIDLIST to the parent of the pidl in the list
3851  */
3852 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3853 {
3854   LPITEMIDLIST pidlParent;
3855 
3856   TRACE("%p\n", pidl);
3857 
3858   pidlParent = COMDLG32_PIDL_ILClone(pidl);
3859   COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3860 
3861   return pidlParent;
3862 }
3863 
3864 /***********************************************************************
3865  *      GetPidlFromName
3866  *
3867  * returns the pidl of the file name relative to folder
3868  * NULL if an error occurred
3869  */
3870 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3871 {
3872   LPITEMIDLIST pidl = NULL;
3873   ULONG ulEaten;
3874 
3875   TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3876 
3877   if(!lpcstrFileName) return NULL;
3878   if(!*lpcstrFileName) return NULL;
3879 
3880   if(!lpsf)
3881   {
3882     if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3883         IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3884         IShellFolder_Release(lpsf);
3885     }
3886   }
3887   else
3888   {
3889     IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3890   }
3891   return pidl;
3892 }
3893 
3894 /*
3895 */
3896 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3897 {
3898 	ULONG uAttr  = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3899 	HRESULT ret;
3900 
3901 	TRACE("%p, %p\n", psf, pidl);
3902 
3903   	ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3904 
3905 	TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
3906 	/* see documentation shell 4.1*/
3907         return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3908 }
3909 
3910 /***********************************************************************
3911  *      BrowseSelectedFolder
3912  */
3913 static BOOL BrowseSelectedFolder(HWND hwnd)
3914 {
3915   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3916   BOOL bBrowseSelFolder = FALSE;
3917 
3918   TRACE("\n");
3919 
3920   if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
3921   {
3922       LPITEMIDLIST pidlSelection;
3923 
3924       /* get the file selected */
3925       pidlSelection  = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
3926       if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
3927       {
3928           if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
3929                          pidlSelection, SBSP_RELATIVE ) ) )
3930           {
3931                WCHAR buf[64];
3932                LoadStringW( COMDLG32_hInstance, IDS_PATHNOTEXISTING, buf, sizeof(buf)/sizeof(WCHAR) );
3933                MessageBoxW( hwnd, buf, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
3934           }
3935           bBrowseSelFolder = TRUE;
3936           if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3937               SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
3938       }
3939       COMDLG32_SHFree( pidlSelection );
3940   }
3941 
3942   return bBrowseSelFolder;
3943 }
3944 
3945 /*
3946  * Memory allocation methods */
3947 static void *MemAlloc(UINT size)
3948 {
3949     return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
3950 }
3951 
3952 static void MemFree(void *mem)
3953 {
3954     HeapFree(GetProcessHeap(),0,mem);
3955 }
3956 
3957 static inline BOOL valid_struct_size( DWORD size )
3958 {
3959     return (size == OPENFILENAME_SIZE_VERSION_400W) ||
3960         (size == sizeof( OPENFILENAMEW ));
3961 }
3962 
3963 static inline BOOL is_win16_looks(DWORD flags)
3964 {
3965     return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
3966             !(flags & OFN_EXPLORER));
3967 }
3968 
3969 /* ------------------ APIs ---------------------- */
3970 
3971 /***********************************************************************
3972  *            GetOpenFileNameA  (COMDLG32.@)
3973  *
3974  * Creates a dialog box for the user to select a file to open.
3975  *
3976  * RETURNS
3977  *    TRUE on success: user enters a valid file
3978  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
3979  *
3980  */
3981 BOOL WINAPI GetOpenFileNameA(OPENFILENAMEA *ofn)
3982 {
3983     TRACE("flags 0x%08x\n", ofn->Flags);
3984 
3985     if (!valid_struct_size( ofn->lStructSize ))
3986     {
3987         COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
3988         return FALSE;
3989     }
3990 
3991     /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
3992     if (ofn->Flags & OFN_FILEMUSTEXIST)
3993         ofn->Flags |= OFN_PATHMUSTEXIST;
3994 
3995     if (is_win16_looks(ofn->Flags))
3996         return GetFileName31A(ofn, OPEN_DIALOG);
3997     else
3998     {
3999         FileOpenDlgInfos info;
4000 
4001         init_filedlg_infoA(ofn, &info);
4002         return GetFileDialog95(&info, OPEN_DIALOG);
4003     }
4004 }
4005 
4006 /***********************************************************************
4007  *            GetOpenFileNameW (COMDLG32.@)
4008  *
4009  * Creates a dialog box for the user to select a file to open.
4010  *
4011  * RETURNS
4012  *    TRUE on success: user enters a valid file
4013  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4014  *
4015  */
4016 BOOL WINAPI GetOpenFileNameW(OPENFILENAMEW *ofn)
4017 {
4018     TRACE("flags 0x%08x\n", ofn->Flags);
4019 
4020     if (!valid_struct_size( ofn->lStructSize ))
4021     {
4022         COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4023         return FALSE;
4024     }
4025 
4026     /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4027     if (ofn->Flags & OFN_FILEMUSTEXIST)
4028         ofn->Flags |= OFN_PATHMUSTEXIST;
4029 
4030     if (is_win16_looks(ofn->Flags))
4031         return GetFileName31W(ofn, OPEN_DIALOG);
4032     else
4033     {
4034         FileOpenDlgInfos info;
4035 
4036         init_filedlg_infoW(ofn, &info);
4037         return GetFileDialog95(&info, OPEN_DIALOG);
4038     }
4039 }
4040 
4041 
4042 /***********************************************************************
4043  *            GetSaveFileNameA  (COMDLG32.@)
4044  *
4045  * Creates a dialog box for the user to select a file to save.
4046  *
4047  * RETURNS
4048  *    TRUE on success: user enters a valid file
4049  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4050  *
4051  */
4052 BOOL WINAPI GetSaveFileNameA(OPENFILENAMEA *ofn)
4053 {
4054     if (!valid_struct_size( ofn->lStructSize ))
4055     {
4056         COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4057         return FALSE;
4058     }
4059 
4060     if (is_win16_looks(ofn->Flags))
4061         return GetFileName31A(ofn, SAVE_DIALOG);
4062     else
4063     {
4064         FileOpenDlgInfos info;
4065 
4066         init_filedlg_infoA(ofn, &info);
4067         return GetFileDialog95(&info, SAVE_DIALOG);
4068     }
4069 }
4070 
4071 /***********************************************************************
4072  *            GetSaveFileNameW  (COMDLG32.@)
4073  *
4074  * Creates a dialog box for the user to select a file to save.
4075  *
4076  * RETURNS
4077  *    TRUE on success: user enters a valid file
4078  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4079  *
4080  */
4081 BOOL WINAPI GetSaveFileNameW(
4082 	LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4083 {
4084     if (!valid_struct_size( ofn->lStructSize ))
4085     {
4086         COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4087         return FALSE;
4088     }
4089 
4090     if (is_win16_looks(ofn->Flags))
4091         return GetFileName31W(ofn, SAVE_DIALOG);
4092     else
4093     {
4094         FileOpenDlgInfos info;
4095 
4096         init_filedlg_infoW(ofn, &info);
4097         return GetFileDialog95(&info, SAVE_DIALOG);
4098     }
4099 }
4100 
4101 /***********************************************************************
4102  *	GetFileTitleA		(COMDLG32.@)
4103  *
4104  * See GetFileTitleW.
4105  */
4106 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4107 {
4108     int ret;
4109     UNICODE_STRING strWFile;
4110     LPWSTR lpWTitle;
4111 
4112     RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4113     lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4114     ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4115     if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4116     RtlFreeUnicodeString( &strWFile );
4117     RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4118     return ret;
4119 }
4120 
4121 
4122 /***********************************************************************
4123  *	GetFileTitleW		(COMDLG32.@)
4124  *
4125  * Get the name of a file.
4126  *
4127  * PARAMS
4128  *  lpFile  [I] name and location of file
4129  *  lpTitle [O] returned file name
4130  *  cbBuf   [I] buffer size of lpTitle
4131  *
4132  * RETURNS
4133  *  Success: zero
4134  *  Failure: negative number.
4135  */
4136 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4137 {
4138 	int i, len;
4139         static const WCHAR brkpoint[] = {'*','[',']',0};
4140 	TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4141 
4142 	if(lpFile == NULL || lpTitle == NULL)
4143 		return -1;
4144 
4145 	len = lstrlenW(lpFile);
4146 
4147 	if (len == 0)
4148 		return -1;
4149 
4150 	if(strpbrkW(lpFile, brkpoint))
4151 		return -1;
4152 
4153 	len--;
4154 
4155 	if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4156 		return -1;
4157 
4158 	for(i = len; i >= 0; i--)
4159 	{
4160 		if (lpFile[i] == '/' ||  lpFile[i] == '\\' ||  lpFile[i] == ':')
4161 		{
4162 			i++;
4163 			break;
4164 		}
4165 	}
4166 
4167 	if(i == -1)
4168 		i++;
4169 
4170 	TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4171 
4172 	len = lstrlenW(lpFile+i)+1;
4173 	if(cbBuf < len)
4174 		return len;
4175 
4176 	lstrcpyW(lpTitle, &lpFile[i]);
4177 	return 0;
4178 }
4179