configure: Setting rpath should no longer be needed for dlls.
[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                                      (LPDLGTEMPLATEA) 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             IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE);
2015           }
2016           else if( nOpenAction == ONOPEN_SEARCH )
2017           {
2018             IShellView_Refresh(fodInfos->Shell.FOIShellView);
2019           }
2020           COMDLG32_SHFree(pidlCurrent);
2021         }
2022       }
2023       ret = FALSE;
2024       break;
2025     case ONOPEN_OPEN:   /* fill in the return struct and close the dialog */
2026       TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2027       {
2028         WCHAR *ext = NULL;
2029
2030         /* update READONLY check box flag */
2031         if ((SendMessageA(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2032           fodInfos->ofnInfos->Flags |= OFN_READONLY;
2033         else
2034           fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2035
2036         /* Attach the file extension with file name*/
2037         ext = PathFindExtensionW(lpstrPathAndFile);
2038         if (! *ext)
2039         {
2040             /* if no extension is specified with file name, then */
2041             /* attach the extension from file filter or default one */
2042             
2043             WCHAR *filterExt = NULL;
2044             LPWSTR lpstrFilter = NULL;
2045             static const WCHAR szwDot[] = {'.',0};
2046             int PathLength = strlenW(lpstrPathAndFile);
2047
2048             /* Attach the dot*/
2049             strcatW(lpstrPathAndFile, szwDot);
2050     
2051             /*Get the file extension from file type filter*/
2052             lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2053                                              fodInfos->ofnInfos->nFilterIndex-1);
2054
2055             if (lpstrFilter != (LPWSTR)CB_ERR)  /* control is not empty */
2056                 filterExt = PathFindExtensionW(lpstrFilter);
2057
2058             if ( filterExt && *filterExt ) /* attach the file extension from file type filter*/
2059                 strcatW(lpstrPathAndFile, filterExt + 1);
2060             else if ( fodInfos->defext ) /* attach the default file extension*/
2061                 strcatW(lpstrPathAndFile, fodInfos->defext);
2062
2063             /* In Open dialog: if file does not exist try without extension */
2064             if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2065                   lpstrPathAndFile[PathLength] = '\0';
2066         }
2067
2068         if (fodInfos->defext) /* add default extension */
2069         {
2070           /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2071           if (*ext)
2072             ext++;
2073           if (!lstrcmpiW(fodInfos->defext, ext))
2074             fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2075           else
2076             fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2077         }
2078
2079         /* In Save dialog: check if the file already exists */
2080         if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2081             && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2082             && PathFileExistsW(lpstrPathAndFile))
2083         {
2084           WCHAR lpstrOverwrite[100];
2085           int answer;
2086
2087           LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2088           answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2089                                MB_YESNO | MB_ICONEXCLAMATION);
2090           if (answer == IDNO)
2091           {
2092             ret = FALSE;
2093             goto ret;
2094           }
2095         }
2096
2097         /* Check that the size of the file does not exceed buffer size.
2098              (Allow for extra \0 if OFN_MULTISELECT is set.) */
2099         if(strlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2100             ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2101         {
2102           LPWSTR lpszTemp;
2103
2104           /* fill destination buffer */
2105           if (fodInfos->ofnInfos->lpstrFile)
2106           {
2107              if(fodInfos->unicode)
2108              {
2109                LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2110
2111                lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2112                if (ofn->Flags & OFN_ALLOWMULTISELECT)
2113                  ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2114              }
2115              else
2116              {
2117                LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2118
2119                WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2120                                    ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2121                if (ofn->Flags & OFN_ALLOWMULTISELECT)
2122                  ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2123              }
2124           }
2125
2126           /* set filename offset */
2127           lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2128           fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2129
2130           /* set extension offset */
2131           lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2132           fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2133
2134           /* set the lpstrFileTitle */
2135           if(fodInfos->ofnInfos->lpstrFileTitle)
2136           {
2137             LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2138             if(fodInfos->unicode)
2139             {
2140               LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2141               lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2142             }
2143             else
2144             {
2145               LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2146               WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2147                     ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2148             }
2149           }
2150
2151           /* copy currently selected filter to lpstrCustomFilter */
2152           if (fodInfos->ofnInfos->lpstrCustomFilter)
2153           {
2154             LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2155             int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2156                                           NULL, 0, NULL, NULL);
2157             if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2158             {
2159               LPSTR s = ofn->lpstrCustomFilter;
2160               s += strlen(ofn->lpstrCustomFilter)+1;
2161               WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2162                                   s, len, NULL, NULL);
2163             }
2164           }
2165
2166
2167           if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2168               goto ret;
2169
2170           TRACE("close\n");
2171           FILEDLG95_Clean(hwnd);
2172           ret = EndDialog(hwnd, TRUE);
2173         }
2174         else
2175         {
2176           WORD size;
2177
2178           size = strlenW(lpstrPathAndFile) + 1;
2179           if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2180              size += 1;
2181           /* return needed size in first two bytes of lpstrFile */
2182           *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2183           FILEDLG95_Clean(hwnd);
2184           ret = EndDialog(hwnd, FALSE);
2185           COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2186         }
2187         goto ret;
2188       }
2189       break;
2190   }
2191
2192 ret:
2193   if(lpsf) IShellFolder_Release(lpsf);
2194   return ret;
2195 }
2196
2197 /***********************************************************************
2198  *      FILEDLG95_SHELL_Init
2199  *
2200  * Initialisation of the shell objects
2201  */
2202 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2203 {
2204   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2205
2206   TRACE("\n");
2207
2208   /*
2209    * Initialisation of the FileOpenDialogInfos structure
2210    */
2211
2212   /* Shell */
2213
2214   /*ShellInfos */
2215   fodInfos->ShellInfos.hwndOwner = hwnd;
2216
2217   /* Disable multi-select if flag not set */
2218   if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2219   {
2220      fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2221   }
2222   fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2223   fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2224
2225   /* Construct the IShellBrowser interface */
2226   fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2227
2228   return NOERROR;
2229 }
2230
2231 /***********************************************************************
2232  *      FILEDLG95_SHELL_ExecuteCommand
2233  *
2234  * Change the folder option and refresh the view
2235  * If the function succeeds, the return value is nonzero.
2236  */
2237 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2238 {
2239   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2240
2241   IContextMenu * pcm;
2242   TRACE("(%p,%p)\n", hwnd, lpVerb);
2243
2244   if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2245                                         SVGIO_BACKGROUND,
2246                                         &IID_IContextMenu,
2247                                         (LPVOID*)&pcm)))
2248   {
2249     CMINVOKECOMMANDINFO ci;
2250     ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2251     ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2252     ci.lpVerb = lpVerb;
2253     ci.hwnd = hwnd;
2254
2255     IContextMenu_InvokeCommand(pcm, &ci);
2256     IContextMenu_Release(pcm);
2257   }
2258
2259   return FALSE;
2260 }
2261
2262 /***********************************************************************
2263  *      FILEDLG95_SHELL_UpFolder
2264  *
2265  * Browse to the specified object
2266  * If the function succeeds, the return value is nonzero.
2267  */
2268 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2269 {
2270   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2271
2272   TRACE("\n");
2273
2274   if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2275                                           NULL,
2276                                           SBSP_PARENT)))
2277   {
2278     SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2279     return TRUE;
2280   }
2281   return FALSE;
2282 }
2283
2284 /***********************************************************************
2285  *      FILEDLG95_SHELL_BrowseToDesktop
2286  *
2287  * Browse to the Desktop
2288  * If the function succeeds, the return value is nonzero.
2289  */
2290 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2291 {
2292   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2293   LPITEMIDLIST pidl;
2294   HRESULT hres;
2295
2296   TRACE("\n");
2297
2298   SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2299   hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2300   SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2301   COMDLG32_SHFree(pidl);
2302   return SUCCEEDED(hres);
2303 }
2304 /***********************************************************************
2305  *      FILEDLG95_SHELL_Clean
2306  *
2307  * Cleans the memory used by shell objects
2308  */
2309 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2310 {
2311     FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2312
2313     TRACE("\n");
2314
2315     COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2316
2317     /* clean Shell interfaces */
2318     IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2319     IShellView_Release(fodInfos->Shell.FOIShellView);
2320     IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2321     IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2322     if (fodInfos->Shell.FOIDataObject)
2323       IDataObject_Release(fodInfos->Shell.FOIDataObject);
2324 }
2325
2326 /***********************************************************************
2327  *      FILEDLG95_FILETYPE_Init
2328  *
2329  * Initialisation of the file type combo box
2330  */
2331 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2332 {
2333   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2334   int nFilters = 0;  /* number of filters */
2335   int nFilterIndexCB;
2336
2337   TRACE("\n");
2338
2339   if(fodInfos->customfilter)
2340   {
2341       /* customfilter has one entry...  title\0ext\0
2342        * Set first entry of combo box item with customfilter
2343        */
2344       LPWSTR  lpstrExt;
2345       LPCWSTR lpstrPos = fodInfos->customfilter;
2346
2347       /* Get the title */
2348       lpstrPos += strlenW(fodInfos->customfilter) + 1;
2349
2350       /* Copy the extensions */
2351       if (! *lpstrPos) return E_FAIL;   /* malformed filter */
2352       if (!(lpstrExt = MemAlloc((strlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2353       strcpyW(lpstrExt,lpstrPos);
2354
2355       /* Add the item at the end of the combo */
2356       CBAddStringW(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2357       CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2358       nFilters++;
2359   }
2360   if(fodInfos->filter)
2361   {
2362     LPCWSTR lpstrPos = fodInfos->filter;
2363
2364     for(;;)
2365     {
2366       /* filter is a list...  title\0ext\0......\0\0
2367        * Set the combo item text to the title and the item data
2368        *  to the ext
2369        */
2370       LPCWSTR lpstrDisplay;
2371       LPWSTR lpstrExt;
2372
2373       /* Get the title */
2374       if(! *lpstrPos) break;    /* end */
2375       lpstrDisplay = lpstrPos;
2376       lpstrPos += strlenW(lpstrPos) + 1;
2377
2378       CBAddStringW(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2379
2380       nFilters++;
2381
2382       /* Copy the extensions */
2383       if (!(lpstrExt = MemAlloc((strlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2384       strcpyW(lpstrExt,lpstrPos);
2385       lpstrPos += strlenW(lpstrPos) + 1;
2386
2387       /* Add the item at the end of the combo */
2388       CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
2389
2390       /* malformed filters are added anyway... */
2391       if (!*lpstrExt) break;
2392     }
2393   }
2394
2395   /*
2396    * Set the current filter to the one specified
2397    * in the initialisation structure
2398    */
2399   if (fodInfos->filter || fodInfos->customfilter)
2400   {
2401     LPWSTR lpstrFilter;
2402
2403     /* Check to make sure our index isn't out of bounds. */
2404     if ( fodInfos->ofnInfos->nFilterIndex >
2405          nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
2406       fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
2407
2408     /* set default filter index */
2409     if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
2410       fodInfos->ofnInfos->nFilterIndex = 1;
2411
2412     /* calculate index of Combo Box item */
2413     nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
2414     if (fodInfos->customfilter == NULL)
2415       nFilterIndexCB--;
2416
2417     /* Set the current index selection. */
2418     CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
2419
2420     /* Get the corresponding text string from the combo box. */
2421     lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2422                                              nFilterIndexCB);
2423
2424     if ((INT_PTR)lpstrFilter == CB_ERR)  /* control is empty */
2425       lpstrFilter = NULL;
2426
2427     if(lpstrFilter)
2428     {
2429       DWORD len;
2430       CharLowerW(lpstrFilter); /* lowercase */
2431       len = strlenW(lpstrFilter)+1;
2432       fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
2433       strcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
2434     }
2435   } else
2436       fodInfos->ofnInfos->nFilterIndex = 0;
2437   return S_OK;
2438 }
2439
2440 /***********************************************************************
2441  *      FILEDLG95_FILETYPE_OnCommand
2442  *
2443  * WM_COMMAND of the file type combo box
2444  * If the function succeeds, the return value is nonzero.
2445  */
2446 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
2447 {
2448   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2449
2450   switch(wNotifyCode)
2451   {
2452     case CBN_SELENDOK:
2453     {
2454       LPWSTR lpstrFilter;
2455
2456       /* Get the current item of the filetype combo box */
2457       int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
2458
2459       /* set the current filter index */
2460       fodInfos->ofnInfos->nFilterIndex = iItem +
2461         (fodInfos->customfilter == NULL ? 1 : 0);
2462
2463       /* Set the current filter with the current selection */
2464       MemFree((LPVOID)fodInfos->ShellInfos.lpstrCurrentFilter);
2465
2466       lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2467                                              iItem);
2468       if((INT_PTR)lpstrFilter != CB_ERR)
2469       {
2470           DWORD len;
2471           CharLowerW(lpstrFilter); /* lowercase */
2472           len = strlenW(lpstrFilter)+1;
2473           fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
2474           strcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
2475           SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
2476       }
2477
2478       /* Refresh the actual view to display the included items*/
2479       IShellView_Refresh(fodInfos->Shell.FOIShellView);
2480     }
2481   }
2482   return FALSE;
2483 }
2484 /***********************************************************************
2485  *      FILEDLG95_FILETYPE_SearchExt
2486  *
2487  * searches for an extension in the filetype box
2488  */
2489 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
2490 {
2491   int i, iCount = CBGetCount(hwnd);
2492
2493   TRACE("%s\n", debugstr_w(lpstrExt));
2494
2495   if(iCount != CB_ERR)
2496   {
2497     for(i=0;i<iCount;i++)
2498     {
2499       if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
2500           return i;
2501     }
2502   }
2503   return -1;
2504 }
2505
2506 /***********************************************************************
2507  *      FILEDLG95_FILETYPE_Clean
2508  *
2509  * Clean the memory used by the filetype combo box
2510  */
2511 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
2512 {
2513   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2514   int iPos;
2515   int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
2516
2517   TRACE("\n");
2518
2519   /* Delete each string of the combo and their associated data */
2520   if(iCount != CB_ERR)
2521   {
2522     for(iPos = iCount-1;iPos>=0;iPos--)
2523     {
2524       MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
2525       CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
2526     }
2527   }
2528   /* Current filter */
2529   MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2530
2531 }
2532
2533 /***********************************************************************
2534  *      FILEDLG95_LOOKIN_Init
2535  *
2536  * Initialisation of the look in combo box
2537  */
2538
2539 /* Small helper function, to determine if the unixfs shell extension is rooted 
2540  * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c. 
2541  */
2542 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
2543     HKEY hKey;
2544     static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
2545         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
2546         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2547         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
2548         'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
2549         '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
2550         '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
2551     
2552     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
2553         return FALSE;
2554         
2555     RegCloseKey(hKey);
2556     return TRUE;
2557 }
2558
2559 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
2560 {
2561   IShellFolder  *psfRoot, *psfDrives;
2562   IEnumIDList   *lpeRoot, *lpeDrives;
2563   LPITEMIDLIST  pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
2564
2565   LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
2566
2567   TRACE("\n");
2568
2569   liInfos->iMaxIndentation = 0;
2570
2571   SetPropA(hwndCombo, LookInInfosStr, (HANDLE) liInfos);
2572
2573   /* set item height for both text field and listbox */
2574   CBSetItemHeight(hwndCombo,-1,GetSystemMetrics(SM_CYSMICON));
2575   CBSetItemHeight(hwndCombo,0,GetSystemMetrics(SM_CYSMICON));
2576    
2577   /* Turn on the extended UI for the combo box like Windows does */
2578   CBSetExtendedUI(hwndCombo, TRUE);
2579
2580   /* Initialise data of Desktop folder */
2581   SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
2582   FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
2583   COMDLG32_SHFree(pidlTmp);
2584
2585   SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
2586
2587   SHGetDesktopFolder(&psfRoot);
2588
2589   if (psfRoot)
2590   {
2591     /* enumerate the contents of the desktop */
2592     if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
2593     {
2594       while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
2595       {
2596         FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
2597
2598         /* If the unixfs extension is rooted, we don't expand the drives by default */
2599         if (!FILEDLG95_unixfs_is_rooted_at_desktop()) 
2600         {
2601           /* special handling for CSIDL_DRIVES */
2602           if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
2603           {
2604             if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
2605             {
2606               /* enumerate the drives */
2607               if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
2608               {
2609                 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
2610                 {
2611                   pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
2612                   FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
2613                   COMDLG32_SHFree(pidlAbsTmp);
2614                   COMDLG32_SHFree(pidlTmp1);
2615                 }
2616                 IEnumIDList_Release(lpeDrives);
2617               }
2618               IShellFolder_Release(psfDrives);
2619             }
2620           }
2621         }
2622
2623         COMDLG32_SHFree(pidlTmp);
2624       }
2625       IEnumIDList_Release(lpeRoot);
2626     }
2627     IShellFolder_Release(psfRoot);
2628   }
2629
2630   COMDLG32_SHFree(pidlDrives);
2631 }
2632
2633 /***********************************************************************
2634  *      FILEDLG95_LOOKIN_DrawItem
2635  *
2636  * WM_DRAWITEM message handler
2637  */
2638 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
2639 {
2640   COLORREF crWin = GetSysColor(COLOR_WINDOW);
2641   COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
2642   COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
2643   RECT rectText;
2644   RECT rectIcon;
2645   SHFILEINFOA sfi;
2646   HIMAGELIST ilItemImage;
2647   int iIndentation;
2648   TEXTMETRICA tm;
2649   LPSFOLDER tmpFolder;
2650
2651
2652   LookInInfos *liInfos = (LookInInfos *)GetPropA(pDIStruct->hwndItem,LookInInfosStr);
2653
2654   TRACE("\n");
2655
2656   if(pDIStruct->itemID == -1)
2657     return 0;
2658
2659   if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
2660                             pDIStruct->itemID)))
2661     return 0;
2662
2663
2664   if(pDIStruct->itemID == liInfos->uSelectedItem)
2665   {
2666     ilItemImage = (HIMAGELIST) SHGetFileInfoA ((LPCSTR) tmpFolder->pidlItem,
2667                                                0,
2668                                                &sfi,
2669                                                sizeof (SHFILEINFOA),
2670                                                SHGFI_PIDL | SHGFI_SMALLICON |
2671                                                SHGFI_OPENICON | SHGFI_SYSICONINDEX    |
2672                                                SHGFI_DISPLAYNAME );
2673   }
2674   else
2675   {
2676     ilItemImage = (HIMAGELIST) SHGetFileInfoA ((LPCSTR) tmpFolder->pidlItem,
2677                                                   0,
2678                                                   &sfi,
2679                                                   sizeof (SHFILEINFOA),
2680                                                   SHGFI_PIDL | SHGFI_SMALLICON |
2681                                                   SHGFI_SYSICONINDEX |
2682                                                   SHGFI_DISPLAYNAME);
2683   }
2684
2685   /* Is this item selected ? */
2686   if(pDIStruct->itemState & ODS_SELECTED)
2687   {
2688     SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
2689     SetBkColor(pDIStruct->hDC,crHighLight);
2690     FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
2691   }
2692   else
2693   {
2694     SetTextColor(pDIStruct->hDC,crText);
2695     SetBkColor(pDIStruct->hDC,crWin);
2696     FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
2697   }
2698
2699   /* Do not indent item if drawing in the edit of the combo */
2700   if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
2701   {
2702     iIndentation = 0;
2703     ilItemImage = (HIMAGELIST) SHGetFileInfoA ((LPCSTR) tmpFolder->pidlItem,
2704                                                 0,
2705                                                 &sfi,
2706                                                 sizeof (SHFILEINFOA),
2707                                                 SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_OPENICON
2708                                                 | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME  );
2709
2710   }
2711   else
2712   {
2713     iIndentation = tmpFolder->m_iIndent;
2714   }
2715   /* Draw text and icon */
2716
2717   /* Initialise the icon display area */
2718   rectIcon.left = pDIStruct->rcItem.left + ICONWIDTH/2 * iIndentation;
2719   rectIcon.top = pDIStruct->rcItem.top;
2720   rectIcon.right = rectIcon.left + ICONWIDTH;
2721   rectIcon.bottom = pDIStruct->rcItem.bottom;
2722
2723   /* Initialise the text display area */
2724   GetTextMetricsA(pDIStruct->hDC, &tm);
2725   rectText.left = rectIcon.right;
2726   rectText.top =
2727           (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
2728   rectText.right = pDIStruct->rcItem.right + XTEXTOFFSET;
2729   rectText.bottom =
2730           (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
2731
2732   /* Draw the icon from the image list */
2733   ImageList_Draw(ilItemImage,
2734                  sfi.iIcon,
2735                  pDIStruct->hDC,
2736                  rectIcon.left,
2737                  rectIcon.top,
2738                  ILD_TRANSPARENT );
2739
2740   /* Draw the associated text */
2741   if(sfi.szDisplayName)
2742     TextOutA(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,strlen(sfi.szDisplayName));
2743
2744
2745   return NOERROR;
2746 }
2747
2748 /***********************************************************************
2749  *      FILEDLG95_LOOKIN_OnCommand
2750  *
2751  * LookIn combo box WM_COMMAND message handler
2752  * If the function succeeds, the return value is nonzero.
2753  */
2754 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
2755 {
2756   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2757
2758   TRACE("%p\n", fodInfos);
2759
2760   switch(wNotifyCode)
2761   {
2762     case CBN_SELENDOK:
2763     {
2764       LPSFOLDER tmpFolder;
2765       int iItem;
2766
2767       iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
2768
2769       if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
2770                                                iItem)))
2771         return FALSE;
2772
2773
2774       if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2775                                               tmpFolder->pidlItem,
2776                                               SBSP_ABSOLUTE)))
2777       {
2778         SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2779         return TRUE;
2780       }
2781       break;
2782     }
2783
2784   }
2785   return FALSE;
2786 }
2787
2788 /***********************************************************************
2789  *      FILEDLG95_LOOKIN_AddItem
2790  *
2791  * Adds an absolute pidl item to the lookin combo box
2792  * returns the index of the inserted item
2793  */
2794 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
2795 {
2796   LPITEMIDLIST pidlNext;
2797   SHFILEINFOA sfi;
2798   SFOLDER *tmpFolder;
2799   LookInInfos *liInfos;
2800
2801   TRACE("%08x\n", iInsertId);
2802
2803   if(!pidl)
2804     return -1;
2805
2806   if(!(liInfos = (LookInInfos *)GetPropA(hwnd,LookInInfosStr)))
2807     return -1;
2808
2809   tmpFolder = MemAlloc(sizeof(SFOLDER));
2810   tmpFolder->m_iIndent = 0;
2811
2812   /* Calculate the indentation of the item in the lookin*/
2813   pidlNext = pidl;
2814   while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
2815   {
2816     tmpFolder->m_iIndent++;
2817   }
2818
2819   tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
2820
2821   if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
2822     liInfos->iMaxIndentation = tmpFolder->m_iIndent;
2823
2824   sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
2825   SHGetFileInfoA((LPSTR)pidl,
2826                   0,
2827                   &sfi,
2828                   sizeof(sfi),
2829                   SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX
2830                   | SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
2831
2832   TRACE("-- Add %s attr=%08lx\n", sfi.szDisplayName, sfi.dwAttributes);
2833
2834   if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
2835   {
2836     int iItemID;
2837
2838     TRACE("-- Add %s at %u\n", sfi.szDisplayName, tmpFolder->m_iIndent);
2839
2840     /* Add the item at the end of the list */
2841     if(iInsertId < 0)
2842     {
2843       iItemID = CBAddString(hwnd,sfi.szDisplayName);
2844     }
2845     /* Insert the item at the iInsertId position*/
2846     else
2847     {
2848       iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
2849     }
2850
2851     CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
2852     return iItemID;
2853   }
2854
2855   COMDLG32_SHFree( tmpFolder->pidlItem );
2856   MemFree( tmpFolder );
2857   return -1;
2858
2859 }
2860
2861 /***********************************************************************
2862  *      FILEDLG95_LOOKIN_InsertItemAfterParent
2863  *
2864  * Insert an item below its parent
2865  */
2866 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
2867 {
2868
2869   LPITEMIDLIST pidlParent = GetParentPidl(pidl);
2870   int iParentPos;
2871
2872   TRACE("\n");
2873
2874   iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
2875
2876   if(iParentPos < 0)
2877   {
2878     iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
2879   }
2880
2881   /* Free pidlParent memory */
2882   COMDLG32_SHFree((LPVOID)pidlParent);
2883
2884   return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
2885 }
2886
2887 /***********************************************************************
2888  *      FILEDLG95_LOOKIN_SelectItem
2889  *
2890  * Adds an absolute pidl item to the lookin combo box
2891  * returns the index of the inserted item
2892  */
2893 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
2894 {
2895   int iItemPos;
2896   LookInInfos *liInfos;
2897
2898   TRACE("\n");
2899
2900   iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
2901
2902   liInfos = (LookInInfos *)GetPropA(hwnd,LookInInfosStr);
2903
2904   if(iItemPos < 0)
2905   {
2906     while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
2907     iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
2908   }
2909
2910   else
2911   {
2912     SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
2913     while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
2914     {
2915       int iRemovedItem;
2916
2917       if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
2918         break;
2919       if(iRemovedItem < iItemPos)
2920         iItemPos--;
2921     }
2922   }
2923
2924   CBSetCurSel(hwnd,iItemPos);
2925   liInfos->uSelectedItem = iItemPos;
2926
2927   return 0;
2928
2929 }
2930
2931 /***********************************************************************
2932  *      FILEDLG95_LOOKIN_RemoveMostExpandedItem
2933  *
2934  * Remove the item with an expansion level over iExpansionLevel
2935  */
2936 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
2937 {
2938   int iItemPos;
2939
2940   LookInInfos *liInfos = (LookInInfos *)GetPropA(hwnd,LookInInfosStr);
2941
2942   TRACE("\n");
2943
2944   if(liInfos->iMaxIndentation <= 2)
2945     return -1;
2946
2947   if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
2948   {
2949     SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
2950     COMDLG32_SHFree(tmpFolder->pidlItem);
2951     MemFree(tmpFolder);
2952     CBDeleteString(hwnd,iItemPos);
2953     liInfos->iMaxIndentation--;
2954
2955     return iItemPos;
2956   }
2957
2958   return -1;
2959 }
2960
2961 /***********************************************************************
2962  *      FILEDLG95_LOOKIN_SearchItem
2963  *
2964  * Search for pidl in the lookin combo box
2965  * returns the index of the found item
2966  */
2967 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
2968 {
2969   int i = 0;
2970   int iCount = CBGetCount(hwnd);
2971
2972   TRACE("0x%08x 0x%x\n",searchArg, iSearchMethod);
2973
2974   if (iCount != CB_ERR)
2975   {
2976     for(;i<iCount;i++)
2977     {
2978       LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
2979
2980       if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
2981         return i;
2982       if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
2983         return i;
2984     }
2985   }
2986
2987   return -1;
2988 }
2989
2990 /***********************************************************************
2991  *      FILEDLG95_LOOKIN_Clean
2992  *
2993  * Clean the memory used by the lookin combo box
2994  */
2995 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
2996 {
2997     FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
2998     int iPos;
2999     int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3000
3001     TRACE("\n");
3002
3003     /* Delete each string of the combo and their associated data */
3004     if (iCount != CB_ERR)
3005     {
3006       for(iPos = iCount-1;iPos>=0;iPos--)
3007       {
3008         SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3009         COMDLG32_SHFree(tmpFolder->pidlItem);
3010         MemFree(tmpFolder);
3011         CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3012       }
3013     }
3014
3015     /* LookInInfos structure */
3016     RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3017
3018 }
3019 /***********************************************************************
3020  * FILEDLG95_FILENAME_FillFromSelection
3021  *
3022  * fills the edit box from the cached DataObject
3023  */
3024 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3025 {
3026     FileOpenDlgInfos *fodInfos;
3027     LPITEMIDLIST      pidl;
3028     UINT              nFiles = 0, nFileToOpen, nFileSelected, nLength = 0;
3029     char              lpstrTemp[MAX_PATH];
3030     LPSTR             lpstrAllFile = NULL, lpstrCurrFile = NULL;
3031
3032     TRACE("\n");
3033     fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
3034
3035     /* Count how many files we have */
3036     nFileSelected = GetNumSelected( fodInfos->Shell.FOIDataObject );
3037
3038     /* calculate the string length, count files */
3039     if (nFileSelected >= 1)
3040     {
3041       nLength += 3;     /* first and last quotes, trailing \0 */
3042       for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ )
3043       {
3044         pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 );
3045
3046         if (pidl)
3047         {
3048           /* get the total length of the selected file names */
3049           lpstrTemp[0] = '\0';
3050           GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp );
3051
3052           if ( ! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl) ) /* Ignore folders */
3053           {
3054             nLength += strlen( lpstrTemp ) + 3;
3055             nFiles++;
3056           }
3057           COMDLG32_SHFree( pidl );
3058         }
3059       }
3060     }
3061
3062     /* allocate the buffer */
3063     if (nFiles <= 1) nLength = MAX_PATH;
3064     lpstrAllFile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength);
3065     lpstrAllFile[0] = '\0';
3066
3067     /* Generate the string for the edit control */
3068     if(nFiles >= 1)
3069     {
3070       lpstrCurrFile = lpstrAllFile;
3071       for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ )
3072       {
3073         pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 );
3074
3075         if (pidl)
3076         {
3077           /* get the file name */
3078           lpstrTemp[0] = '\0';
3079           GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp );
3080
3081           if (! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl)) /* Ignore folders */
3082           {
3083             if ( nFiles > 1)
3084             {
3085               *lpstrCurrFile++ =  '\"';
3086               strcpy( lpstrCurrFile, lpstrTemp );
3087               lpstrCurrFile += strlen( lpstrTemp );
3088               strcpy( lpstrCurrFile, "\" " );
3089               lpstrCurrFile += 2;
3090             }
3091             else
3092             {
3093               strcpy( lpstrAllFile, lpstrTemp );
3094             }
3095           }
3096           COMDLG32_SHFree( (LPVOID) pidl );
3097         }
3098       }
3099       SetWindowTextA( fodInfos->DlgInfos.hwndFileName, lpstrAllFile );
3100        
3101       /* Select the file name like Windows does */ 
3102       SendMessageA(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
3103     }
3104     HeapFree(GetProcessHeap(),0, lpstrAllFile );
3105 }
3106
3107
3108 /* copied from shell32 to avoid linking to it
3109  * FIXME: why?  shell32 is already linked
3110  */
3111 static HRESULT COMDLG32_StrRetToStrNA (LPVOID dest, DWORD len, LPSTRRET src, LPITEMIDLIST pidl)
3112 {
3113         switch (src->uType)
3114         {
3115           case STRRET_WSTR:
3116             WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, (LPSTR)dest, len, NULL, NULL);
3117             COMDLG32_SHFree(src->u.pOleStr);
3118             break;
3119
3120           case STRRET_CSTR:
3121             lstrcpynA((LPSTR)dest, src->u.cStr, len);
3122             break;
3123
3124           case STRRET_OFFSET:
3125             lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
3126             break;
3127
3128           default:
3129             FIXME("unknown type!\n");
3130             if (len)
3131             {
3132               *(LPSTR)dest = '\0';
3133             }
3134             return(E_FAIL);
3135         }
3136         return S_OK;
3137 }
3138
3139 /***********************************************************************
3140  * FILEDLG95_FILENAME_GetFileNames
3141  *
3142  * Copies the filenames to a delimited string list.
3143  * The delimiter is specified by the parameter 'separator',
3144  *  usually either a space or a nul
3145  */
3146 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed, char separator)
3147 {
3148         FileOpenDlgInfos *fodInfos  = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
3149         UINT nStrCharCount = 0; /* index in src buffer */
3150         UINT nFileIndex = 0;    /* index in dest buffer */
3151         UINT nFileCount = 0;    /* number of files */
3152         UINT nStrLen = 0;       /* length of string in edit control */
3153         LPWSTR lpstrEdit;       /* buffer for string from edit control */
3154
3155         TRACE("\n");
3156
3157         /* get the filenames from the edit control */
3158         nStrLen = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0);
3159         lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3160         GetDlgItemTextW(hwnd, IDC_FILENAME, lpstrEdit, nStrLen+1);
3161
3162         TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3163
3164         /* we might get single filename without any '"',
3165          * so we need nStrLen + terminating \0 + end-of-list \0 */
3166         *lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
3167         *sizeUsed = 0;
3168
3169         /* build delimited file list from filenames */
3170         while ( nStrCharCount <= nStrLen )
3171         {
3172           if ( lpstrEdit[nStrCharCount]=='"' )
3173           {
3174             nStrCharCount++;
3175             while ((lpstrEdit[nStrCharCount]!='"') && (nStrCharCount <= nStrLen))
3176             {
3177               (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
3178               (*sizeUsed)++;
3179               nStrCharCount++;
3180             }
3181             (*lpstrFileList)[nFileIndex++] = separator;
3182             (*sizeUsed)++;
3183             nFileCount++;
3184           }
3185           nStrCharCount++;
3186         }
3187
3188         /* single, unquoted string */
3189         if ((nStrLen > 0) && (*sizeUsed == 0) )
3190         {
3191           strcpyW(*lpstrFileList, lpstrEdit);
3192           nFileIndex = strlenW(lpstrEdit) + 1;
3193           (*sizeUsed) = nFileIndex;
3194           nFileCount = 1;
3195         }
3196
3197         /* trailing \0 */
3198         (*lpstrFileList)[nFileIndex] = '\0';
3199         (*sizeUsed)++;
3200
3201         MemFree(lpstrEdit);
3202         return nFileCount;
3203 }
3204
3205 #define SETDefFormatEtc(fe,cf,med) \
3206 { \
3207     (fe).cfFormat = cf;\
3208     (fe).dwAspect = DVASPECT_CONTENT; \
3209     (fe).ptd =NULL;\
3210     (fe).tymed = med;\
3211     (fe).lindex = -1;\
3212 };
3213
3214 /*
3215  * DATAOBJECT Helper functions
3216  */
3217
3218 /***********************************************************************
3219  * COMCTL32_ReleaseStgMedium
3220  *
3221  * like ReleaseStgMedium from ole32
3222  */
3223 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3224 {
3225       if(medium.pUnkForRelease)
3226       {
3227         IUnknown_Release(medium.pUnkForRelease);
3228       }
3229       else
3230       {
3231         GlobalUnlock(medium.u.hGlobal);
3232         GlobalFree(medium.u.hGlobal);
3233       }
3234 }
3235
3236 /***********************************************************************
3237  *          GetPidlFromDataObject
3238  *
3239  * Return pidl(s) by number from the cached DataObject
3240  *
3241  * nPidlIndex=0 gets the fully qualified root path
3242  */
3243 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3244 {
3245
3246     STGMEDIUM medium;
3247     FORMATETC formatetc;
3248     LPITEMIDLIST pidl = NULL;
3249
3250     TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3251
3252     if (!doSelected)
3253         return NULL;
3254         
3255     /* Set the FORMATETC structure*/
3256     SETDefFormatEtc(formatetc, RegisterClipboardFormatA(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
3257
3258     /* Get the pidls from IDataObject */
3259     if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3260     {
3261       LPIDA cida = GlobalLock(medium.u.hGlobal);
3262       if(nPidlIndex <= cida->cidl)
3263       {
3264         pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3265       }
3266       COMCTL32_ReleaseStgMedium(medium);
3267     }
3268     return pidl;
3269 }
3270
3271 /***********************************************************************
3272  *          GetNumSelected
3273  *
3274  * Return the number of selected items in the DataObject.
3275  *
3276 */
3277 UINT GetNumSelected( IDataObject *doSelected )
3278 {
3279     UINT retVal = 0;
3280     STGMEDIUM medium;
3281     FORMATETC formatetc;
3282
3283     TRACE("sv=%p\n", doSelected);
3284
3285     if (!doSelected) return 0;
3286
3287     /* Set the FORMATETC structure*/
3288     SETDefFormatEtc(formatetc, RegisterClipboardFormatA(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
3289
3290     /* Get the pidls from IDataObject */
3291     if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3292     {
3293       LPIDA cida = GlobalLock(medium.u.hGlobal);
3294       retVal = cida->cidl;
3295       COMCTL32_ReleaseStgMedium(medium);
3296       return retVal;
3297     }
3298     return 0;
3299 }
3300
3301 /*
3302  * TOOLS
3303  */
3304
3305 /***********************************************************************
3306  *      GetName
3307  *
3308  * Get the pidl's display name (relative to folder) and
3309  * put it in lpstrFileName.
3310  *
3311  * Return NOERROR on success,
3312  * E_FAIL otherwise
3313  */
3314
3315 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPSTR lpstrFileName)
3316 {
3317   STRRET str;
3318   HRESULT hRes;
3319
3320   TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3321
3322   if(!lpsf)
3323   {
3324     SHGetDesktopFolder(&lpsf);
3325     hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3326     IShellFolder_Release(lpsf);
3327     return hRes;
3328   }
3329
3330   /* Get the display name of the pidl relative to the folder */
3331   if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3332   {
3333       return COMDLG32_StrRetToStrNA(lpstrFileName, MAX_PATH, &str, pidl);
3334   }
3335   return E_FAIL;
3336 }
3337
3338 /***********************************************************************
3339  *      GetShellFolderFromPidl
3340  *
3341  * pidlRel is the item pidl relative
3342  * Return the IShellFolder of the absolute pidl
3343  */
3344 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3345 {
3346   IShellFolder *psf = NULL,*psfParent;
3347
3348   TRACE("%p\n", pidlAbs);
3349
3350   if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3351   {
3352     psf = psfParent;
3353     if(pidlAbs && pidlAbs->mkid.cb)
3354     {
3355       if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3356       {
3357         IShellFolder_Release(psfParent);
3358         return psf;
3359       }
3360     }
3361     /* return the desktop */
3362     return psfParent;
3363   }
3364   return NULL;
3365 }
3366
3367 /***********************************************************************
3368  *      GetParentPidl
3369  *
3370  * Return the LPITEMIDLIST to the parent of the pidl in the list
3371  */
3372 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3373 {
3374   LPITEMIDLIST pidlParent;
3375
3376   TRACE("%p\n", pidl);
3377
3378   pidlParent = COMDLG32_PIDL_ILClone(pidl);
3379   COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3380
3381   return pidlParent;
3382 }
3383
3384 /***********************************************************************
3385  *      GetPidlFromName
3386  *
3387  * returns the pidl of the file name relative to folder
3388  * NULL if an error occurred
3389  */
3390 LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3391 {
3392   LPITEMIDLIST pidl = NULL;
3393   ULONG ulEaten;
3394
3395   TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3396
3397   if(!lpcstrFileName) return NULL;
3398   if(!*lpcstrFileName) return NULL;
3399
3400   if(!lpsf)
3401   {
3402     if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3403         IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3404         IShellFolder_Release(lpsf);
3405     }
3406   }
3407   else
3408   {
3409     IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3410   }
3411   return pidl;
3412 }
3413
3414 /*
3415 */
3416 BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3417 {
3418         ULONG uAttr  = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3419         HRESULT ret;
3420
3421         TRACE("%p, %p\n", psf, pidl);
3422
3423         ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3424
3425         TRACE("-- 0x%08lx 0x%08lx\n", uAttr, ret);
3426         /* see documentation shell 4.1*/
3427         return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3428 }
3429
3430 /***********************************************************************
3431  *      BrowseSelectedFolder
3432  */
3433 static BOOL BrowseSelectedFolder(HWND hwnd)
3434 {
3435   BOOL bBrowseSelFolder = FALSE;
3436   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) GetPropA(hwnd,FileOpenDlgInfosStr);
3437
3438   TRACE("\n");
3439
3440   if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
3441   {
3442       LPITEMIDLIST pidlSelection;
3443
3444       /* get the file selected */
3445       pidlSelection  = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
3446       if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
3447       {
3448           if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
3449                          pidlSelection, SBSP_RELATIVE ) ) )
3450           {
3451                static const WCHAR notexist[] = {'P','a','t','h',' ','d','o','e','s',
3452                                    ' ','n','o','t',' ','e','x','i','s','t',0};
3453                MessageBoxW( hwnd, notexist, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
3454           }
3455           bBrowseSelFolder = TRUE;
3456           SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
3457       }
3458       COMDLG32_SHFree( pidlSelection );
3459   }
3460
3461   return bBrowseSelFolder;
3462 }
3463
3464 /*
3465  * Memory allocation methods */
3466 static void *MemAlloc(UINT size)
3467 {
3468     return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
3469 }
3470
3471 static void MemFree(void *mem)
3472 {
3473     HeapFree(GetProcessHeap(),0,mem);
3474 }
3475
3476 /*
3477  * Old-style (win3.1) dialogs */
3478
3479 /***********************************************************************
3480  *           FD32_GetTemplate                                  [internal]
3481  *
3482  * Get a template (or FALSE if failure) when 16 bits dialogs are used
3483  * by a 32 bits application
3484  *
3485  */
3486 static BOOL FD32_GetTemplate(PFD31_DATA lfs)
3487 {
3488     LPOPENFILENAMEW ofnW = lfs->ofnW;
3489     PFD32_PRIVATE priv = (PFD32_PRIVATE) lfs->private1632;
3490     HANDLE hDlgTmpl;
3491
3492     if (ofnW->Flags & OFN_ENABLETEMPLATEHANDLE)
3493     {
3494         if (!(lfs->template = LockResource( ofnW->hInstance )))
3495         {
3496             COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
3497             return FALSE;
3498         }
3499     }
3500     else if (ofnW->Flags & OFN_ENABLETEMPLATE)
3501     {
3502         HRSRC hResInfo;
3503         if (priv->ofnA)
3504             hResInfo = FindResourceA(priv->ofnA->hInstance,
3505                                  priv->ofnA->lpTemplateName,
3506                                  (LPSTR)RT_DIALOG);
3507         else
3508             hResInfo = FindResourceW(ofnW->hInstance,
3509                                  ofnW->lpTemplateName,
3510                                  (LPWSTR)RT_DIALOG);
3511         if (!hResInfo)
3512         {
3513             COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
3514             return FALSE;
3515         }
3516         if (!(hDlgTmpl = LoadResource(ofnW->hInstance,
3517                                 hResInfo)) ||
3518                     !(lfs->template = LockResource(hDlgTmpl)))
3519         {
3520             COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
3521             return FALSE;
3522         }
3523     } else { /* get it from internal Wine resource */
3524         HRSRC hResInfo;
3525         if (!(hResInfo = FindResourceA(COMDLG32_hInstance,
3526              lfs->open? "OPEN_FILE":"SAVE_FILE", (LPSTR)RT_DIALOG)))
3527         {
3528             COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
3529             return FALSE;
3530         }
3531         if (!(hDlgTmpl = LoadResource(COMDLG32_hInstance, hResInfo )) ||
3532                 !(lfs->template = LockResource( hDlgTmpl )))
3533         {
3534             COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
3535             return FALSE;
3536         }
3537     }
3538     return TRUE;
3539 }
3540
3541
3542 /************************************************************************
3543  *                              FD32_Init          [internal]
3544  *      called from the common 16/32 code to initialize 32 bit data
3545  */
3546 static BOOL CALLBACK FD32_Init(LPARAM lParam, PFD31_DATA lfs, DWORD data)
3547 {
3548     BOOL IsUnicode = (BOOL) data;
3549     PFD32_PRIVATE priv;
3550
3551     priv = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FD32_PRIVATE));
3552     lfs->private1632 = priv;
3553     if (NULL == lfs->private1632) return FALSE;
3554     if (IsUnicode)
3555     {
3556         lfs->ofnW = (LPOPENFILENAMEW) lParam;
3557         if (lfs->ofnW->Flags & OFN_ENABLEHOOK)
3558             if (lfs->ofnW->lpfnHook)
3559                 lfs->hook = TRUE;
3560     }
3561     else
3562     {
3563         priv->ofnA = (LPOPENFILENAMEA) lParam;
3564         if (priv->ofnA->Flags & OFN_ENABLEHOOK)
3565             if (priv->ofnA->lpfnHook)
3566                 lfs->hook = TRUE;
3567         lfs->ofnW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lfs->ofnW));
3568         FD31_MapOfnStructA(priv->ofnA, lfs->ofnW, lfs->open);
3569     }
3570
3571     if (! FD32_GetTemplate(lfs)) return FALSE;
3572
3573     return TRUE;
3574 }
3575
3576 /***********************************************************************
3577  *                              FD32_CallWindowProc          [internal]
3578  *
3579  *      called from the common 16/32 code to call the appropriate hook
3580  */
3581 static BOOL CALLBACK FD32_CallWindowProc(PFD31_DATA lfs, UINT wMsg, WPARAM wParam,
3582                                  LPARAM lParam)
3583 {
3584     BOOL ret;
3585     PFD32_PRIVATE priv = (PFD32_PRIVATE) lfs->private1632;
3586
3587     if (priv->ofnA)
3588     {
3589         TRACE("Call hookA %p (%p, %04x, %08x, %08lx)\n",
3590                priv->ofnA->lpfnHook, lfs->hwnd, wMsg, wParam, lParam);
3591         ret = priv->ofnA->lpfnHook(lfs->hwnd, wMsg, wParam, lParam);
3592         TRACE("ret hookA %p (%p, %04x, %08x, %08lx)\n",
3593                priv->ofnA->lpfnHook, lfs->hwnd, wMsg, wParam, lParam);
3594         return ret;
3595     }
3596
3597     TRACE("Call hookW %p (%p, %04x, %08x, %08lx)\n",
3598            lfs->ofnW->lpfnHook, lfs->hwnd, wMsg, wParam, lParam);
3599     ret = lfs->ofnW->lpfnHook(lfs->hwnd, wMsg, wParam, lParam);
3600     TRACE("Ret hookW %p (%p, %04x, %08x, %08lx)\n",
3601            lfs->ofnW->lpfnHook, lfs->hwnd, wMsg, wParam, lParam);
3602     return ret;
3603 }
3604
3605 /***********************************************************************
3606  *                              FD32_UpdateResult            [internal]
3607  *          update the real client structures if any
3608  */
3609 static void CALLBACK FD32_UpdateResult(PFD31_DATA lfs)
3610 {
3611     PFD32_PRIVATE priv = (PFD32_PRIVATE) lfs->private1632;
3612     LPOPENFILENAMEW ofnW = lfs->ofnW;
3613
3614     if (priv->ofnA)
3615     {
3616         if (ofnW->nMaxFile &&
3617             !WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFile, -1,
3618                                   priv->ofnA->lpstrFile, ofnW->nMaxFile, NULL, NULL ))
3619             priv->ofnA->lpstrFile[ofnW->nMaxFile-1] = 0;
3620         priv->ofnA->nFileOffset = ofnW->nFileOffset;
3621         priv->ofnA->nFileExtension = ofnW->nFileExtension;
3622     }
3623 }
3624
3625 /***********************************************************************
3626  *                              FD32_UpdateFileTitle            [internal]
3627  *          update the real client structures if any
3628  */
3629 static void CALLBACK FD32_UpdateFileTitle(PFD31_DATA lfs)
3630 {
3631     PFD32_PRIVATE priv = (PFD32_PRIVATE) lfs->private1632;
3632     LPOPENFILENAMEW ofnW = lfs->ofnW;
3633
3634     if (priv->ofnA)
3635     {
3636         if (!WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFileTitle, -1,
3637                                   priv->ofnA->lpstrFileTitle, ofnW->nMaxFileTitle, NULL, NULL ))
3638             priv->ofnA->lpstrFileTitle[ofnW->nMaxFileTitle-1] = 0;
3639     }
3640 }
3641
3642
3643 /***********************************************************************
3644  *                              FD32_SendLbGetCurSel         [internal]
3645  *          retrieve selected listbox item
3646  */
3647 static LRESULT CALLBACK FD32_SendLbGetCurSel(PFD31_DATA lfs)
3648 {
3649     return SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETCURSEL, 0, 0);
3650 }
3651
3652
3653 /************************************************************************
3654  *                              FD32_Destroy          [internal]
3655  *      called from the common 16/32 code to cleanup 32 bit data
3656  */
3657 static void CALLBACK FD32_Destroy(PFD31_DATA lfs)
3658 {
3659     PFD32_PRIVATE priv = (PFD32_PRIVATE) lfs->private1632;
3660
3661     /* if ofnW has been allocated, have to free everything in it */
3662     if (NULL != priv && NULL != priv->ofnA)
3663     {
3664         FD31_FreeOfnW(lfs->ofnW);
3665         HeapFree(GetProcessHeap(), 0, lfs->ofnW);
3666     }
3667 }
3668
3669 static void FD32_SetupCallbacks(PFD31_CALLBACKS callbacks)
3670 {
3671     callbacks->Init = FD32_Init;
3672     callbacks->CWP = FD32_CallWindowProc;
3673     callbacks->UpdateResult = FD32_UpdateResult;
3674     callbacks->UpdateFileTitle = FD32_UpdateFileTitle;
3675     callbacks->SendLbGetCurSel = FD32_SendLbGetCurSel;
3676     callbacks->Destroy = FD32_Destroy;
3677 }
3678
3679 /***********************************************************************
3680  *                              FD32_WMMeasureItem           [internal]
3681  */
3682 static LONG FD32_WMMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
3683 {
3684     LPMEASUREITEMSTRUCT lpmeasure;
3685
3686     lpmeasure = (LPMEASUREITEMSTRUCT)lParam;
3687     lpmeasure->itemHeight = FD31_GetFldrHeight();
3688     return TRUE;
3689 }
3690
3691
3692 /***********************************************************************
3693  *           FileOpenDlgProc                                    [internal]
3694  *      Used for open and save, in fact.
3695  */
3696 static INT_PTR CALLBACK FD32_FileOpenDlgProc(HWND hWnd, UINT wMsg,
3697                                              WPARAM wParam, LPARAM lParam)
3698 {
3699     PFD31_DATA lfs = (PFD31_DATA)GetPropA(hWnd,FD31_OFN_PROP);
3700
3701     TRACE("msg=%x wparam=%x lParam=%lx\n", wMsg, wParam, lParam);
3702     if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
3703         {
3704             INT_PTR lRet;
3705             lRet  = (INT_PTR)FD31_CallWindowProc(lfs, wMsg, wParam, lParam);
3706             if (lRet)
3707                 return lRet;         /* else continue message processing */
3708         }
3709     switch (wMsg)
3710     {
3711     case WM_INITDIALOG:
3712         return FD31_WMInitDialog(hWnd, wParam, lParam);
3713
3714     case WM_MEASUREITEM:
3715         return FD32_WMMeasureItem(hWnd, wParam, lParam);
3716
3717     case WM_DRAWITEM:
3718         return FD31_WMDrawItem(hWnd, wParam, lParam, !lfs->open, (DRAWITEMSTRUCT *)lParam);
3719
3720     case WM_COMMAND:
3721         return FD31_WMCommand(hWnd, lParam, HIWORD(wParam), LOWORD(wParam), lfs);
3722 #if 0
3723     case WM_CTLCOLOR:
3724          SetBkColor((HDC16)wParam, 0x00C0C0C0);
3725          switch (HIWORD(lParam))
3726          {
3727          case CTLCOLOR_BTN:
3728              SetTextColor((HDC16)wParam, 0x00000000);
3729              return hGRAYBrush;
3730         case CTLCOLOR_STATIC:
3731              SetTextColor((HDC16)wParam, 0x00000000);
3732              return hGRAYBrush;
3733         }
3734       break;
3735 #endif
3736     }
3737     return FALSE;
3738 }
3739
3740
3741 /***********************************************************************
3742  *           GetFileName31A                                 [internal]
3743  *
3744  * Creates a win31 style dialog box for the user to select a file to open/save.
3745  */
3746 static BOOL GetFileName31A(LPOPENFILENAMEA lpofn, /* addess of structure with data*/
3747                            UINT dlgType /* type dialogue : open/save */
3748                            )
3749 {
3750     HINSTANCE hInst;
3751     BOOL bRet = FALSE;
3752     PFD31_DATA lfs;
3753     FD31_CALLBACKS callbacks;
3754
3755     if (!lpofn || !FD31_Init()) return FALSE;
3756
3757     TRACE("ofn flags %08lx\n", lpofn->Flags);
3758     FD32_SetupCallbacks(&callbacks);
3759     lfs = FD31_AllocPrivate((LPARAM) lpofn, dlgType, &callbacks, (DWORD) FALSE);
3760     if (lfs)
3761     {
3762         hInst = (HINSTANCE)GetWindowLongPtrA( lpofn->hwndOwner, GWLP_HINSTANCE );
3763         bRet = DialogBoxIndirectParamA( hInst, lfs->template, lpofn->hwndOwner,
3764                                         FD32_FileOpenDlgProc, (LPARAM)lfs);
3765         FD31_DestroyPrivate(lfs);
3766     }
3767
3768     TRACE("return lpstrFile='%s' !\n", lpofn->lpstrFile);
3769     return bRet;
3770 }
3771
3772 /***********************************************************************
3773  *           GetFileName31W                                 [internal]
3774  *
3775  * Creates a win31 style dialog box for the user to select a file to open/save
3776  */
3777 static BOOL GetFileName31W(LPOPENFILENAMEW lpofn, /* addess of structure with data*/
3778                            UINT dlgType /* type dialogue : open/save */
3779                            )
3780 {
3781     HINSTANCE hInst;
3782     BOOL bRet = FALSE;
3783     PFD31_DATA lfs;
3784     FD31_CALLBACKS callbacks;
3785
3786     if (!lpofn || !FD31_Init()) return FALSE;
3787
3788     FD32_SetupCallbacks(&callbacks);
3789     lfs = FD31_AllocPrivate((LPARAM) lpofn, dlgType, &callbacks, (DWORD) TRUE);
3790     if (lfs)
3791     {
3792         hInst = (HINSTANCE)GetWindowLongPtrW( lpofn->hwndOwner, GWLP_HINSTANCE );
3793         bRet = DialogBoxIndirectParamW( hInst, lfs->template, lpofn->hwndOwner,
3794                                         FD32_FileOpenDlgProc, (LPARAM)lfs);
3795         FD31_DestroyPrivate(lfs);
3796     }
3797
3798     TRACE("file %s, file offset %d, ext offset %d\n",
3799           debugstr_w(lpofn->lpstrFile), lpofn->nFileOffset, lpofn->nFileExtension);
3800     return bRet;
3801 }
3802
3803 /* ------------------ APIs ---------------------- */
3804
3805 /***********************************************************************
3806  *            GetOpenFileNameA  (COMDLG32.@)
3807  *
3808  * Creates a dialog box for the user to select a file to open.
3809  *
3810  * RETURNS
3811  *    TRUE on success: user enters a valid file
3812  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
3813  *
3814  */
3815 BOOL WINAPI GetOpenFileNameA(
3816         LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
3817 {
3818     BOOL win16look = FALSE;
3819
3820     TRACE("flags %08lx\n", ofn->Flags);
3821
3822     /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
3823     if (ofn->Flags & OFN_FILEMUSTEXIST)
3824         ofn->Flags |= OFN_PATHMUSTEXIST;
3825
3826     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
3827         win16look = (ofn->Flags & OFN_EXPLORER) ? FALSE : TRUE;
3828
3829     if (win16look)
3830         return GetFileName31A(ofn, OPEN_DIALOG);
3831     else
3832         return GetFileDialog95A(ofn, OPEN_DIALOG);
3833 }
3834
3835 /***********************************************************************
3836  *            GetOpenFileNameW (COMDLG32.@)
3837  *
3838  * Creates a dialog box for the user to select a file to open.
3839  *
3840  * RETURNS
3841  *    TRUE on success: user enters a valid file
3842  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
3843  *
3844  */
3845 BOOL WINAPI GetOpenFileNameW(
3846         LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
3847 {
3848     BOOL win16look = FALSE;
3849
3850     TRACE("flags %08lx\n", ofn->Flags);
3851
3852     /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
3853     if (ofn->Flags & OFN_FILEMUSTEXIST)
3854         ofn->Flags |= OFN_PATHMUSTEXIST;
3855
3856     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
3857         win16look = (ofn->Flags & OFN_EXPLORER) ? FALSE : TRUE;
3858
3859     if (win16look)
3860         return GetFileName31W(ofn, OPEN_DIALOG);
3861     else
3862         return GetFileDialog95W(ofn, OPEN_DIALOG);
3863 }
3864
3865
3866 /***********************************************************************
3867  *            GetSaveFileNameA  (COMDLG32.@)
3868  *
3869  * Creates a dialog box for the user to select a file to save.
3870  *
3871  * RETURNS
3872  *    TRUE on success: user enters a valid file
3873  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
3874  *
3875  */
3876 BOOL WINAPI GetSaveFileNameA(
3877         LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
3878 {
3879     BOOL win16look = FALSE;
3880
3881     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
3882         win16look = (ofn->Flags & OFN_EXPLORER) ? FALSE : TRUE;
3883
3884     if (win16look)
3885         return GetFileName31A(ofn, SAVE_DIALOG);
3886     else
3887         return GetFileDialog95A(ofn, SAVE_DIALOG);
3888 }
3889
3890 /***********************************************************************
3891  *            GetSaveFileNameW  (COMDLG32.@)
3892  *
3893  * Creates a dialog box for the user to select a file to save.
3894  *
3895  * RETURNS
3896  *    TRUE on success: user enters a valid file
3897  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
3898  *
3899  */
3900 BOOL WINAPI GetSaveFileNameW(
3901         LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
3902 {
3903     BOOL win16look = FALSE;
3904
3905     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
3906         win16look = (ofn->Flags & OFN_EXPLORER) ? FALSE : TRUE;
3907
3908     if (win16look)
3909         return GetFileName31W(ofn, SAVE_DIALOG);
3910     else
3911         return GetFileDialog95W(ofn, SAVE_DIALOG);
3912 }