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