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