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