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