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