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