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