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