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