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