xref: /reactos/dll/win32/comdlg32/filedlg.c (revision 58588b76)
1 /*
2  * COMMDLG - File Open Dialogs Win95 look and feel
3  *
4  * Copyright 1999 Francois Boisvert
5  * Copyright 1999, 2000 Juergen Schmied
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * FIXME: The whole concept of handling unicode is badly broken.
22  *	many hook-messages expect a pointer to a
23  *	OPENFILENAMEA or W structure. With the current architecture
24  *	we would have to convert the beast at every call to a hook.
25  *	we have to find a better solution but it would likely cause
26  *	a complete rewrite after which we should handle the
27  *	OPENFILENAME structure without any converting (jsch).
28  *
29  * FIXME: any hook gets a OPENFILENAMEA structure
30  *
31  * FIXME: CDN_FILEOK is wrong implemented, other CDN_ messages likely too
32  *
33  * FIXME: old style hook messages are not implemented (except FILEOKSTRING)
34  *
35  * FIXME: algorithm for selecting the initial directory is too simple
36  *
37  * FIXME: add to recent docs
38  *
39  * FIXME: flags not implemented: OFN_DONTADDTORECENT,
40  * OFN_NODEREFERENCELINKS, OFN_NOREADONLYRETURN,
41  * OFN_NOTESTFILECREATE, OFN_USEMONIKERS
42  *
43  * FIXME: lCustData for lpfnHook (WM_INITDIALOG)
44  *
45  *
46  */
47 
48 #include "config.h"
49 #include "wine/port.h"
50 
51 #include <ctype.h>
52 #include <stdlib.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <string.h>
56 
57 #define COBJMACROS
58 #define NONAMELESSUNION
59 
60 #include "windef.h"
61 #include "winbase.h"
62 #include "winternl.h"
63 #include "winnls.h"
64 #include "wingdi.h"
65 #ifdef __REACTOS__
66 /* RegGetValueW is supported by Win2k3 SP1 but headers need Win Vista */
67 #undef _WIN32_WINNT
68 #define _WIN32_WINNT 0x0600
69 #endif
70 #include "winreg.h"
71 #include "winuser.h"
72 #include "commdlg.h"
73 #include "dlgs.h"
74 #include "cdlg.h"
75 #include "cderr.h"
76 #include "shellapi.h"
77 #include "shlobj.h"
78 #include "filedlgbrowser.h"
79 #include "shlwapi.h"
80 
81 #include "wine/unicode.h"
82 #include "wine/debug.h"
83 #include "wine/heap.h"
84 
85 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
86 
87 #define UNIMPLEMENTED_FLAGS \
88 (OFN_DONTADDTORECENT |\
89 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
90 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
91 
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 
111 /***********************************************************************
112  * Defines and global variables
113  */
114 
115 /* Draw item constant */
116 #define XTEXTOFFSET 3
117 
118 /* AddItem flags*/
119 #define LISTEND -1
120 
121 /* SearchItem methods */
122 #define SEARCH_PIDL 1
123 #define SEARCH_EXP  2
124 #define ITEM_NOTFOUND -1
125 
126 /* Undefined windows message sent by CreateViewObject*/
127 #define WM_GETISHELLBROWSER  WM_USER+7
128 
129 #define TBPLACES_CMDID_PLACE0    0xa064
130 #define TBPLACES_CMDID_PLACE1    0xa065
131 #define TBPLACES_CMDID_PLACE2    0xa066
132 #define TBPLACES_CMDID_PLACE3    0xa067
133 #define TBPLACES_CMDID_PLACE4    0xa068
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 CBGetItemDataPtr(hwnd,iItemId) \
142     SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
143 
144 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
145 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
146 
147 static const WCHAR LastVisitedMRUW[] =
148     {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
149         'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
150         'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
151         'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
152 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
153 
154 static const WCHAR filedlg_info_propnameW[] = {'F','i','l','e','O','p','e','n','D','l','g','I','n','f','o','s',0};
155 
156 FileOpenDlgInfos *get_filedlg_infoptr(HWND hwnd)
157 {
158     return GetPropW(hwnd, filedlg_info_propnameW);
159 }
160 
161 static BOOL is_dialog_hooked(const FileOpenDlgInfos *info)
162 {
163     return (info->ofnInfos->Flags & OFN_ENABLEHOOK) && info->ofnInfos->lpfnHook;
164 }
165 
166 static BOOL filedialog_is_readonly_hidden(const FileOpenDlgInfos *info)
167 {
168     return (info->ofnInfos->Flags & OFN_HIDEREADONLY) || (info->DlgInfos.dwDlgProp & FODPROP_SAVEDLG);
169 }
170 
171 /***********************************************************************
172  * Prototypes
173  */
174 
175 /* Internal functions used by the dialog */
176 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
177 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
178 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
179 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
180 static BOOL    FILEDLG95_OnOpen(HWND hwnd);
181 static LRESULT FILEDLG95_InitControls(HWND hwnd);
182 static void    FILEDLG95_Clean(HWND hwnd);
183 
184 /* Functions used by the shell navigation */
185 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
186 static BOOL    FILEDLG95_SHELL_UpFolder(HWND hwnd);
187 static BOOL    FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
188 static void    FILEDLG95_SHELL_Clean(HWND hwnd);
189 
190 /* Functions used by the EDIT box */
191 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
192 
193 /* Functions used by the filetype combo box */
194 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
195 static BOOL    FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
196 static int     FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
197 static void    FILEDLG95_FILETYPE_Clean(HWND hwnd);
198 
199 /* Functions used by the Look In combo box */
200 static void    FILEDLG95_LOOKIN_Init(HWND hwndCombo);
201 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
202 static BOOL    FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
203 static int     FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
204 static int     FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
205 static int     FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
206 static int     FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
207        int     FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
208 static void    FILEDLG95_LOOKIN_Clean(HWND hwnd);
209 
210 /* Functions for dealing with the most-recently-used registry keys */
211 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
212 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
213 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
214 #ifdef __REACTOS__
215 static void FILEDLG95_MRU_load_ext(LPWSTR stored_path, size_t cchMax, LPCWSTR defext);
216 static void FILEDLG95_MRU_save_ext(LPCWSTR filename);
217 #endif
218 
219 /* Miscellaneous tool functions */
220 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
221 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
222 LPITEMIDLIST  GetParentPidl(LPITEMIDLIST pidl);
223 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
224 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
225 static UINT GetNumSelected( IDataObject *doSelected );
226 static void COMCTL32_ReleaseStgMedium(STGMEDIUM medium);
227 
228 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
229 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
230 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
231 static BOOL BrowseSelectedFolder(HWND hwnd);
232 
233 static BOOL get_config_key_as_dword(HKEY hkey, const WCHAR *name, DWORD *value)
234 {
235     DWORD type, data, size;
236 
237     size = sizeof(data);
238     if (hkey && !RegQueryValueExW(hkey, name, 0, &type, (BYTE *)&data, &size))
239     {
240         *value = data;
241         return TRUE;
242     }
243 
244     return FALSE;
245 }
246 
247 static BOOL get_config_key_dword(HKEY hkey, const WCHAR *name, DWORD *value)
248 {
249     DWORD type, data, size;
250 
251     size = sizeof(data);
252     if (hkey && !RegQueryValueExW(hkey, name, 0, &type, (BYTE *)&data, &size) && type == REG_DWORD)
253     {
254         *value = data;
255         return TRUE;
256     }
257 
258     return FALSE;
259 }
260 
261 static BOOL get_config_key_string(HKEY hkey, const WCHAR *name, WCHAR **value)
262 {
263     DWORD type, size;
264     WCHAR *str;
265 
266     if (hkey && !RegQueryValueExW(hkey, name, 0, &type, NULL, &size))
267     {
268         if (type != REG_SZ && type != REG_EXPAND_SZ)
269             return FALSE;
270     }
271 
272     str = heap_alloc(size);
273     if (RegQueryValueExW(hkey, name, 0, &type, (BYTE *)str, &size))
274     {
275         heap_free(str);
276         return FALSE;
277     }
278 
279     *value = str;
280     return TRUE;
281 }
282 
283 static BOOL is_places_bar_enabled(const FileOpenDlgInfos *fodInfos)
284 {
285     static const WCHAR noplacesbarW[] = {'N','o','P','l','a','c','e','s','B','a','r',0};
286     DWORD value;
287     HKEY hkey;
288 
289     if (fodInfos->ofnInfos->lStructSize != sizeof(*fodInfos->ofnInfos) ||
290             (fodInfos->ofnInfos->FlagsEx & OFN_EX_NOPLACESBAR) ||
291            !(fodInfos->ofnInfos->Flags & OFN_EXPLORER))
292     {
293         return FALSE;
294     }
295 
296     if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32", &hkey))
297         return TRUE;
298 
299     value = 0;
300     get_config_key_as_dword(hkey, noplacesbarW, &value);
301     RegCloseKey(hkey);
302     return value == 0;
303 }
304 
305 static void filedlg_collect_places_pidls(FileOpenDlgInfos *fodInfos)
306 {
307     static const int default_places[] =
308     {
309         CSIDL_DESKTOP,
310         CSIDL_MYDOCUMENTS,
311         CSIDL_DRIVES,
312     };
313     unsigned int i;
314     HKEY hkey;
315 
316     if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32\\Placesbar",
317             &hkey))
318     {
319         for (i = 0; i < ARRAY_SIZE(fodInfos->places); i++)
320         {
321             static const WCHAR placeW[] = {'P','l','a','c','e','%','d',0};
322             WCHAR nameW[8];
323             DWORD value;
324             HRESULT hr;
325             WCHAR *str;
326 
327             sprintfW(nameW, placeW, i);
328             if (get_config_key_dword(hkey, nameW, &value))
329             {
330                 hr = SHGetSpecialFolderLocation(NULL, value, &fodInfos->places[i]);
331                 if (FAILED(hr))
332                     WARN("Unrecognized special folder %u.\n", value);
333             }
334             else if (get_config_key_string(hkey, nameW, &str))
335             {
336                 hr = SHParseDisplayName(str, NULL, &fodInfos->places[i], 0, NULL);
337                 if (FAILED(hr))
338                     WARN("Failed to parse custom places location, %s.\n", debugstr_w(str));
339                 heap_free(str);
340             }
341         }
342 
343         /* FIXME: eliminate duplicates. */
344 
345         RegCloseKey(hkey);
346         return;
347     }
348 
349     for (i = 0; i < ARRAY_SIZE(default_places); i++)
350         SHGetSpecialFolderLocation(NULL, default_places[i], &fodInfos->places[i]);
351 }
352 
353 /***********************************************************************
354  *      GetFileName95
355  *
356  * Creates an Open common dialog box that lets the user select
357  * the drive, directory, and the name of a file or set of files to open.
358  *
359  * IN  : The FileOpenDlgInfos structure associated with the dialog
360  * OUT : TRUE on success
361  *       FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
362  */
363 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
364 {
365     LRESULT lRes;
366     void *template;
367     HRSRC hRes;
368     HANDLE hDlgTmpl = 0;
369     WORD templateid;
370 
371     /* test for missing functionality */
372     if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
373     {
374       FIXME("Flags 0x%08x not yet implemented\n",
375          fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
376     }
377 
378     /* Create the dialog from a template */
379 
380     if (is_places_bar_enabled(fodInfos))
381         templateid = NEWFILEOPENV2ORD;
382     else
383         templateid = NEWFILEOPENORD;
384 
385     if (!(hRes = FindResourceW(COMDLG32_hInstance, MAKEINTRESOURCEW(templateid), (LPCWSTR)RT_DIALOG)))
386     {
387         COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
388         return FALSE;
389     }
390     if (!(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes )) ||
391         !(template = LockResource( hDlgTmpl )))
392     {
393         COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
394         return FALSE;
395     }
396 
397     /* msdn: explorer style dialogs permit sizing by default.
398      * The OFN_ENABLESIZING flag is only needed when a hook or
399      * custom template is provided */
400     if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
401             !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
402         fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
403 
404     if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
405     {
406         fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
407         fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
408     }
409 
410     /* old style hook messages */
411     if (is_dialog_hooked(fodInfos))
412     {
413       fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
414       fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
415       fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
416       fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
417     }
418 
419     if (fodInfos->unicode)
420       lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
421                                      template,
422                                      fodInfos->ofnInfos->hwndOwner,
423                                      FileOpenDlgProc95,
424                                      (LPARAM) fodInfos);
425     else
426       lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
427                                      template,
428                                      fodInfos->ofnInfos->hwndOwner,
429                                      FileOpenDlgProc95,
430                                      (LPARAM) fodInfos);
431     if (fodInfos->ole_initialized)
432         OleUninitialize();
433 
434     /* Unable to create the dialog */
435     if( lRes == -1)
436         return FALSE;
437 
438     return lRes;
439 }
440 
441 static WCHAR *heap_strdupAtoW(const char *str)
442 {
443     WCHAR *ret;
444     INT len;
445 
446     if (!str)
447         return NULL;
448 
449     len = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
450     ret = heap_alloc(len * sizeof(WCHAR));
451     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
452 
453     return ret;
454 }
455 
456 static void init_filedlg_infoW(OPENFILENAMEW *ofn, FileOpenDlgInfos *info)
457 {
458     INITCOMMONCONTROLSEX icc;
459 
460     /* Initialize ComboBoxEx32 */
461     icc.dwSize = sizeof(icc);
462     icc.dwICC = ICC_USEREX_CLASSES;
463     InitCommonControlsEx(&icc);
464 
465     /* Initialize CommDlgExtendedError() */
466     COMDLG32_SetCommDlgExtendedError(0);
467 
468     memset(info, 0, sizeof(*info));
469 
470     /* Pass in the original ofn */
471     info->ofnInfos = ofn;
472 
473     info->title = ofn->lpstrTitle;
474     info->defext = ofn->lpstrDefExt;
475     info->filter = ofn->lpstrFilter;
476     info->customfilter = ofn->lpstrCustomFilter;
477 
478     if (ofn->lpstrFile)
479     {
480         info->filename = heap_alloc(ofn->nMaxFile * sizeof(WCHAR));
481         lstrcpynW(info->filename, ofn->lpstrFile, ofn->nMaxFile);
482     }
483 
484     if (ofn->lpstrInitialDir)
485     {
486         DWORD len = ExpandEnvironmentStringsW(ofn->lpstrInitialDir, NULL, 0);
487         if (len)
488         {
489             info->initdir = heap_alloc(len * sizeof(WCHAR));
490             ExpandEnvironmentStringsW(ofn->lpstrInitialDir, info->initdir, len);
491         }
492     }
493 
494     info->unicode = TRUE;
495 }
496 
497 static void init_filedlg_infoA(OPENFILENAMEA *ofn, FileOpenDlgInfos *info)
498 {
499     OPENFILENAMEW ofnW;
500     int len;
501 
502     ofnW = *(OPENFILENAMEW *)ofn;
503 
504     ofnW.lpstrInitialDir = heap_strdupAtoW(ofn->lpstrInitialDir);
505     ofnW.lpstrDefExt = heap_strdupAtoW(ofn->lpstrDefExt);
506     ofnW.lpstrTitle = heap_strdupAtoW(ofn->lpstrTitle);
507 
508     if (ofn->lpstrFile)
509     {
510         len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, NULL, 0);
511         ofnW.lpstrFile = heap_alloc(len * sizeof(WCHAR));
512         MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, ofnW.lpstrFile, len);
513         ofnW.nMaxFile = len;
514     }
515 
516     if (ofn->lpstrFilter)
517     {
518         LPCSTR s;
519         int n;
520 
521         /* filter is a list...  title\0ext\0......\0\0 */
522         s = ofn->lpstrFilter;
523         while (*s) s = s+strlen(s)+1;
524         s++;
525         n = s - ofn->lpstrFilter;
526         len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0);
527         ofnW.lpstrFilter = heap_alloc(len * sizeof(WCHAR));
528         MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, (WCHAR *)ofnW.lpstrFilter, len);
529     }
530 
531     /* convert lpstrCustomFilter */
532     if (ofn->lpstrCustomFilter)
533     {
534         int n, len;
535         LPCSTR s;
536 
537         /* customfilter contains a pair of strings...  title\0ext\0 */
538         s = ofn->lpstrCustomFilter;
539         if (*s) s = s+strlen(s)+1;
540         if (*s) s = s+strlen(s)+1;
541         n = s - ofn->lpstrCustomFilter;
542         len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0);
543         ofnW.lpstrCustomFilter = heap_alloc(len * sizeof(WCHAR));
544         MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, ofnW.lpstrCustomFilter, len);
545     }
546 
547     init_filedlg_infoW(&ofnW, info);
548 
549     /* fixup A-specific fields */
550     info->ofnInfos = (OPENFILENAMEW *)ofn;
551     info->unicode = FALSE;
552 
553     /* free what was duplicated */
554     heap_free((void *)ofnW.lpstrInitialDir);
555     heap_free(ofnW.lpstrFile);
556 }
557 
558 /***********************************************************************
559  *      GetFileDialog95
560  *
561  * Call GetFileName95 with this structure and clean the memory.
562  */
563 static BOOL GetFileDialog95(FileOpenDlgInfos *info, UINT dlg_type)
564 {
565     WCHAR *current_dir = NULL;
566     unsigned int i;
567     BOOL ret;
568 
569     /* save current directory */
570     if (info->ofnInfos->Flags & OFN_NOCHANGEDIR)
571     {
572         current_dir = heap_alloc(MAX_PATH * sizeof(WCHAR));
573         GetCurrentDirectoryW(MAX_PATH, current_dir);
574     }
575 
576     switch (dlg_type)
577     {
578     case OPEN_DIALOG:
579         ret = GetFileName95(info);
580         break;
581     case SAVE_DIALOG:
582         info->DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
583         ret = GetFileName95(info);
584         break;
585     default:
586         ret = FALSE;
587     }
588 
589     /* set the lpstrFileTitle */
590     if (ret && info->ofnInfos->lpstrFile && info->ofnInfos->lpstrFileTitle)
591     {
592         if (info->unicode)
593         {
594             LPOPENFILENAMEW ofn = info->ofnInfos;
595             WCHAR *file_title = PathFindFileNameW(ofn->lpstrFile);
596             lstrcpynW(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
597         }
598         else
599         {
600             LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)info->ofnInfos;
601             char *file_title = PathFindFileNameA(ofn->lpstrFile);
602             lstrcpynA(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
603         }
604     }
605 
606     if (current_dir)
607     {
608         SetCurrentDirectoryW(current_dir);
609         heap_free(current_dir);
610     }
611 
612     if (!info->unicode)
613     {
614         heap_free((void *)info->defext);
615         heap_free((void *)info->title);
616         heap_free((void *)info->filter);
617         heap_free((void *)info->customfilter);
618     }
619 
620     heap_free(info->filename);
621     heap_free(info->initdir);
622 
623     for (i = 0; i < ARRAY_SIZE(info->places); i++)
624         ILFree(info->places[i]);
625 
626     return ret;
627 }
628 
629 /******************************************************************************
630  * COMDLG32_GetDisplayNameOf [internal]
631  *
632  * Helper function to get the display name for a pidl.
633  */
634 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
635     LPSHELLFOLDER psfDesktop;
636     STRRET strret;
637 
638     if (FAILED(SHGetDesktopFolder(&psfDesktop)))
639         return FALSE;
640 
641     if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
642         IShellFolder_Release(psfDesktop);
643         return FALSE;
644     }
645 
646     IShellFolder_Release(psfDesktop);
647     return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
648 }
649 
650 /******************************************************************************
651  * COMDLG32_GetCanonicalPath [internal]
652  *
653  * Helper function to get the canonical path.
654  */
655 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
656                                LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
657 {
658   WCHAR lpstrTemp[MAX_PATH];
659 
660   /* Get the current directory name */
661   if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
662   {
663     /* last fallback */
664     GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
665   }
666   PathAddBackslashW(lpstrPathAndFile);
667 
668   TRACE("current directory=%s, file=%s\n", debugstr_w(lpstrPathAndFile), debugstr_w(lpstrFile));
669 
670   /* if the user specified a fully qualified path use it */
671   if(PathIsRelativeW(lpstrFile))
672   {
673     lstrcatW(lpstrPathAndFile, lpstrFile);
674   }
675   else
676   {
677     /* does the path have a drive letter? */
678     if (PathGetDriveNumberW(lpstrFile) == -1)
679       lstrcpyW(lpstrPathAndFile+2, lpstrFile);
680     else
681       lstrcpyW(lpstrPathAndFile, lpstrFile);
682   }
683 
684   /* resolve "." and ".." */
685   PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
686   lstrcpyW(lpstrPathAndFile, lpstrTemp);
687   TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
688 }
689 
690 /***********************************************************************
691  *      COMDLG32_SplitFileNames [internal]
692  *
693  * Creates a delimited list of filenames.
694  */
695 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
696 {
697 	UINT nStrCharCount = 0;	/* index in src buffer */
698 	UINT nFileIndex = 0;	/* index in dest buffer */
699 	UINT nFileCount = 0;	/* number of files */
700 
701 	/* we might get single filename without any '"',
702 	 * so we need nStrLen + terminating \0 + end-of-list \0 */
703 	*lpstrFileList = heap_alloc((nStrLen + 2) * sizeof(WCHAR));
704 	*sizeUsed = 0;
705 
706 	/* build delimited file list from filenames */
707 	while ( nStrCharCount <= nStrLen )
708 	{
709 	  if ( lpstrEdit[nStrCharCount]=='"' )
710 	  {
711 	    nStrCharCount++;
712 	    while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
713 	    {
714 	      (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
715 	      nStrCharCount++;
716 	    }
717 	    (*lpstrFileList)[nFileIndex++] = 0;
718 	    nFileCount++;
719 	  }
720 	  nStrCharCount++;
721 	}
722 
723 	/* single, unquoted string */
724 	if ((nStrLen > 0) && (nFileIndex == 0) )
725 	{
726 	  lstrcpyW(*lpstrFileList, lpstrEdit);
727 	  nFileIndex = lstrlenW(lpstrEdit) + 1;
728 	  nFileCount = 1;
729 	}
730 
731         /* trailing \0 */
732         (*lpstrFileList)[nFileIndex++] = '\0';
733 
734         *sizeUsed = nFileIndex;
735 	return nFileCount;
736 }
737 
738 /***********************************************************************
739  *      ArrangeCtrlPositions [internal]
740  *
741  * NOTE: Make sure to add testcases for any changes made here.
742  */
743 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
744 {
745     HWND hwndChild, hwndStc32;
746     RECT rectParent, rectChild, rectStc32;
747     INT help_fixup = 0;
748     int chgx, chgy;
749 
750     /* Take into account if open as read only checkbox and help button
751      * are hidden
752      */
753      if (hide_help)
754      {
755          RECT rectHelp, rectCancel;
756          GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
757          GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
758          /* subtract the height of the help button plus the space between
759           * the help button and the cancel button to the height of the dialog
760           */
761           help_fixup = rectHelp.bottom - rectCancel.bottom;
762     }
763 
764     /*
765       There are two possibilities to add components to the default file dialog box.
766 
767       By default, all the new components are added below the standard dialog box (the else case).
768 
769       However, if there is a static text component with the stc32 id, a special case happens.
770       The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
771       in the window and the cx and cy indicate how to size the window.
772       Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
773       of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
774 
775      */
776 
777     GetClientRect(hwndParentDlg, &rectParent);
778 
779     /* when arranging controls we have to use fixed parent size */
780     rectParent.bottom -= help_fixup;
781 
782     hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
783     if (hwndStc32)
784     {
785         GetWindowRect(hwndStc32, &rectStc32);
786         MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
787 
788         /* set the size of the stc32 control according to the size of
789          * client area of the parent dialog
790          */
791         SetWindowPos(hwndStc32, 0,
792                      0, 0,
793                      rectParent.right, rectParent.bottom,
794                      SWP_NOMOVE | SWP_NOZORDER);
795     }
796     else
797         SetRectEmpty(&rectStc32);
798 
799     /* this part moves controls of the child dialog */
800     hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
801     while (hwndChild)
802     {
803         if (hwndChild != hwndStc32)
804         {
805             GetWindowRect(hwndChild, &rectChild);
806             MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
807 
808             /* move only if stc32 exist */
809             if (hwndStc32 && rectChild.left > rectStc32.right)
810             {
811                 /* move to the right of visible controls of the parent dialog */
812                 rectChild.left += rectParent.right;
813                 rectChild.left -= rectStc32.right;
814             }
815             /* move even if stc32 doesn't exist */
816             if (rectChild.top >= rectStc32.bottom)
817             {
818                 /* move below visible controls of the parent dialog */
819                 rectChild.top += rectParent.bottom;
820                 rectChild.top -= rectStc32.bottom - rectStc32.top;
821             }
822 
823             SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
824                          0, 0, SWP_NOSIZE | SWP_NOZORDER);
825         }
826         hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
827     }
828 
829     /* this part moves controls of the parent dialog */
830     hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
831     while (hwndChild)
832     {
833         if (hwndChild != hwndChildDlg)
834         {
835             GetWindowRect(hwndChild, &rectChild);
836             MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
837 
838             /* left,top of stc32 marks the position of controls
839              * from the parent dialog
840              */
841             rectChild.left += rectStc32.left;
842             rectChild.top += rectStc32.top;
843 
844             SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
845                          0, 0, SWP_NOSIZE | SWP_NOZORDER);
846         }
847         hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
848     }
849 
850     /* calculate the size of the resulting dialog */
851 
852     /* here we have to use original parent size */
853     GetClientRect(hwndParentDlg, &rectParent);
854     GetClientRect(hwndChildDlg, &rectChild);
855     TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
856             wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
857 
858     if (hwndStc32)
859     {
860         /* width */
861         if (rectParent.right > rectStc32.right - rectStc32.left)
862             chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
863         else
864             chgx = rectChild.right - rectParent.right;
865         /* height */
866         if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
867             chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
868         else
869             /* Unconditionally set new dialog
870              * height to that of the child
871              */
872             chgy = rectChild.bottom - rectParent.bottom;
873     }
874     else
875     {
876         chgx = 0;
877         chgy = rectChild.bottom - help_fixup;
878     }
879     /* set the size of the parent dialog */
880     GetWindowRect(hwndParentDlg, &rectParent);
881     SetWindowPos(hwndParentDlg, 0,
882                  0, 0,
883                  rectParent.right - rectParent.left + chgx,
884                  rectParent.bottom - rectParent.top + chgy,
885                  SWP_NOMOVE | SWP_NOZORDER);
886 }
887 
888 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
889 {
890     switch(uMsg) {
891     case WM_INITDIALOG:
892         return TRUE;
893     }
894     return FALSE;
895 }
896 
897 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
898 {
899     LPCVOID template;
900     HRSRC hRes;
901     HANDLE hDlgTmpl = 0;
902     HWND hChildDlg = 0;
903 
904     TRACE("%p, %p\n", fodInfos, hwnd);
905 
906     /*
907      * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
908      * structure's hInstance parameter is not a HINSTANCE, but
909      * instead a pointer to a template resource to use.
910      */
911     if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
912     {
913       HINSTANCE hinst;
914       if (fodInfos->ofnInfos->Flags  & OFN_ENABLETEMPLATEHANDLE)
915       {
916         hinst = COMDLG32_hInstance;
917         if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
918         {
919           COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
920           return NULL;
921         }
922       }
923       else
924       {
925         hinst = fodInfos->ofnInfos->hInstance;
926         if(fodInfos->unicode)
927         {
928             LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
929             hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
930         }
931         else
932         {
933             LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
934             hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
935         }
936         if (!hRes)
937         {
938           COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
939           return NULL;
940         }
941         if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
942             !(template = LockResource( hDlgTmpl )))
943         {
944           COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
945           return NULL;
946     	}
947       }
948       if (fodInfos->unicode)
949           hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
950               is_dialog_hooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
951               (LPARAM)fodInfos->ofnInfos);
952       else
953           hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
954               is_dialog_hooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
955               (LPARAM)fodInfos->ofnInfos);
956       return hChildDlg;
957     }
958     else if (is_dialog_hooked(fodInfos))
959     {
960       RECT rectHwnd;
961       struct  {
962          DLGTEMPLATE tmplate;
963          WORD menu,class,title;
964          } temp;
965       GetClientRect(hwnd,&rectHwnd);
966       temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
967       temp.tmplate.dwExtendedStyle = 0;
968       temp.tmplate.cdit = 0;
969       temp.tmplate.x = 0;
970       temp.tmplate.y = 0;
971       temp.tmplate.cx = 0;
972       temp.tmplate.cy = 0;
973       temp.menu = temp.class = temp.title = 0;
974 
975       hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
976                   hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
977 
978       return hChildDlg;
979     }
980     return NULL;
981 }
982 
983 /***********************************************************************
984 *          SendCustomDlgNotificationMessage
985 *
986 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
987 */
988 
989 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
990 {
991     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwndParentDlg);
992     LRESULT hook_result;
993     OFNOTIFYW ofnNotify;
994 
995     TRACE("%p %d\n", hwndParentDlg, uCode);
996 
997     if (!fodInfos || !fodInfos->DlgInfos.hwndCustomDlg)
998         return 0;
999 
1000     TRACE("CALL NOTIFY for %d\n", uCode);
1001 
1002     ofnNotify.hdr.hwndFrom = hwndParentDlg;
1003     ofnNotify.hdr.idFrom = 0;
1004     ofnNotify.hdr.code = uCode;
1005     ofnNotify.lpOFN = fodInfos->ofnInfos;
1006     ofnNotify.pszFile = NULL;
1007 
1008     if (fodInfos->unicode)
1009         hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
1010     else
1011         hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
1012 
1013     TRACE("RET NOTIFY retval %#lx\n", hook_result);
1014 
1015     return hook_result;
1016 }
1017 
1018 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
1019 {
1020     UINT len, total;
1021     WCHAR *p, *buffer;
1022     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1023 
1024     TRACE("CDM_GETFILEPATH:\n");
1025 
1026     if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
1027         return -1;
1028 
1029     /* get path and filenames */
1030     len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
1031     buffer = heap_alloc( (len + 2 + MAX_PATH) * sizeof(WCHAR) );
1032     COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
1033     if (len)
1034     {
1035         p = buffer + strlenW(buffer);
1036         *p++ = '\\';
1037         SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
1038     }
1039     if (fodInfos->unicode)
1040     {
1041         total = strlenW( buffer) + 1;
1042         if (result) lstrcpynW( result, buffer, size );
1043         TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
1044     }
1045     else
1046     {
1047         total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
1048         if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
1049         TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
1050     }
1051     heap_free( buffer );
1052     return total;
1053 }
1054 
1055 /***********************************************************************
1056 *         FILEDLG95_HandleCustomDialogMessages
1057 *
1058 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
1059 */
1060 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1061 {
1062     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1063     WCHAR lpstrPath[MAX_PATH];
1064     INT_PTR retval;
1065 
1066     if(!fodInfos) return FALSE;
1067 
1068     switch(uMsg)
1069     {
1070         case CDM_GETFILEPATH:
1071             retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
1072             break;
1073 
1074         case CDM_GETFOLDERPATH:
1075             TRACE("CDM_GETFOLDERPATH:\n");
1076             COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
1077             if (lParam)
1078             {
1079                 if (fodInfos->unicode)
1080                     lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
1081                 else
1082                     WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
1083                                         (LPSTR)lParam, (int)wParam, NULL, NULL);
1084             }
1085             retval = lstrlenW(lpstrPath) + 1;
1086             break;
1087 
1088         case CDM_GETFOLDERIDLIST:
1089             retval = ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
1090             if (retval <= wParam)
1091                 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
1092             break;
1093 
1094         case CDM_GETSPEC:
1095             TRACE("CDM_GETSPEC:\n");
1096             retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
1097             if (lParam)
1098             {
1099                 if (fodInfos->unicode)
1100                     SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1101                 else
1102                     SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1103             }
1104             break;
1105 
1106         case CDM_SETCONTROLTEXT:
1107             TRACE("CDM_SETCONTROLTEXT:\n");
1108 	    if ( lParam )
1109             {
1110                 if( fodInfos->unicode )
1111 	            SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
1112                 else
1113 	            SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
1114             }
1115             retval = TRUE;
1116             break;
1117 
1118         case CDM_HIDECONTROL:
1119             /* MSDN states that it should fail for not OFN_EXPLORER case */
1120             if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1121             {
1122                 HWND control = GetDlgItem( hwnd, wParam );
1123                 if (control) ShowWindow( control, SW_HIDE );
1124                 retval = TRUE;
1125             }
1126             else retval = FALSE;
1127             break;
1128 
1129         default:
1130             if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1131                 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
1132             return FALSE;
1133     }
1134     SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
1135     return TRUE;
1136 }
1137 
1138 /***********************************************************************
1139  *          FILEDLG95_OnWMGetMMI
1140  *
1141  * WM_GETMINMAXINFO message handler for resizable dialogs
1142  */
1143 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
1144 {
1145     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1146     if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1147     if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1148     {
1149         mmiptr->ptMinTrackSize = fodInfos->initial_size;
1150     }
1151     return TRUE;
1152 }
1153 
1154 /***********************************************************************
1155  *          FILEDLG95_OnWMSize
1156  *
1157  * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1158  *
1159  * FIXME: this could be made more elaborate. Now use a simple scheme
1160  * where the file view is enlarged and the controls are either moved
1161  * vertically or horizontally to get out of the way. Only the "grip"
1162  * is moved in both directions to stay in the corner.
1163  */
1164 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1165 {
1166     RECT rc, rcview;
1167     int chgx, chgy;
1168     HWND ctrl;
1169     HDWP hdwp;
1170     FileOpenDlgInfos *fodInfos;
1171 
1172     if( wParam != SIZE_RESTORED) return FALSE;
1173     fodInfos = get_filedlg_infoptr(hwnd);
1174     if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1175     /* get the new dialog rectangle */
1176     GetWindowRect( hwnd, &rc);
1177     TRACE("%p, size from %d,%d to %d,%d\n", hwnd, fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1178             rc.right -rc.left, rc.bottom -rc.top);
1179     /* not initialized yet */
1180     if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1181         ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1182              (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1183         return FALSE;
1184     chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1185     chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1186     fodInfos->sizedlg.cx = rc.right - rc.left;
1187     fodInfos->sizedlg.cy = rc.bottom - rc.top;
1188     /* change the size of the view window */
1189     GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1190     MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1191     hdwp = BeginDeferWindowPos( 10);
1192     DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1193             rcview.right - rcview.left + chgx,
1194             rcview.bottom - rcview.top + chgy,
1195             SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1196     /* change position and sizes of the controls */
1197     for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1198     {
1199         int ctrlid = GetDlgCtrlID( ctrl);
1200         GetWindowRect( ctrl, &rc);
1201         MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1202         if( ctrl == fodInfos->DlgInfos.hwndGrip)
1203         {
1204             DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1205                     0, 0,
1206                     SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1207         }
1208         else if( rc.top > rcview.bottom)
1209         {
1210             /* if it was below the shell view
1211              * move to bottom */
1212             switch( ctrlid)
1213             {
1214                 /* file name (edit or comboboxex) and file types combo change also width */
1215                 case edt1:
1216                 case cmb13:
1217                 case cmb1:
1218                     DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1219                             rc.right - rc.left + chgx, rc.bottom - rc.top,
1220                             SWP_NOACTIVATE | SWP_NOZORDER);
1221                     break;
1222                     /* then these buttons must move out of the way */
1223                 case IDOK:
1224                 case IDCANCEL:
1225                 case pshHelp:
1226                     DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1227                             0, 0,
1228                             SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1229                     break;
1230                 default:
1231                 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1232                         0, 0,
1233                         SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1234             }
1235         }
1236         else if( rc.left > rcview.right)
1237         {
1238             /* if it was to the right of the shell view
1239              * move to right */
1240             DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1241                     0, 0,
1242                     SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1243         }
1244         else
1245             /* special cases */
1246         {
1247             switch( ctrlid)
1248             {
1249 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1250                 case IDC_LOOKIN:
1251                     DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1252                             rc.right - rc.left + chgx, rc.bottom - rc.top,
1253                             SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1254                     break;
1255                 case IDC_TOOLBARSTATIC:
1256                 case IDC_TOOLBAR:
1257                     DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1258                             0, 0,
1259                             SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1260                     break;
1261 #endif
1262                 /* not resized in windows. Since wine uses this invisible control
1263                  * to size the browser view it needs to be resized */
1264                 case IDC_SHELLSTATIC:
1265                     DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1266                             rc.right - rc.left + chgx,
1267                             rc.bottom - rc.top + chgy,
1268                             SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1269                     break;
1270                 case IDC_TOOLBARPLACES:
1271                     DeferWindowPos( hdwp, ctrl, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top + chgy,
1272                                     SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1273                     break;
1274             }
1275         }
1276     }
1277     if(fodInfos->DlgInfos.hwndCustomDlg &&
1278         (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1279     {
1280         for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1281                 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1282         {
1283             GetWindowRect( ctrl, &rc);
1284             MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1285             if( rc.top > rcview.bottom)
1286             {
1287                 /* if it was below the shell view
1288                  * move to bottom */
1289                 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1290                         rc.right - rc.left, rc.bottom - rc.top,
1291                         SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1292             }
1293             else if( rc.left > rcview.right)
1294             {
1295                 /* if it was to the right of the shell view
1296                  * move to right */
1297                 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1298                         rc.right - rc.left, rc.bottom - rc.top,
1299                         SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1300             }
1301         }
1302         /* size the custom dialog at the end: some applications do some
1303          * control re-arranging at this point */
1304         GetClientRect(hwnd, &rc);
1305         DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1306             0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1307     }
1308     EndDeferWindowPos( hdwp);
1309     /* should not be needed */
1310     RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1311     return TRUE;
1312 }
1313 
1314 /***********************************************************************
1315  *          FileOpenDlgProc95
1316  *
1317  * File open dialog procedure
1318  */
1319 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1320 {
1321 #if 0
1322   TRACE("%p 0x%04x\n", hwnd, uMsg);
1323 #endif
1324 
1325   switch(uMsg)
1326   {
1327     case WM_INITDIALOG:
1328       {
1329          FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1330          RECT rc, rcstc;
1331          int gripx = GetSystemMetrics( SM_CYHSCROLL);
1332          int gripy = GetSystemMetrics( SM_CYVSCROLL);
1333 
1334          /* Some shell namespace extensions depend on COM being initialized. */
1335          if (SUCCEEDED(OleInitialize(NULL)))
1336              fodInfos->ole_initialized = TRUE;
1337 
1338          SetPropW(hwnd, filedlg_info_propnameW, fodInfos);
1339 
1340          FILEDLG95_InitControls(hwnd);
1341 
1342          if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1343          {
1344              DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1345              DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1346              RECT client, client_adjusted;
1347 
1348              if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1349              {
1350                  style |= WS_SIZEBOX;
1351                  ex_style |= WS_EX_WINDOWEDGE;
1352              }
1353              else
1354                  style &= ~WS_SIZEBOX;
1355              SetWindowLongW(hwnd, GWL_STYLE, style);
1356              SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1357 
1358              GetClientRect( hwnd, &client );
1359              GetClientRect( hwnd, &client_adjusted );
1360              AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1361 
1362              GetWindowRect( hwnd, &rc );
1363              rc.right += client_adjusted.right - client.right;
1364              rc.bottom += client_adjusted.bottom - client.bottom;
1365              SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1366                  SWP_NOZORDER | SWP_NOMOVE);
1367 
1368              GetWindowRect( hwnd, &rc );
1369              fodInfos->DlgInfos.hwndGrip =
1370                  CreateWindowExA( 0, "SCROLLBAR", NULL,
1371                      WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1372                      SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1373                      rc.right - gripx, rc.bottom - gripy,
1374                      gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1375          }
1376 
1377       	 fodInfos->DlgInfos.hwndCustomDlg =
1378      	   CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1379 
1380          FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1381       	 FILEDLG95_FillControls(hwnd, wParam, lParam);
1382 
1383          if( fodInfos->DlgInfos.hwndCustomDlg)
1384              ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1385 
1386          if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1387              SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1388              SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1389          }
1390 
1391          /* if the app has changed the position of the invisible listbox,
1392           * change that of the listview (browser) as well */
1393          GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1394          GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1395          if( !EqualRect( &rc, &rcstc))
1396          {
1397              MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1398              SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1399                      rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1400                      SWP_NOACTIVATE | SWP_NOZORDER);
1401          }
1402 
1403          if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1404          {
1405              GetWindowRect( hwnd, &rc);
1406              fodInfos->sizedlg.cx = rc.right - rc.left;
1407              fodInfos->sizedlg.cy = rc.bottom - rc.top;
1408              fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1409              fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1410              GetClientRect( hwnd, &rc);
1411              SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1412                      rc.right - gripx, rc.bottom - gripy,
1413                      0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1414              /* resize the dialog to the previous invocation */
1415              if( MemDialogSize.cx && MemDialogSize.cy)
1416                  SetWindowPos( hwnd, NULL,
1417                          0, 0, MemDialogSize.cx, MemDialogSize.cy,
1418                          SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1419          }
1420 
1421          if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1422              SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1423 
1424          return 0;
1425        }
1426     case WM_SIZE:
1427       return FILEDLG95_OnWMSize(hwnd, wParam);
1428     case WM_GETMINMAXINFO:
1429       return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1430     case WM_COMMAND:
1431       return FILEDLG95_OnWMCommand(hwnd, wParam);
1432     case WM_DRAWITEM:
1433       {
1434         switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1435         {
1436         case IDC_LOOKIN:
1437           FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1438           return TRUE;
1439         }
1440       }
1441       return FALSE;
1442 
1443     case WM_GETISHELLBROWSER:
1444       return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1445 
1446     case WM_DESTROY:
1447       {
1448           FileOpenDlgInfos * fodInfos = get_filedlg_infoptr(hwnd);
1449           HWND places_bar = GetDlgItem(hwnd, IDC_TOOLBARPLACES);
1450           HIMAGELIST himl;
1451 
1452           if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1453               MemDialogSize = fodInfos->sizedlg;
1454 
1455           if (places_bar)
1456           {
1457               himl = (HIMAGELIST)SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_GETIMAGELIST, 0, 0);
1458               SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETIMAGELIST, 0, 0);
1459               ImageList_Destroy(himl);
1460           }
1461           return FALSE;
1462       }
1463 
1464     case WM_NCDESTROY:
1465         RemovePropW(hwnd, filedlg_info_propnameW);
1466         return 0;
1467 
1468     case WM_NOTIFY:
1469     {
1470 	LPNMHDR lpnmh = (LPNMHDR)lParam;
1471 	UINT stringId = -1;
1472 
1473 	/* set up the button tooltips strings */
1474 	if(TTN_GETDISPINFOA == lpnmh->code )
1475 	{
1476 	    LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1477 	    switch(lpnmh->idFrom )
1478 	    {
1479 		/* Up folder button */
1480 		case FCIDM_TB_UPFOLDER:
1481 		    stringId = IDS_UPFOLDER;
1482 		    break;
1483 		/* New folder button */
1484 		case FCIDM_TB_NEWFOLDER:
1485 		    stringId = IDS_NEWFOLDER;
1486 		    break;
1487 		/* List option button */
1488 		case FCIDM_TB_SMALLICON:
1489 		    stringId = IDS_LISTVIEW;
1490 		    break;
1491 		/* Details option button */
1492 		case FCIDM_TB_REPORTVIEW:
1493 		    stringId = IDS_REPORTVIEW;
1494 		    break;
1495 		/* Desktop button */
1496 		case FCIDM_TB_DESKTOP:
1497 		    stringId = IDS_TODESKTOP;
1498 		    break;
1499 		default:
1500 		    stringId = 0;
1501 	    }
1502 	    lpdi->hinst = COMDLG32_hInstance;
1503 	    lpdi->lpszText =  MAKEINTRESOURCEA(stringId);
1504 	}
1505         return FALSE;
1506     }
1507     default :
1508       if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1509         return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1510       return FALSE;
1511   }
1512 }
1513 
1514 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1515 {
1516     return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1517         (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1518 }
1519 
1520 /***********************************************************************
1521  *      FILEDLG95_InitControls
1522  *
1523  * WM_INITDIALOG message handler (before hook notification)
1524  */
1525 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1526 {
1527   BOOL win2000plus = FALSE;
1528   BOOL win98plus   = FALSE;
1529   BOOL handledPath = FALSE;
1530   OSVERSIONINFOW osVi;
1531   static const WCHAR szwSlash[] = { '\\', 0 };
1532   static const WCHAR szwStar[] = { '*',0 };
1533 
1534   static const TBBUTTON tbb[] =
1535   {
1536    {0,                 0,                   TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1537    {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER,   TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1538    {0,                 0,                   TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1539    {VIEW_NEWFOLDER+1,  FCIDM_TB_DESKTOP,    TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1540    {0,                 0,                   TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1541    {VIEW_NEWFOLDER,    FCIDM_TB_NEWFOLDER,  TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1542    {0,                 0,                   TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1543    {VIEW_LIST,         FCIDM_TB_SMALLICON,  TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1544    {VIEW_DETAILS,      FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1545   };
1546   static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1547 
1548   RECT rectTB;
1549   RECT rectlook;
1550 
1551   HIMAGELIST toolbarImageList;
1552   ITEMIDLIST *desktopPidl;
1553   SHFILEINFOW fileinfo;
1554 
1555   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1556 
1557   TRACE("%p\n", fodInfos);
1558 
1559   /* Get windows version emulating */
1560   osVi.dwOSVersionInfoSize = sizeof(osVi);
1561   GetVersionExW(&osVi);
1562   if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1563     win98plus   = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1564   } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1565     win2000plus = (osVi.dwMajorVersion > 4);
1566     if (win2000plus) win98plus = TRUE;
1567   }
1568   TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1569 
1570 
1571   /* Use either the edit or the comboboxex for the filename control */
1572   if (filename_is_edit( fodInfos ))
1573   {
1574       DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1575       fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1576   }
1577   else
1578   {
1579       DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1580       fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1581   }
1582 
1583   /* Get the hwnd of the controls */
1584   fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1585   fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1586 
1587   GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1588   MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1589 
1590   /* construct the toolbar */
1591   GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1592   MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1593 
1594   rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1595   rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1596   rectTB.left = rectlook.right;
1597   rectTB.top = rectlook.top-1;
1598 
1599   if (fodInfos->unicode)
1600       fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1601           WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NORESIZE,
1602           rectTB.left, rectTB.top,
1603           rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1604           hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1605   else
1606       fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1607           WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NORESIZE,
1608           rectTB.left, rectTB.top,
1609           rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1610           hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1611 
1612   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1613 
1614 /* FIXME: use TB_LOADIMAGES when implemented */
1615 /*  SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1616   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1617   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1618 
1619   /* Retrieve and add desktop icon to the toolbar */
1620   toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1621   SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1622   SHGetFileInfoW((const WCHAR *)desktopPidl, 0, &fileinfo, sizeof(fileinfo),
1623     SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1624   ImageList_AddIcon(toolbarImageList, fileinfo.hIcon);
1625 
1626   DestroyIcon(fileinfo.hIcon);
1627   CoTaskMemFree(desktopPidl);
1628 
1629   /* Finish Toolbar Construction */
1630   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1631   SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1632 
1633   if (is_places_bar_enabled(fodInfos))
1634   {
1635       TBBUTTON tb = { 0 };
1636       HIMAGELIST himl;
1637       RECT rect;
1638       int i, cx;
1639 
1640       SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_BUTTONSTRUCTSIZE, 0, 0);
1641       GetClientRect(GetDlgItem(hwnd, IDC_TOOLBARPLACES), &rect);
1642       cx = rect.right - rect.left;
1643 
1644       SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETBUTTONWIDTH, 0, MAKELPARAM(cx, cx));
1645       himl = ImageList_Create(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), ILC_COLOR32, 4, 1);
1646 
1647       filedlg_collect_places_pidls(fodInfos);
1648       for (i = 0; i < ARRAY_SIZE(fodInfos->places); i++)
1649       {
1650           int index;
1651 
1652           if (!fodInfos->places[i])
1653               continue;
1654 
1655           memset(&fileinfo, 0, sizeof(fileinfo));
1656           SHGetFileInfoW((const WCHAR *)fodInfos->places[i], 0, &fileinfo, sizeof(fileinfo),
1657               SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_ICON);
1658           index = ImageList_AddIcon(himl, fileinfo.hIcon);
1659 
1660           tb.iBitmap = index;
1661           tb.iString = (INT_PTR)fileinfo.szDisplayName;
1662           tb.fsState = TBSTATE_ENABLED | TBSTATE_WRAP;
1663           tb.idCommand = TBPLACES_CMDID_PLACE0 + i;
1664           SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_ADDBUTTONSW, 1, (LPARAM)&tb);
1665 
1666           DestroyIcon(fileinfo.hIcon);
1667       }
1668 
1669       SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETIMAGELIST, 0, (LPARAM)himl);
1670       SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETBUTTONSIZE, 0, MAKELPARAM(cx, cx * 3 / 4));
1671   }
1672 
1673   /* Set the window text with the text specified in the OPENFILENAME structure */
1674   if(fodInfos->title)
1675   {
1676       SetWindowTextW(hwnd,fodInfos->title);
1677   }
1678   else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1679   {
1680       WCHAR buf[64];
1681       LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, ARRAY_SIZE(buf));
1682       SetWindowTextW(hwnd, buf);
1683   }
1684 
1685   /* Initialise the file name edit control */
1686   handledPath = FALSE;
1687   TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1688 
1689   if(fodInfos->filename)
1690   {
1691       /* 1. If win2000 or higher and filename contains a path, use it
1692          in preference over the lpstrInitialDir                       */
1693       if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1694          WCHAR tmpBuf[MAX_PATH];
1695          WCHAR *nameBit;
1696          DWORD result;
1697 
1698          result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1699          if (result) {
1700 
1701             /* nameBit is always shorter than the original filename. It may be NULL
1702              * when the filename contains only a drive name instead of file name */
1703             if (nameBit)
1704             {
1705                 lstrcpyW(fodInfos->filename,nameBit);
1706                 *nameBit = 0x00;
1707             }
1708             else
1709                 *fodInfos->filename = '\0';
1710 
1711             heap_free(fodInfos->initdir);
1712             fodInfos->initdir = heap_alloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1713             lstrcpyW(fodInfos->initdir, tmpBuf);
1714             handledPath = TRUE;
1715             TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1716                     debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1717          }
1718          SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1719 
1720       } else {
1721          SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1722       }
1723   }
1724 
1725   /* 2. (All platforms) If initdir is not null, then use it */
1726   if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1727   {
1728         /* Work out the proper path as supplied one might be relative          */
1729         /* (Here because supplying '.' as dir browses to My Computer)          */
1730         WCHAR tmpBuf[MAX_PATH];
1731         WCHAR tmpBuf2[MAX_PATH];
1732         WCHAR *nameBit;
1733         DWORD result;
1734 
1735         lstrcpyW(tmpBuf, fodInfos->initdir);
1736         if (PathFileExistsW(tmpBuf)) {
1737             /* initdir does not have to be a directory. If a file is
1738              * specified, the dir part is taken */
1739             if (PathIsDirectoryW(tmpBuf)) {
1740                 PathAddBackslashW(tmpBuf);
1741                 lstrcatW(tmpBuf, szwStar);
1742             }
1743             result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1744             if (result) {
1745                 *nameBit = 0x00;
1746                 heap_free(fodInfos->initdir);
1747                 fodInfos->initdir = heap_alloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1748                 lstrcpyW(fodInfos->initdir, tmpBuf2);
1749                 handledPath = TRUE;
1750                 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1751             }
1752         }
1753         else if (fodInfos->initdir)
1754         {
1755             heap_free(fodInfos->initdir);
1756             fodInfos->initdir = NULL;
1757             TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1758         }
1759   }
1760 
1761 #ifdef __REACTOS__
1762   if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1763   {
1764       /* 2.5. Win2000+: Recently used defext */
1765       if (win2000plus) {
1766           fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1767           fodInfos->initdir[0] = '\0';
1768 
1769           FILEDLG95_MRU_load_ext(fodInfos->initdir, MAX_PATH, fodInfos->defext);
1770 
1771           if (fodInfos->initdir[0] && PathIsDirectoryW(fodInfos->initdir)) {
1772              handledPath = TRUE;
1773           } else {
1774              heap_free(fodInfos->initdir);
1775              fodInfos->initdir = NULL;
1776           }
1777       }
1778   }
1779 #endif
1780 
1781   if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1782   {
1783       /* 3. All except w2k+: if filename contains a path use it */
1784       if (!win2000plus && fodInfos->filename &&
1785           *fodInfos->filename &&
1786           strpbrkW(fodInfos->filename, szwSlash)) {
1787          WCHAR tmpBuf[MAX_PATH];
1788          WCHAR *nameBit;
1789          DWORD result;
1790 
1791          result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1792                                   tmpBuf, &nameBit);
1793          if (result) {
1794             int len;
1795 
1796             /* nameBit is always shorter than the original filename */
1797             lstrcpyW(fodInfos->filename, nameBit);
1798             *nameBit = 0x00;
1799 
1800             len = lstrlenW(tmpBuf);
1801             heap_free(fodInfos->initdir);
1802             fodInfos->initdir = heap_alloc((len+1)*sizeof(WCHAR));
1803             lstrcpyW(fodInfos->initdir, tmpBuf);
1804 
1805             handledPath = TRUE;
1806             TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1807                  debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1808          }
1809          SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1810       }
1811 
1812       /* 4. Win2000+: Recently used */
1813       if (!handledPath && win2000plus) {
1814           fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1815           fodInfos->initdir[0] = '\0';
1816 
1817           FILEDLG95_MRU_load_filename(fodInfos->initdir);
1818 
1819           if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1820              handledPath = TRUE;
1821           }else{
1822              heap_free(fodInfos->initdir);
1823              fodInfos->initdir = NULL;
1824           }
1825       }
1826 
1827       /* 5. win98+ and win2000+ if any files of specified filter types in
1828             current directory, use it                                      */
1829       if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1830 
1831          LPCWSTR lpstrPos = fodInfos->filter;
1832          WIN32_FIND_DATAW FindFileData;
1833          HANDLE hFind;
1834 
1835          while (1)
1836          {
1837            /* filter is a list...  title\0ext\0......\0\0 */
1838 
1839            /* Skip the title */
1840            if(! *lpstrPos) break;	/* end */
1841            lpstrPos += lstrlenW(lpstrPos) + 1;
1842 
1843            /* See if any files exist in the current dir with this extension */
1844            if(! *lpstrPos) break;	/* end */
1845 
1846            hFind = FindFirstFileW(lpstrPos, &FindFileData);
1847 
1848            if (hFind == INVALID_HANDLE_VALUE) {
1849                /* None found - continue search */
1850                lpstrPos += lstrlenW(lpstrPos) + 1;
1851 
1852            } else {
1853 
1854                heap_free(fodInfos->initdir);
1855                fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1856                GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1857 
1858                handledPath = TRUE;
1859                TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1860                  debugstr_w(lpstrPos));
1861                FindClose(hFind);
1862                break;
1863            }
1864          }
1865       }
1866 
1867       /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1868       if (!handledPath && (win2000plus || win98plus)) {
1869           fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1870 
1871           if (SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir) == S_OK)
1872           {
1873               if (SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir) == S_OK)
1874               {
1875                   /* last fallback */
1876                   GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1877                   TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1878               }
1879               else
1880                 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1881           }
1882           else
1883               TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1884 
1885           handledPath = TRUE;
1886       } else if (!handledPath) {
1887           fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1888           GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1889           handledPath = TRUE;
1890           TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1891       }
1892   }
1893   SetFocus( fodInfos->DlgInfos.hwndFileName );
1894   TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1895 
1896   /* Must the open as read only check box be checked ?*/
1897   if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1898   {
1899     SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1900   }
1901 
1902   /* Must the open as read only check box be hidden? */
1903   if (filedialog_is_readonly_hidden(fodInfos))
1904   {
1905     ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1906     EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1907   }
1908 
1909   /* Must the help button be hidden? */
1910   if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1911   {
1912     ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1913     EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1914   }
1915 
1916   /* change Open to Save */
1917   if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1918   {
1919       WCHAR buf[16];
1920       LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, ARRAY_SIZE(buf));
1921       SetDlgItemTextW(hwnd, IDOK, buf);
1922       LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, ARRAY_SIZE(buf));
1923       SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1924   }
1925 
1926   /* Initialize the filter combo box */
1927   FILEDLG95_FILETYPE_Init(hwnd);
1928 
1929   return 0;
1930 }
1931 
1932 /***********************************************************************
1933  *      FILEDLG95_ResizeControls
1934  *
1935  * WM_INITDIALOG message handler (after hook notification)
1936  */
1937 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1938 {
1939   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1940 
1941   if (fodInfos->DlgInfos.hwndCustomDlg)
1942   {
1943     RECT rc;
1944     UINT flags = SWP_NOACTIVATE;
1945 
1946     ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1947         filedialog_is_readonly_hidden(fodInfos) && !(fodInfos->ofnInfos->Flags & OFN_SHOWHELP));
1948 
1949     /* resize the custom dialog to the parent size */
1950     if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1951       GetClientRect(hwnd, &rc);
1952     else
1953     {
1954       /* our own fake template is zero sized and doesn't have children, so
1955        * there is no need to resize it. Picasa depends on it.
1956        */
1957       flags |= SWP_NOSIZE;
1958       SetRectEmpty(&rc);
1959     }
1960     SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1961                  0, 0, rc.right, rc.bottom, flags);
1962   }
1963   else
1964   {
1965     /* Resize the height; if opened as read-only, checkbox and help button are
1966      * hidden and we are not using a custom template nor a customDialog
1967      */
1968     if (filedialog_is_readonly_hidden(fodInfos) &&
1969                 (!(fodInfos->ofnInfos->Flags &
1970                    (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1971     {
1972       RECT rectDlg, rectHelp, rectCancel;
1973       GetWindowRect(hwnd, &rectDlg);
1974       GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1975       GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1976       /* subtract the height of the help button plus the space between the help
1977        * button and the cancel button to the height of the dialog
1978        */
1979       SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1980           (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1981           SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1982     }
1983   }
1984   return TRUE;
1985 }
1986 
1987 /***********************************************************************
1988  *      FILEDLG95_FillControls
1989  *
1990  * WM_INITDIALOG message handler (after hook notification)
1991  */
1992 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1993 {
1994   LPITEMIDLIST pidlItemId = NULL;
1995 
1996   FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1997 
1998   TRACE("dir=%s file=%s\n",
1999   debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
2000 
2001   /* Get the initial directory pidl */
2002 
2003   if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
2004   {
2005     WCHAR path[MAX_PATH];
2006 
2007     GetCurrentDirectoryW(MAX_PATH,path);
2008     pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
2009   }
2010 
2011   /* Initialise shell objects */
2012   FILEDLG95_SHELL_Init(hwnd);
2013 
2014   /* Initialize the Look In combo box */
2015   FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
2016 
2017   /* Browse to the initial directory */
2018   IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
2019 
2020   ILFree(pidlItemId);
2021 
2022   return TRUE;
2023 }
2024 /***********************************************************************
2025  *      FILEDLG95_Clean
2026  *
2027  * Regroups all the cleaning functions of the filedlg
2028  */
2029 void FILEDLG95_Clean(HWND hwnd)
2030 {
2031       FILEDLG95_FILETYPE_Clean(hwnd);
2032       FILEDLG95_LOOKIN_Clean(hwnd);
2033       FILEDLG95_SHELL_Clean(hwnd);
2034 }
2035 
2036 
2037 /***********************************************************************
2038  * Browse to arbitrary pidl
2039  */
2040 static void filedlg_browse_to_pidl(const FileOpenDlgInfos *info, LPITEMIDLIST pidl)
2041 {
2042     TRACE("%p, %p\n", info->ShellInfos.hwndOwner, pidl);
2043 
2044     IShellBrowser_BrowseObject(info->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2045     if (info->ofnInfos->Flags & OFN_EXPLORER)
2046         SendCustomDlgNotificationMessage(info->ShellInfos.hwndOwner, CDN_FOLDERCHANGE);
2047 }
2048 
2049 /***********************************************************************
2050  *      FILEDLG95_OnWMCommand
2051  *
2052  * WM_COMMAND message handler
2053  */
2054 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
2055 {
2056   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2057   WORD wNotifyCode = HIWORD(wParam); /* notification code */
2058   WORD id = LOWORD(wParam);         /* item, control, or accelerator identifier */
2059 
2060   switch (id)
2061   {
2062     /* OK button */
2063   case IDOK:
2064     FILEDLG95_OnOpen(hwnd);
2065     break;
2066     /* Cancel button */
2067   case IDCANCEL:
2068     FILEDLG95_Clean(hwnd);
2069     EndDialog(hwnd, FALSE);
2070     break;
2071     /* Filetype combo box */
2072   case IDC_FILETYPE:
2073     FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
2074     break;
2075     /* LookIn combo box */
2076   case IDC_LOOKIN:
2077     FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
2078     break;
2079 
2080   /* --- toolbar --- */
2081     /* Up folder button */
2082   case FCIDM_TB_UPFOLDER:
2083     FILEDLG95_SHELL_UpFolder(hwnd);
2084     break;
2085     /* New folder button */
2086   case FCIDM_TB_NEWFOLDER:
2087     FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
2088     break;
2089     /* List option button */
2090   case FCIDM_TB_SMALLICON:
2091     FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
2092     break;
2093     /* Details option button */
2094   case FCIDM_TB_REPORTVIEW:
2095     FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
2096     break;
2097 
2098   case FCIDM_TB_DESKTOP:
2099   {
2100     LPITEMIDLIST pidl;
2101 
2102     SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, &pidl);
2103     filedlg_browse_to_pidl(fodInfos, pidl);
2104     ILFree(pidl);
2105     break;
2106   }
2107 
2108   /* Places bar */
2109   case TBPLACES_CMDID_PLACE0:
2110   case TBPLACES_CMDID_PLACE1:
2111   case TBPLACES_CMDID_PLACE2:
2112   case TBPLACES_CMDID_PLACE3:
2113   case TBPLACES_CMDID_PLACE4:
2114     filedlg_browse_to_pidl(fodInfos, fodInfos->places[id - TBPLACES_CMDID_PLACE0]);
2115     break;
2116 
2117   case edt1:
2118   case cmb13:
2119     break;
2120 
2121   }
2122   /* Do not use the listview selection anymore */
2123   fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
2124   return 0;
2125 }
2126 
2127 /***********************************************************************
2128  *      FILEDLG95_OnWMGetIShellBrowser
2129  *
2130  * WM_GETISHELLBROWSER message handler
2131  */
2132 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
2133 {
2134   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2135 
2136   TRACE("\n");
2137 
2138   SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
2139 
2140   return TRUE;
2141 }
2142 
2143 
2144 /***********************************************************************
2145  *      FILEDLG95_SendFileOK
2146  *
2147  * Sends the CDN_FILEOK notification if required
2148  *
2149  * RETURNS
2150  *  TRUE if the dialog should close
2151  *  FALSE if the dialog should not be closed
2152  */
2153 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
2154 {
2155     /* ask the hook if we can close */
2156     if (is_dialog_hooked(fodInfos))
2157     {
2158         LRESULT retval = 0;
2159 
2160         TRACE("---\n");
2161         /* First send CDN_FILEOK as MSDN doc says */
2162         if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2163             retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
2164         if( retval)
2165         {
2166             TRACE("canceled\n");
2167             return FALSE;
2168         }
2169 
2170         /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
2171         retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
2172                               fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
2173         if( retval)
2174         {
2175             TRACE("canceled\n");
2176             return FALSE;
2177         }
2178     }
2179     return TRUE;
2180 }
2181 
2182 /***********************************************************************
2183  *      FILEDLG95_OnOpenMultipleFiles
2184  *
2185  * Handles the opening of multiple files.
2186  *
2187  * FIXME
2188  *  check destination buffer size
2189  */
2190 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
2191 {
2192   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2193   WCHAR   lpstrPathSpec[MAX_PATH] = {0};
2194   UINT   nCount, nSizePath;
2195 
2196   TRACE("\n");
2197 
2198   if(fodInfos->unicode)
2199   {
2200      LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2201      ofn->lpstrFile[0] = '\0';
2202   }
2203   else
2204   {
2205      LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
2206      ofn->lpstrFile[0] = '\0';
2207   }
2208 
2209   COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
2210 
2211   if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2212       ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
2213        ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
2214   {
2215     LPWSTR lpstrTemp = lpstrFileList;
2216 
2217     for ( nCount = 0; nCount < nFileCount; nCount++ )
2218     {
2219       LPITEMIDLIST pidl;
2220 
2221       pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
2222       if (!pidl)
2223       {
2224         WCHAR lpstrNotFound[100];
2225         WCHAR lpstrMsg[100];
2226         WCHAR tmp[400];
2227         static const WCHAR nl[] = {'\n',0};
2228 
2229         LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
2230         LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
2231 
2232         lstrcpyW(tmp, lpstrTemp);
2233         lstrcatW(tmp, nl);
2234         lstrcatW(tmp, lpstrNotFound);
2235         lstrcatW(tmp, nl);
2236         lstrcatW(tmp, lpstrMsg);
2237 
2238         MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
2239         return FALSE;
2240       }
2241 
2242       /* move to the next file in the list of files */
2243       lpstrTemp += lstrlenW(lpstrTemp) + 1;
2244       ILFree(pidl);
2245     }
2246   }
2247 
2248   nSizePath = lstrlenW(lpstrPathSpec) + 1;
2249   if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2250   {
2251     /* For "oldstyle" dialog the components have to
2252        be separated by blanks (not '\0'!) and short
2253        filenames have to be used! */
2254     FIXME("Components have to be separated by blanks\n");
2255   }
2256   if(fodInfos->unicode)
2257   {
2258     LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2259     lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2260     memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2261   }
2262   else
2263   {
2264     LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2265 
2266     if (ofn->lpstrFile != NULL)
2267     {
2268       nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2269 			  ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2270       if (ofn->nMaxFile > nSizePath)
2271       {
2272 	WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2273 			    ofn->lpstrFile + nSizePath,
2274 			    ofn->nMaxFile - nSizePath, NULL, NULL);
2275       }
2276     }
2277   }
2278 
2279   fodInfos->ofnInfos->nFileOffset = nSizePath;
2280   fodInfos->ofnInfos->nFileExtension = 0;
2281 
2282   if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2283     return FALSE;
2284 
2285   /* clean and exit */
2286   FILEDLG95_Clean(hwnd);
2287   return EndDialog(hwnd,TRUE);
2288 }
2289 
2290 /* Returns the 'slot name' of the given module_name in the registry's
2291  * most-recently-used list.  This will be an ASCII value in the
2292  * range ['a','z'). Returns zero on error.
2293  *
2294  * The slot's value in the registry has the form:
2295  *   module_name\0mru_path\0
2296  *
2297  * If stored_path is given, then stored_path will contain the path name
2298  * stored in the registry's MRU list for the given module_name.
2299  *
2300  * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2301  * MRU list key for the given module_name.
2302  */
2303 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2304 {
2305     WCHAR mru_list[32], *cur_mru_slot;
2306     BOOL taken[25] = {0};
2307     DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2308     HKEY hkey_tmp, *hkey;
2309     LONG ret;
2310 
2311     if(hkey_ret)
2312         hkey = hkey_ret;
2313     else
2314         hkey = &hkey_tmp;
2315 
2316     if(stored_path)
2317         *stored_path = '\0';
2318 
2319     ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2320     if(ret){
2321         WARN("Unable to create MRU key: %d\n", ret);
2322         return 0;
2323     }
2324 
2325     ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2326             (LPBYTE)mru_list, &mru_list_size);
2327     if(ret || key_type != REG_SZ){
2328         if(ret == ERROR_FILE_NOT_FOUND)
2329             return 'a';
2330 
2331         WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2332         RegCloseKey(*hkey);
2333         return 0;
2334     }
2335 
2336     for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2337         WCHAR value_data[MAX_PATH], value_name[2] = {0};
2338         DWORD value_data_size = sizeof(value_data);
2339 
2340         *value_name = *cur_mru_slot;
2341 
2342         ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2343                 &key_type, (LPBYTE)value_data, &value_data_size);
2344         if(ret || key_type != REG_BINARY){
2345             WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2346             continue;
2347         }
2348 
2349         if(!strcmpiW(module_name, value_data)){
2350             if(!hkey_ret)
2351                 RegCloseKey(*hkey);
2352             if(stored_path)
2353                 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2354             return *value_name;
2355         }
2356     }
2357 
2358     if(!hkey_ret)
2359         RegCloseKey(*hkey);
2360 
2361     /* the module name isn't in the registry, so find the next open slot */
2362     for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2363         taken[*cur_mru_slot - 'a'] = TRUE;
2364     for(i = 0; i < 25; ++i){
2365         if(!taken[i])
2366             return i + 'a';
2367     }
2368 
2369     /* all slots are taken, so return the last one in MRUList */
2370     --cur_mru_slot;
2371     return *cur_mru_slot;
2372 }
2373 
2374 /* save the given filename as most-recently-used path for this module */
2375 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2376 {
2377     WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2378     LONG ret;
2379     HKEY hkey;
2380 
2381     /* get the current executable's name */
2382     if (!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, ARRAY_SIZE(module_path)))
2383     {
2384         WARN("GotModuleFileName failed: %d\n", GetLastError());
2385         return;
2386     }
2387     module_name = strrchrW(module_path, '\\');
2388     if(!module_name)
2389         module_name = module_path;
2390     else
2391         module_name += 1;
2392 
2393     slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2394     if(!slot)
2395         return;
2396     *slot_name = slot;
2397 
2398     { /* update the slot's info */
2399         WCHAR *path_ends, *final;
2400         DWORD path_len, final_len;
2401 
2402         /* use only the path segment of `filename' */
2403         path_ends = strrchrW(filename, '\\');
2404         path_len = path_ends - filename;
2405 
2406         final_len = path_len + lstrlenW(module_name) + 2;
2407 
2408         final = heap_alloc(final_len * sizeof(WCHAR));
2409         if(!final)
2410             return;
2411         lstrcpyW(final, module_name);
2412         memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2413         final[final_len-1] = '\0';
2414 
2415         ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2416                 final_len * sizeof(WCHAR));
2417         if(ret){
2418             WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2419             heap_free(final);
2420             RegCloseKey(hkey);
2421             return;
2422         }
2423 
2424         heap_free(final);
2425     }
2426 
2427     { /* update MRUList value */
2428         WCHAR old_mru_list[32], new_mru_list[32];
2429         WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2430         DWORD mru_list_size = sizeof(old_mru_list), key_type;
2431 
2432         ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2433                 (LPBYTE)old_mru_list, &mru_list_size);
2434         if(ret || key_type != REG_SZ){
2435             if(ret == ERROR_FILE_NOT_FOUND){
2436                 new_mru_list[0] = slot;
2437                 new_mru_list[1] = '\0';
2438             }else{
2439                 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2440                 RegCloseKey(hkey);
2441                 return;
2442             }
2443         }else{
2444             /* copy old list data over so that the new slot is at the start
2445              * of the list */
2446             *new_mru_slot++ = slot;
2447             for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2448                 if(*old_mru_slot != slot)
2449                     *new_mru_slot++ = *old_mru_slot;
2450             }
2451             *new_mru_slot = '\0';
2452         }
2453 
2454         ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2455                 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2456         if(ret){
2457             WARN("Error saving MRUList data: %d\n", ret);
2458             RegCloseKey(hkey);
2459             return;
2460         }
2461     }
2462 }
2463 
2464 /* load the most-recently-used path for this module */
2465 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2466 {
2467     WCHAR module_path[MAX_PATH], *module_name;
2468 
2469     /* get the current executable's name */
2470     if (!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, ARRAY_SIZE(module_path)))
2471     {
2472         WARN("GotModuleFileName failed: %d\n", GetLastError());
2473         return;
2474     }
2475     module_name = strrchrW(module_path, '\\');
2476     if(!module_name)
2477         module_name = module_path;
2478     else
2479         module_name += 1;
2480 
2481     FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2482     TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2483 }
2484 #ifdef __REACTOS__
2485 static const WCHAR s_subkey[] =
2486 {
2487     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
2488     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s',
2489     'i','o','n','\\','E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g',
2490     '3','2','\\','O','p','e','n','S','a','v','e','M','R','U',0
2491 };
2492 static const WCHAR s_szAst[] = { '*', 0 };
2493 
2494 typedef INT (CALLBACK *MRUStringCmpFnW)(LPCWSTR lhs, LPCWSTR rhs);
2495 typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length);
2496 
2497 /* https://docs.microsoft.com/en-us/windows/desktop/shell/mruinfo */
2498 typedef struct tagMRUINFOW
2499 {
2500     DWORD   cbSize;
2501     UINT    uMax;
2502     UINT    fFlags;
2503     HKEY    hKey;
2504     LPCWSTR lpszSubKey;
2505     union
2506     {
2507         MRUStringCmpFnW string_cmpfn;
2508         MRUBinaryCmpFn  binary_cmpfn;
2509     } u;
2510 } MRUINFOW, *LPMRUINFOW;
2511 
2512 /* flags for MRUINFOW.fFlags */
2513 #define MRU_STRING 0x0000
2514 #define MRU_BINARY 0x0001
2515 #define MRU_CACHEWRITE 0x0002
2516 
2517 static HINSTANCE s_hComCtl32 = NULL;
2518 
2519 /* comctl32.400: CreateMRUListW */
2520 typedef HANDLE (WINAPI *CREATEMRULISTW)(const MRUINFOW *);
2521 static CREATEMRULISTW s_pCreateMRUListW = NULL;
2522 
2523 /* comctl32.401: AddMRUStringW */
2524 typedef INT (WINAPI *ADDMRUSTRINGW)(HANDLE, LPCWSTR);
2525 static ADDMRUSTRINGW s_pAddMRUStringW = NULL;
2526 
2527 /* comctl32.402: FindMRUStringW */
2528 typedef INT (WINAPI *FINDMRUSTRINGW)(HANDLE, LPCWSTR, LPINT);
2529 static FINDMRUSTRINGW s_pFindMRUStringW = NULL;
2530 
2531 /* comctl32.403: EnumMRUListW */
2532 typedef INT (WINAPI *ENUMMRULISTW)(HANDLE, INT, LPVOID, DWORD);
2533 static ENUMMRULISTW s_pEnumMRUListW = NULL;
2534 
2535 /* comctl32.152: FreeMRUList */
2536 typedef void (WINAPI *FREEMRULIST)(HANDLE);
2537 static FREEMRULIST s_pFreeMRUList = NULL;
2538 
2539 static BOOL FILEDLG_InitMRUList(void)
2540 {
2541     if (s_hComCtl32)
2542         return TRUE;
2543 
2544     s_hComCtl32 = GetModuleHandleA("comctl32");
2545     if (!s_hComCtl32)
2546         return FALSE;
2547 
2548     s_pCreateMRUListW = (CREATEMRULISTW)GetProcAddress(s_hComCtl32, (LPCSTR)400);
2549     s_pAddMRUStringW = (ADDMRUSTRINGW)GetProcAddress(s_hComCtl32, (LPCSTR)401);
2550     s_pFindMRUStringW = (FINDMRUSTRINGW)GetProcAddress(s_hComCtl32, (LPCSTR)402);
2551     s_pEnumMRUListW = (ENUMMRULISTW)GetProcAddress(s_hComCtl32, (LPCSTR)403);
2552     s_pFreeMRUList = (FREEMRULIST)GetProcAddress(s_hComCtl32, (LPCSTR)152);
2553     if (!s_pCreateMRUListW ||
2554         !s_pAddMRUStringW ||
2555         !s_pFindMRUStringW ||
2556         !s_pEnumMRUListW ||
2557         !s_pFreeMRUList)
2558     {
2559         s_hComCtl32 = NULL;
2560         return FALSE;
2561     }
2562 
2563     return TRUE;
2564 }
2565 
2566 static BOOL ExtIsPicture(LPCWSTR ext)
2567 {
2568     static const WCHAR s_image_exts[][6] =
2569     {
2570         { 'b','m','p',0 },
2571         { 'd','i','b',0 },
2572         { 'j','p','g',0 },
2573         { 'j','p','e','g',0 },
2574         { 'j','p','e',0 },
2575         { 'j','f','i','f',0 },
2576         { 'p','n','g',0 },
2577         { 'g','i','f',0 },
2578         { 't','i','f',0 },
2579         { 't','i','f','f',0 }
2580     };
2581     size_t i;
2582 
2583     for (i = 0; i < ARRAY_SIZE(s_image_exts); ++i)
2584     {
2585         if (lstrcmpiW(ext, s_image_exts[i]) == 0)
2586         {
2587             return TRUE;
2588         }
2589     }
2590     return FALSE;
2591 }
2592 
2593 static void FILEDLG95_MRU_load_ext(LPWSTR stored_path, size_t cchMax, LPCWSTR defext)
2594 {
2595     HKEY hOpenSaveMRT = NULL;
2596     LONG result;
2597     MRUINFOW mi;
2598     HANDLE hList;
2599     WCHAR szText[MAX_PATH];
2600     INT ret = 0;
2601 
2602     stored_path[0] = 0;
2603 
2604     if (!defext || !*defext || !FILEDLG_InitMRUList())
2605     {
2606         return;
2607     }
2608 
2609     if (*defext == '.')
2610         ++defext;
2611 
2612     result = RegOpenKeyW(HKEY_CURRENT_USER, s_subkey, &hOpenSaveMRT);
2613     if (!result && hOpenSaveMRT)
2614     {
2615         ZeroMemory(&mi, sizeof(mi));
2616         mi.cbSize = sizeof(mi);
2617         mi.uMax = 26;
2618         mi.fFlags = MRU_STRING;
2619         mi.hKey = hOpenSaveMRT;
2620         mi.lpszSubKey = defext;
2621         mi.u.string_cmpfn = lstrcmpiW;
2622         hList = (*s_pCreateMRUListW)(&mi);
2623         if (hList)
2624         {
2625             ret = (*s_pEnumMRUListW)(hList, 0, szText, sizeof(szText));
2626             if (ret > 0)
2627             {
2628                 lstrcpynW(stored_path, szText, cchMax);
2629                 PathRemoveFileSpecW(stored_path);
2630             }
2631             (*s_pFreeMRUList)(hList);
2632         }
2633 
2634         RegCloseKey(hOpenSaveMRT);
2635     }
2636 
2637     if (stored_path[0] == 0)
2638     {
2639         LPITEMIDLIST pidl;
2640         if (ExtIsPicture(defext))
2641         {
2642             SHGetSpecialFolderLocation(NULL, CSIDL_MYPICTURES, &pidl);
2643         }
2644         else
2645         {
2646             SHGetSpecialFolderLocation(NULL, CSIDL_MYDOCUMENTS, &pidl);
2647         }
2648         SHGetPathFromIDListW(pidl, stored_path);
2649         ILFree(pidl);
2650     }
2651 }
2652 
2653 static void FILEDLG95_MRU_save_ext(LPCWSTR filename)
2654 {
2655     HKEY hOpenSaveMRT = NULL;
2656     LONG result;
2657     MRUINFOW mi;
2658     HANDLE hList;
2659     LPCWSTR defext = PathFindExtensionW(filename);
2660 
2661     if (!defext || !*defext || !FILEDLG_InitMRUList())
2662     {
2663         return;
2664     }
2665 
2666     if (*defext == '.')
2667         ++defext;
2668 
2669     result = RegOpenKeyW(HKEY_CURRENT_USER, s_subkey, &hOpenSaveMRT);
2670     if (!result && hOpenSaveMRT)
2671     {
2672         ZeroMemory(&mi, sizeof(mi));
2673         mi.cbSize = sizeof(mi);
2674         mi.uMax = 26;
2675         mi.fFlags = MRU_STRING;
2676         mi.hKey = hOpenSaveMRT;
2677         mi.lpszSubKey = defext;
2678         mi.u.string_cmpfn = lstrcmpiW;
2679         hList = (*s_pCreateMRUListW)(&mi);
2680         if (hList)
2681         {
2682             (*s_pAddMRUStringW)(hList, filename);
2683             (*s_pFreeMRUList)(hList);
2684         }
2685 
2686         mi.cbSize = sizeof(mi);
2687         mi.uMax = 26;
2688         mi.fFlags = MRU_STRING;
2689         mi.hKey = hOpenSaveMRT;
2690         mi.lpszSubKey = s_szAst;
2691         mi.u.string_cmpfn = lstrcmpiW;
2692         hList = (*s_pCreateMRUListW)(&mi);
2693         if (hList)
2694         {
2695             (*s_pAddMRUStringW)(hList, filename);
2696             (*s_pFreeMRUList)(hList);
2697         }
2698 
2699         RegCloseKey(hOpenSaveMRT);
2700     }
2701 }
2702 #endif
2703 
2704 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2705 {
2706   WCHAR strMsgTitle[MAX_PATH];
2707   WCHAR strMsgText [MAX_PATH];
2708   if (idCaption)
2709     LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, ARRAY_SIZE(strMsgTitle));
2710   else
2711     strMsgTitle[0] = '\0';
2712   LoadStringW(COMDLG32_hInstance, idText, strMsgText, ARRAY_SIZE(strMsgText));
2713   MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2714 }
2715 
2716 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2717                                  HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2718 {
2719     int nOpenAction = defAction;
2720     LPWSTR lpszTemp, lpszTemp1;
2721     LPITEMIDLIST pidl = NULL;
2722     static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2723 
2724     /* check for invalid chars */
2725     if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2726     {
2727         FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2728         return FALSE;
2729     }
2730 
2731     if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2732 
2733     lpszTemp1 = lpszTemp = lpstrPathAndFile;
2734     while (lpszTemp1)
2735     {
2736         LPSHELLFOLDER lpsfChild;
2737         WCHAR lpwstrTemp[MAX_PATH];
2738         DWORD dwEaten, dwAttributes;
2739         LPWSTR p;
2740 
2741         lstrcpyW(lpwstrTemp, lpszTemp);
2742         p = PathFindNextComponentW(lpwstrTemp);
2743 
2744         if (!p) break; /* end of path */
2745 
2746         *p = 0;
2747         lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2748 
2749         /* There are no wildcards when OFN_NOVALIDATE is set */
2750         if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2751         {
2752             static const WCHAR wszWild[] = { '*', '?', 0 };
2753             /* if the last element is a wildcard do a search */
2754             if(strpbrkW(lpszTemp1, wszWild) != NULL)
2755             {
2756                 nOpenAction = ONOPEN_SEARCH;
2757                 break;
2758             }
2759         }
2760         lpszTemp1 = lpszTemp;
2761 
2762         TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2763 
2764         /* append a backslash to drive letters */
2765         if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2766            ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2767             (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2768         {
2769             PathAddBackslashW(lpwstrTemp);
2770         }
2771 
2772         dwAttributes = SFGAO_FOLDER;
2773         if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2774         {
2775             /* the path component is valid, we have a pidl of the next path component */
2776             TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2777             if(dwAttributes & SFGAO_FOLDER)
2778             {
2779                 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2780                 {
2781                     ERR("bind to failed\n"); /* should not fail */
2782                     break;
2783                 }
2784                 IShellFolder_Release(*ppsf);
2785                 *ppsf = lpsfChild;
2786                 lpsfChild = NULL;
2787             }
2788             else
2789             {
2790                 TRACE("value\n");
2791 
2792                 /* end dialog, return value */
2793                 nOpenAction = ONOPEN_OPEN;
2794                 break;
2795             }
2796             ILFree(pidl);
2797             pidl = NULL;
2798         }
2799         else if (!(flags & OFN_NOVALIDATE))
2800         {
2801             if(*lpszTemp ||	/* points to trailing null for last path element */
2802                (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2803             {
2804                 if(flags & OFN_PATHMUSTEXIST)
2805                 {
2806                     FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2807                     break;
2808                 }
2809             }
2810             else
2811             {
2812                 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2813                 {
2814                     FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2815                     break;
2816                 }
2817             }
2818             /* change to the current folder */
2819             nOpenAction = ONOPEN_OPEN;
2820             break;
2821         }
2822         else
2823         {
2824             nOpenAction = ONOPEN_OPEN;
2825             break;
2826         }
2827     }
2828     ILFree(pidl);
2829 
2830     return nOpenAction;
2831 }
2832 
2833 /***********************************************************************
2834  *      FILEDLG95_OnOpen
2835  *
2836  * Ok button WM_COMMAND message handler
2837  *
2838  * If the function succeeds, the return value is nonzero.
2839  */
2840 BOOL FILEDLG95_OnOpen(HWND hwnd)
2841 {
2842   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2843   LPWSTR lpstrFileList;
2844   UINT nFileCount = 0;
2845   UINT sizeUsed = 0;
2846   BOOL ret = TRUE;
2847   WCHAR lpstrPathAndFile[MAX_PATH];
2848   LPSHELLFOLDER lpsf = NULL;
2849   int nOpenAction;
2850 
2851   TRACE("hwnd=%p\n", hwnd);
2852 
2853   /* try to browse the selected item */
2854   if(BrowseSelectedFolder(hwnd))
2855       return FALSE;
2856 
2857   /* get the files from the edit control */
2858   nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2859 
2860   if(nFileCount == 0)
2861       return FALSE;
2862 
2863   if(nFileCount > 1)
2864   {
2865       ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2866       goto ret;
2867   }
2868 
2869   TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2870 
2871 /*
2872   Step 1:  Build a complete path name from the current folder and
2873   the filename or path in the edit box.
2874   Special cases:
2875   - the path in the edit box is a root path
2876     (with or without drive letter)
2877   - the edit box contains ".." (or a path with ".." in it)
2878 */
2879 
2880   COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2881   heap_free(lpstrFileList);
2882 
2883 /*
2884   Step 2: here we have a cleaned up path
2885 
2886   We have to parse the path step by step to see if we have to browse
2887   to a folder if the path points to a directory or the last
2888   valid element is a directory.
2889 
2890   valid variables:
2891     lpstrPathAndFile: cleaned up path
2892  */
2893 
2894   if (nFileCount &&
2895       (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2896       !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2897     nOpenAction = ONOPEN_OPEN;
2898   else
2899     nOpenAction = ONOPEN_BROWSE;
2900 
2901   nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2902                                              fodInfos->ofnInfos->Flags,
2903                                              fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2904                                              nOpenAction);
2905   if(!nOpenAction)
2906       goto ret;
2907 
2908 /*
2909   Step 3: here we have a cleaned up and validated path
2910 
2911   valid variables:
2912    lpsf:             ShellFolder bound to the rightmost valid path component
2913    lpstrPathAndFile: cleaned up path
2914    nOpenAction:      action to do
2915 */
2916   TRACE("end validate sf=%p\n", lpsf);
2917 
2918   switch(nOpenAction)
2919   {
2920     case ONOPEN_SEARCH:   /* set the current filter to the file mask and refresh */
2921       TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2922       {
2923         int iPos;
2924         LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2925         DWORD len;
2926 
2927         /* replace the current filter */
2928         heap_free(fodInfos->ShellInfos.lpstrCurrentFilter);
2929         len = lstrlenW(lpszTemp)+1;
2930         fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc(len * sizeof(WCHAR));
2931         lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2932 
2933         /* set the filter cb to the extension when possible */
2934         if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2935         SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETCURSEL, iPos, 0);
2936       }
2937       /* fall through */
2938     case ONOPEN_BROWSE:   /* browse to the highest folder we could bind to */
2939       TRACE("ONOPEN_BROWSE\n");
2940       {
2941 	IPersistFolder2 * ppf2;
2942         if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2943         {
2944           LPITEMIDLIST pidlCurrent;
2945           IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2946           IPersistFolder2_Release(ppf2);
2947           if (!ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2948 	  {
2949             if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2950                 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2951             {
2952               SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2953               SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2954             }
2955 	  }
2956 	  else if( nOpenAction == ONOPEN_SEARCH )
2957 	  {
2958             if (fodInfos->Shell.FOIShellView)
2959               IShellView_Refresh(fodInfos->Shell.FOIShellView);
2960 	  }
2961           ILFree(pidlCurrent);
2962           if (filename_is_edit( fodInfos ))
2963               SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2964           else
2965           {
2966               HWND hwnd;
2967 
2968               hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2969               SendMessageW(hwnd, EM_SETSEL, 0, -1);
2970           }
2971         }
2972       }
2973       ret = FALSE;
2974       break;
2975     case ONOPEN_OPEN:   /* fill in the return struct and close the dialog */
2976       TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2977       {
2978         WCHAR *ext = NULL;
2979 
2980         /* update READONLY check box flag */
2981 	if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2982 	  fodInfos->ofnInfos->Flags |= OFN_READONLY;
2983 	else
2984 	  fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2985 
2986         /* Attach the file extension with file name*/
2987         ext = PathFindExtensionW(lpstrPathAndFile);
2988 #ifdef __REACTOS__
2989         {
2990             LPWSTR filterExt = NULL, lpstrFilter = NULL, pch, pchNext;
2991             LPCWSTR the_ext = NULL;
2992             static const WCHAR szwDot[] = {'.',0};
2993             int PathLength = lstrlenW(lpstrPathAndFile);
2994 
2995             /* get filter extensions */
2996             lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2997                                                     fodInfos->ofnInfos->nFilterIndex - 1);
2998             if (lpstrFilter != (LPWSTR)CB_ERR)  /* control is not empty */
2999             {
3000                 LPWSTR filterSearchIndex, pchFirst = NULL;
3001                 filterExt = heap_alloc((lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
3002                 if (filterExt)
3003                 {
3004                     strcpyW(filterExt, lpstrFilter);
3005 
3006                     if (ext && *ext)
3007                     {
3008                         /* find ext in filter */
3009                         for (pch = filterExt; pch && *pch; pch = pchNext)
3010                         {
3011                             filterSearchIndex = strchrW(pch, ';');
3012                             if (filterSearchIndex)
3013                             {
3014                                 filterSearchIndex[0] = 0;
3015                                 pchNext = filterSearchIndex + 1;
3016                             }
3017                             else
3018                             {
3019                                 pchNext = NULL;
3020                             }
3021 
3022                             while (*pch == '*' || *pch == '.' || *pch == '?')
3023                             {
3024                                 ++pch;
3025                             }
3026 
3027                             if (!pchFirst)
3028                                 pchFirst = pch;
3029 
3030                             if (lstrcmpiW(pch, &ext[1]) == 0)
3031                             {
3032                                 the_ext = pch;
3033                                 break;
3034                             }
3035                         }
3036 
3037                         /* use first one if not found */
3038                         if (!the_ext && pchFirst && *pchFirst)
3039                         {
3040                             the_ext = pchFirst;
3041                         }
3042                     }
3043                 }
3044             }
3045 
3046             if (!the_ext)
3047             {
3048                 /* use default extension if no extension in filter */
3049                 the_ext = fodInfos->defext;
3050             }
3051 
3052             if (the_ext && *the_ext && lstrcmpiW(&ext[1], the_ext) != 0)
3053             {
3054                 if (strlenW(lpstrPathAndFile) + 1 + strlenW(the_ext) + 1 <=
3055                     fodInfos->ofnInfos->nMaxFile)
3056                 {
3057                     /* append the dot */
3058                     lstrcatW(lpstrPathAndFile, szwDot);
3059                     /* append the extension */
3060                     lstrcatW(lpstrPathAndFile, the_ext);
3061                     /* update ext */
3062                     ext = PathFindExtensionW(lpstrPathAndFile);
3063                 }
3064             }
3065 
3066             heap_free(filterExt);
3067 
3068             /* In Open dialog: if file does not exist try without extension */
3069             if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
3070                 lpstrPathAndFile[PathLength] = 0;
3071 
3072             /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
3073             if (*ext)
3074                 ext++;
3075             if (!lstrcmpiW(fodInfos->defext, ext))
3076                 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
3077             else
3078                 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
3079         }
3080 
3081         /* update dialog data */
3082         SetWindowTextW(fodInfos->DlgInfos.hwndFileName, PathFindFileNameW(lpstrPathAndFile));
3083 #else
3084         if (! *ext && fodInfos->defext)
3085         {
3086             /* if no extension is specified with file name, then */
3087             /* attach the extension from file filter or default one */
3088 
3089             WCHAR *filterExt = NULL;
3090             LPWSTR lpstrFilter = NULL;
3091             static const WCHAR szwDot[] = {'.',0};
3092             int PathLength = lstrlenW(lpstrPathAndFile);
3093 
3094             /*Get the file extension from file type filter*/
3095             lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3096                                              fodInfos->ofnInfos->nFilterIndex-1);
3097 
3098             if (lpstrFilter != (LPWSTR)CB_ERR)  /* control is not empty */
3099             {
3100                 WCHAR* filterSearchIndex;
3101                 filterExt = heap_alloc((lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
3102                 strcpyW(filterExt, lpstrFilter);
3103 
3104                 /* if a semicolon-separated list of file extensions was given, do not include the
3105                    semicolon or anything after it in the extension.
3106                    example: if filterExt was "*.abc;*.def", it will become "*.abc" */
3107                 filterSearchIndex = strchrW(filterExt, ';');
3108                 if (filterSearchIndex)
3109                 {
3110                     filterSearchIndex[0] = '\0';
3111                 }
3112 
3113                 /* find the file extension by searching for the first dot in filterExt */
3114                 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
3115                 /* if the extension is invalid or contains a glob, ignore it */
3116                 filterSearchIndex = strchrW(filterExt, '.');
3117                 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
3118                 {
3119                     strcpyW(filterExt, filterSearchIndex);
3120                 }
3121                 else
3122                 {
3123                     heap_free(filterExt);
3124                     filterExt = NULL;
3125                 }
3126             }
3127 
3128             if (!filterExt)
3129             {
3130                 /* use the default file extension */
3131                 filterExt = heap_alloc((lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
3132                 strcpyW(filterExt, fodInfos->defext);
3133             }
3134 
3135             if (*filterExt) /* ignore filterExt="" */
3136             {
3137                 /* Attach the dot*/
3138                 lstrcatW(lpstrPathAndFile, szwDot);
3139                 /* Attach the extension */
3140                 lstrcatW(lpstrPathAndFile, filterExt);
3141             }
3142 
3143             heap_free(filterExt);
3144 
3145             /* In Open dialog: if file does not exist try without extension */
3146             if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
3147                   lpstrPathAndFile[PathLength] = '\0';
3148 
3149             /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
3150             if (*ext)
3151                 ext++;
3152             if (!lstrcmpiW(fodInfos->defext, ext))
3153                 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
3154             else
3155                 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
3156 	}
3157 #endif
3158 
3159 	/* In Save dialog: check if the file already exists */
3160 	if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
3161 	    && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
3162 	    && PathFileExistsW(lpstrPathAndFile))
3163 	{
3164 	  WCHAR lpstrOverwrite[100];
3165 	  int answer;
3166 
3167 	  LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
3168 	  answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
3169 			       MB_YESNO | MB_ICONEXCLAMATION);
3170 	  if (answer == IDNO || answer == IDCANCEL)
3171 	  {
3172 	    ret = FALSE;
3173 	    goto ret;
3174 	  }
3175 	}
3176 
3177         /* In Open dialog: check if it should be created if it doesn't exist */
3178         if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
3179             && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
3180             && !PathFileExistsW(lpstrPathAndFile))
3181         {
3182           WCHAR lpstrCreate[100];
3183           int answer;
3184 
3185           LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
3186           answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
3187                                MB_YESNO | MB_ICONEXCLAMATION);
3188           if (answer == IDNO || answer == IDCANCEL)
3189           {
3190             ret = FALSE;
3191             goto ret;
3192           }
3193         }
3194 
3195         /* Check that the size of the file does not exceed buffer size.
3196              (Allow for extra \0 if OFN_MULTISELECT is set.) */
3197         if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
3198             ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
3199         {
3200 
3201           /* fill destination buffer */
3202           if (fodInfos->ofnInfos->lpstrFile)
3203           {
3204              if(fodInfos->unicode)
3205              {
3206                LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
3207 
3208                lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
3209                if (ofn->Flags & OFN_ALLOWMULTISELECT)
3210                  ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
3211              }
3212              else
3213              {
3214                LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
3215 
3216                WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
3217                                    ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
3218                if (ofn->Flags & OFN_ALLOWMULTISELECT)
3219                  ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
3220              }
3221           }
3222 
3223           if(fodInfos->unicode)
3224           {
3225               LPWSTR lpszTemp;
3226 
3227               /* set filename offset */
3228               lpszTemp = PathFindFileNameW(lpstrPathAndFile);
3229               fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
3230 
3231               /* set extension offset */
3232               lpszTemp = PathFindExtensionW(lpstrPathAndFile);
3233               fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
3234           }
3235           else
3236           {
3237               LPSTR lpszTemp;
3238               CHAR tempFileA[MAX_PATH];
3239 
3240               /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
3241               WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
3242                                   tempFileA, sizeof(tempFileA), NULL, NULL);
3243 
3244               /* set filename offset */
3245               lpszTemp = PathFindFileNameA(tempFileA);
3246               fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
3247 
3248               /* set extension offset */
3249               lpszTemp = PathFindExtensionA(tempFileA);
3250               fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
3251           }
3252 
3253           /* copy currently selected filter to lpstrCustomFilter */
3254           if (fodInfos->ofnInfos->lpstrCustomFilter)
3255           {
3256             LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
3257             int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
3258                                           NULL, 0, NULL, NULL);
3259             if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
3260             {
3261               LPSTR s = ofn->lpstrCustomFilter;
3262               s += strlen(ofn->lpstrCustomFilter)+1;
3263               WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
3264                                   s, len, NULL, NULL);
3265             }
3266           }
3267 
3268 
3269           if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
3270 	      goto ret;
3271 
3272           FILEDLG95_MRU_save_filename(lpstrPathAndFile);
3273 #ifdef __REACTOS__
3274           FILEDLG95_MRU_save_ext(lpstrPathAndFile);
3275 #endif
3276 
3277           TRACE("close\n");
3278 	  FILEDLG95_Clean(hwnd);
3279           ret = EndDialog(hwnd, TRUE);
3280 	}
3281 	else
3282         {
3283           WORD size;
3284 
3285           size = lstrlenW(lpstrPathAndFile) + 1;
3286           if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
3287              size += 1;
3288           /* return needed size in first two bytes of lpstrFile */
3289           if(fodInfos->ofnInfos->lpstrFile)
3290               *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
3291           FILEDLG95_Clean(hwnd);
3292           ret = EndDialog(hwnd, FALSE);
3293           COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
3294         }
3295       }
3296       break;
3297   }
3298 
3299 ret:
3300   if(lpsf) IShellFolder_Release(lpsf);
3301   return ret;
3302 }
3303 
3304 /***********************************************************************
3305  *      FILEDLG95_SHELL_Init
3306  *
3307  * Initialisation of the shell objects
3308  */
3309 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
3310 {
3311   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3312 
3313   TRACE("%p\n", hwnd);
3314 
3315   /*
3316    * Initialisation of the FileOpenDialogInfos structure
3317    */
3318 
3319   /* Shell */
3320 
3321   /*ShellInfos */
3322   fodInfos->ShellInfos.hwndOwner = hwnd;
3323 
3324   /* Disable multi-select if flag not set */
3325   if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
3326   {
3327      fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
3328   }
3329   fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
3330   fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
3331 
3332   /* Construct the IShellBrowser interface */
3333   fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
3334 
3335   return NOERROR;
3336 }
3337 
3338 /***********************************************************************
3339  *      FILEDLG95_SHELL_ExecuteCommand
3340  *
3341  * Change the folder option and refresh the view
3342  * If the function succeeds, the return value is nonzero.
3343  */
3344 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
3345 {
3346   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3347   IContextMenu * pcm;
3348 
3349   TRACE("(%p,%p)\n", hwnd, lpVerb);
3350 
3351   if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
3352 					SVGIO_BACKGROUND,
3353 					&IID_IContextMenu,
3354 					(LPVOID*)&pcm)))
3355   {
3356     CMINVOKECOMMANDINFO ci;
3357     ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
3358     ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
3359     ci.lpVerb = lpVerb;
3360     ci.hwnd = hwnd;
3361 
3362     IContextMenu_InvokeCommand(pcm, &ci);
3363     IContextMenu_Release(pcm);
3364   }
3365 
3366   return FALSE;
3367 }
3368 
3369 /***********************************************************************
3370  *      FILEDLG95_SHELL_UpFolder
3371  *
3372  * Browse to the specified object
3373  * If the function succeeds, the return value is nonzero.
3374  */
3375 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
3376 {
3377   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3378 
3379   TRACE("\n");
3380 
3381   if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3382                                           NULL,
3383                                           SBSP_PARENT)))
3384   {
3385     if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3386         SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3387     return TRUE;
3388   }
3389   return FALSE;
3390 }
3391 /***********************************************************************
3392  *      FILEDLG95_SHELL_Clean
3393  *
3394  * Cleans the memory used by shell objects
3395  */
3396 static void FILEDLG95_SHELL_Clean(HWND hwnd)
3397 {
3398     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3399 
3400     TRACE("\n");
3401 
3402     ILFree(fodInfos->ShellInfos.pidlAbsCurrent);
3403 
3404     /* clean Shell interfaces */
3405     if (fodInfos->Shell.FOIShellView)
3406     {
3407       IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
3408       IShellView_Release(fodInfos->Shell.FOIShellView);
3409     }
3410     if (fodInfos->Shell.FOIShellFolder)
3411       IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
3412     IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
3413     if (fodInfos->Shell.FOIDataObject)
3414       IDataObject_Release(fodInfos->Shell.FOIDataObject);
3415 }
3416 
3417 /***********************************************************************
3418  *      FILEDLG95_FILETYPE_Init
3419  *
3420  * Initialisation of the file type combo box
3421  */
3422 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
3423 {
3424   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3425   int nFilters = 0;  /* number of filters */
3426   int nFilterIndexCB;
3427 
3428   TRACE("%p\n", hwnd);
3429 
3430   if(fodInfos->customfilter)
3431   {
3432       /* customfilter has one entry...  title\0ext\0
3433        * Set first entry of combo box item with customfilter
3434        */
3435       LPWSTR  lpstrExt;
3436       LPCWSTR lpstrPos = fodInfos->customfilter;
3437 
3438       /* Get the title */
3439       lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
3440 
3441       /* Copy the extensions */
3442       if (! *lpstrPos) return E_FAIL;	/* malformed filter */
3443       if (!(lpstrExt = heap_alloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
3444       lstrcpyW(lpstrExt,lpstrPos);
3445 
3446       /* Add the item at the end of the combo */
3447       SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_ADDSTRING, 0, (LPARAM)fodInfos->customfilter);
3448       SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETITEMDATA, nFilters, (LPARAM)lpstrExt);
3449 
3450       nFilters++;
3451   }
3452   if(fodInfos->filter)
3453   {
3454     LPCWSTR lpstrPos = fodInfos->filter;
3455 
3456     for(;;)
3457     {
3458       /* filter is a list...  title\0ext\0......\0\0
3459        * Set the combo item text to the title and the item data
3460        *  to the ext
3461        */
3462       LPCWSTR lpstrDisplay;
3463       LPWSTR lpstrExt;
3464 
3465       /* Get the title */
3466       if(! *lpstrPos) break;	/* end */
3467       lpstrDisplay = lpstrPos;
3468       lpstrPos += lstrlenW(lpstrPos) + 1;
3469 
3470       SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_ADDSTRING, 0, (LPARAM)lpstrDisplay);
3471 
3472       nFilters++;
3473 
3474       /* Copy the extensions */
3475       if (!(lpstrExt = heap_alloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
3476       lstrcpyW(lpstrExt,lpstrPos);
3477       lpstrPos += lstrlenW(lpstrPos) + 1;
3478 
3479       /* Add the item at the end of the combo */
3480       SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETITEMDATA, nFilters - 1, (LPARAM)lpstrExt);
3481 
3482       /* malformed filters are added anyway... */
3483       if (!*lpstrExt) break;
3484     }
3485   }
3486 
3487   /*
3488    * Set the current filter to the one specified
3489    * in the initialisation structure
3490    */
3491   if (fodInfos->filter || fodInfos->customfilter)
3492   {
3493     LPWSTR lpstrFilter;
3494 
3495     /* Check to make sure our index isn't out of bounds. */
3496     if ( fodInfos->ofnInfos->nFilterIndex >
3497          nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
3498       fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
3499 
3500     /* set default filter index */
3501     if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
3502       fodInfos->ofnInfos->nFilterIndex = 1;
3503 
3504     /* calculate index of Combo Box item */
3505     nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
3506     if (fodInfos->customfilter == NULL)
3507       nFilterIndexCB--;
3508 
3509     /* Set the current index selection. */
3510     SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETCURSEL, nFilterIndexCB, 0);
3511 
3512     /* Get the corresponding text string from the combo box. */
3513     lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3514                                              nFilterIndexCB);
3515 
3516     if ((INT_PTR)lpstrFilter == CB_ERR)  /* control is empty */
3517       lpstrFilter = NULL;
3518 
3519     if(lpstrFilter)
3520     {
3521       DWORD len;
3522       CharLowerW(lpstrFilter); /* lowercase */
3523       len = lstrlenW(lpstrFilter)+1;
3524       fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc( len * sizeof(WCHAR) );
3525       lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3526     }
3527   } else
3528       fodInfos->ofnInfos->nFilterIndex = 0;
3529   return S_OK;
3530 }
3531 
3532 /***********************************************************************
3533  *      FILEDLG95_FILETYPE_OnCommand
3534  *
3535  * WM_COMMAND of the file type combo box
3536  * If the function succeeds, the return value is nonzero.
3537  */
3538 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
3539 {
3540   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3541 
3542   switch(wNotifyCode)
3543   {
3544     case CBN_SELENDOK:
3545     {
3546       LPWSTR lpstrFilter;
3547 
3548       /* Get the current item of the filetype combo box */
3549       int iItem = SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_GETCURSEL, 0, 0);
3550 
3551       /* set the current filter index */
3552       fodInfos->ofnInfos->nFilterIndex = iItem +
3553         (fodInfos->customfilter == NULL ? 1 : 0);
3554 
3555       /* Set the current filter with the current selection */
3556       heap_free(fodInfos->ShellInfos.lpstrCurrentFilter);
3557 
3558       lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3559                                              iItem);
3560       if((INT_PTR)lpstrFilter != CB_ERR)
3561       {
3562           DWORD len;
3563           CharLowerW(lpstrFilter); /* lowercase */
3564           len = lstrlenW(lpstrFilter)+1;
3565           fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc( len * sizeof(WCHAR) );
3566           lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3567           if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3568               SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3569       }
3570 
3571       /* Refresh the actual view to display the included items*/
3572       if (fodInfos->Shell.FOIShellView)
3573         IShellView_Refresh(fodInfos->Shell.FOIShellView);
3574     }
3575   }
3576   return FALSE;
3577 }
3578 /***********************************************************************
3579  *      FILEDLG95_FILETYPE_SearchExt
3580  *
3581  * searches for an extension in the filetype box
3582  */
3583 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3584 {
3585   int i, iCount;
3586 
3587   iCount = SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
3588 
3589   TRACE("%s\n", debugstr_w(lpstrExt));
3590 
3591   if(iCount != CB_ERR)
3592   {
3593     for(i=0;i<iCount;i++)
3594     {
3595       if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3596           return i;
3597     }
3598   }
3599   return -1;
3600 }
3601 
3602 /***********************************************************************
3603  *      FILEDLG95_FILETYPE_Clean
3604  *
3605  * Clean the memory used by the filetype combo box
3606  */
3607 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3608 {
3609   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3610   int iPos;
3611   int iCount;
3612 
3613   iCount = SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_GETCOUNT, 0, 0);
3614 
3615   TRACE("\n");
3616 
3617   /* Delete each string of the combo and their associated data */
3618   if(iCount != CB_ERR)
3619   {
3620     for(iPos = iCount-1;iPos>=0;iPos--)
3621     {
3622       heap_free((void *)CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3623       SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_DELETESTRING, iPos, 0);
3624     }
3625   }
3626   /* Current filter */
3627   heap_free(fodInfos->ShellInfos.lpstrCurrentFilter);
3628 }
3629 
3630 /***********************************************************************
3631  *      FILEDLG95_LOOKIN_Init
3632  *
3633  * Initialisation of the look in combo box
3634  */
3635 
3636 /* Small helper function, to determine if the unixfs shell extension is rooted
3637  * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3638  */
3639 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3640     HKEY hKey;
3641     static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3642         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3643         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3644         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3645         'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3646         '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3647         '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3648 
3649     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3650         return FALSE;
3651 
3652     RegCloseKey(hKey);
3653     return TRUE;
3654 }
3655 
3656 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3657 {
3658   IShellFolder	*psfRoot, *psfDrives;
3659   IEnumIDList	*lpeRoot, *lpeDrives;
3660   LPITEMIDLIST	pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3661   HDC hdc;
3662   TEXTMETRICW tm;
3663   LookInInfos *liInfos = heap_alloc_zero(sizeof(*liInfos));
3664 
3665   TRACE("%p\n", hwndCombo);
3666 
3667   liInfos->iMaxIndentation = 0;
3668 
3669   SetPropA(hwndCombo, LookInInfosStr, liInfos);
3670 
3671   hdc = GetDC( hwndCombo );
3672   SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3673   GetTextMetricsW( hdc, &tm );
3674   ReleaseDC( hwndCombo, hdc );
3675 
3676   /* set item height for both text field and listbox */
3677   SendMessageW(hwndCombo, CB_SETITEMHEIGHT, -1, max(tm.tmHeight, GetSystemMetrics(SM_CYSMICON)));
3678   SendMessageW(hwndCombo, CB_SETITEMHEIGHT, 0, max(tm.tmHeight, GetSystemMetrics(SM_CYSMICON)));
3679 
3680   /* Turn on the extended UI for the combo box like Windows does */
3681   SendMessageW(hwndCombo, CB_SETEXTENDEDUI, TRUE, 0);
3682 
3683   /* Initialise data of Desktop folder */
3684   SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3685   FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3686   ILFree(pidlTmp);
3687 
3688   SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3689 
3690   SHGetDesktopFolder(&psfRoot);
3691 
3692   if (psfRoot)
3693   {
3694     /* enumerate the contents of the desktop */
3695     if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3696     {
3697       while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3698       {
3699 	FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3700 
3701 	/* If the unixfs extension is rooted, we don't expand the drives by default */
3702 	if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3703 	{
3704 	  /* special handling for CSIDL_DRIVES */
3705 	  if (ILIsEqual(pidlTmp, pidlDrives))
3706 	  {
3707 	    if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3708 	    {
3709 	      /* enumerate the drives */
3710 	      if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3711 	      {
3712 	        while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3713 	        {
3714 	          pidlAbsTmp = ILCombine(pidlTmp, pidlTmp1);
3715 	          FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3716 	          ILFree(pidlAbsTmp);
3717 	          ILFree(pidlTmp1);
3718 	        }
3719 	        IEnumIDList_Release(lpeDrives);
3720 	      }
3721 	      IShellFolder_Release(psfDrives);
3722 	    }
3723 	  }
3724 	}
3725 
3726         ILFree(pidlTmp);
3727       }
3728       IEnumIDList_Release(lpeRoot);
3729     }
3730     IShellFolder_Release(psfRoot);
3731   }
3732 
3733   ILFree(pidlDrives);
3734 }
3735 
3736 /***********************************************************************
3737  *      FILEDLG95_LOOKIN_DrawItem
3738  *
3739  * WM_DRAWITEM message handler
3740  */
3741 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3742 {
3743   COLORREF crWin = GetSysColor(COLOR_WINDOW);
3744   COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3745   COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3746   RECT rectText;
3747   RECT rectIcon;
3748   SHFILEINFOW sfi;
3749   HIMAGELIST ilItemImage;
3750   int iIndentation;
3751   TEXTMETRICW tm;
3752   LPSFOLDER tmpFolder;
3753   UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3754   UINT icon_width, icon_height;
3755 
3756   TRACE("\n");
3757 
3758   if(pDIStruct->itemID == -1)
3759     return 0;
3760 
3761   if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3762                             pDIStruct->itemID)))
3763     return 0;
3764 
3765 
3766   icon_width = GetSystemMetrics(SM_CXICON);
3767   icon_height = GetSystemMetrics(SM_CYICON);
3768   if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3769   {
3770       icon_width = GetSystemMetrics(SM_CXSMICON);
3771       icon_height = GetSystemMetrics(SM_CYSMICON);
3772       shgfi_flags |= SHGFI_SMALLICON;
3773   }
3774 
3775   ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3776                                              0, &sfi, sizeof (sfi), shgfi_flags );
3777 
3778   /* Is this item selected ? */
3779   if(pDIStruct->itemState & ODS_SELECTED)
3780   {
3781     SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3782     SetBkColor(pDIStruct->hDC,crHighLight);
3783     FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3784   }
3785   else
3786   {
3787     SetTextColor(pDIStruct->hDC,crText);
3788     SetBkColor(pDIStruct->hDC,crWin);
3789     FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3790   }
3791 
3792   /* Do not indent item if drawing in the edit of the combo */
3793   if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3794     iIndentation = 0;
3795   else
3796     iIndentation = tmpFolder->m_iIndent;
3797 
3798   /* Draw text and icon */
3799 
3800   /* Initialise the icon display area */
3801   rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3802   rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3803   rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3804   rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3805 
3806   /* Initialise the text display area */
3807   GetTextMetricsW(pDIStruct->hDC, &tm);
3808   rectText.left = rectIcon.right;
3809   rectText.top =
3810 	  (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3811   rectText.right = pDIStruct->rcItem.right;
3812   rectText.bottom =
3813 	  (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3814 
3815   /* Draw the icon from the image list */
3816   ImageList_Draw(ilItemImage,
3817                  sfi.iIcon,
3818                  pDIStruct->hDC,
3819                  rectIcon.left,
3820                  rectIcon.top,
3821                  ILD_TRANSPARENT );
3822 
3823   /* Draw the associated text */
3824   TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3825   return NOERROR;
3826 }
3827 
3828 /***********************************************************************
3829  *      FILEDLG95_LOOKIN_OnCommand
3830  *
3831  * LookIn combo box WM_COMMAND message handler
3832  * If the function succeeds, the return value is nonzero.
3833  */
3834 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3835 {
3836   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3837 
3838   TRACE("%p\n", fodInfos);
3839 
3840   switch(wNotifyCode)
3841   {
3842     case CBN_SELENDOK:
3843     {
3844       LPSFOLDER tmpFolder;
3845       int iItem;
3846 
3847       iItem = SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_GETCURSEL, 0, 0);
3848 
3849       if( iItem == CB_ERR) return FALSE;
3850 
3851       if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3852                                                iItem)))
3853 	return FALSE;
3854 
3855 
3856       if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3857                                               tmpFolder->pidlItem,
3858                                               SBSP_ABSOLUTE)))
3859       {
3860         if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3861             SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3862         return TRUE;
3863       }
3864       break;
3865     }
3866 
3867   }
3868   return FALSE;
3869 }
3870 
3871 /***********************************************************************
3872  *      FILEDLG95_LOOKIN_AddItem
3873  *
3874  * Adds an absolute pidl item to the lookin combo box
3875  * returns the index of the inserted item
3876  */
3877 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3878 {
3879   LPITEMIDLIST pidlNext;
3880   SHFILEINFOW sfi;
3881   SFOLDER *tmpFolder;
3882   LookInInfos *liInfos;
3883 
3884   TRACE("%p, %p, %d\n", hwnd, pidl, iInsertId);
3885 
3886   if(!pidl)
3887     return -1;
3888 
3889   if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3890     return -1;
3891 
3892   tmpFolder = heap_alloc_zero(sizeof(*tmpFolder));
3893   tmpFolder->m_iIndent = 0;
3894 
3895   /* Calculate the indentation of the item in the lookin*/
3896   pidlNext = pidl;
3897   while ((pidlNext = ILGetNext(pidlNext)))
3898   {
3899     tmpFolder->m_iIndent++;
3900   }
3901 
3902   tmpFolder->pidlItem = ILClone(pidl);
3903 
3904   if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3905     liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3906 
3907   sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3908   SHGetFileInfoW((LPCWSTR)pidl,
3909                   0,
3910                   &sfi,
3911                   sizeof(sfi),
3912                   SHGFI_DISPLAYNAME | SHGFI_PIDL | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3913 
3914   TRACE("-- Add %s attr=0x%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3915 
3916   if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3917   {
3918     int iItemID;
3919 
3920     TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3921 
3922     /* Add the item at the end of the list */
3923     if(iInsertId < 0)
3924     {
3925       iItemID = SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)sfi.szDisplayName);
3926     }
3927     /* Insert the item at the iInsertId position*/
3928     else
3929     {
3930       iItemID = SendMessageW(hwnd, CB_INSERTSTRING, iInsertId, (LPARAM)sfi.szDisplayName);
3931     }
3932 
3933     SendMessageW(hwnd, CB_SETITEMDATA, iItemID, (LPARAM)tmpFolder);
3934     return iItemID;
3935   }
3936 
3937   ILFree( tmpFolder->pidlItem );
3938   heap_free( tmpFolder );
3939   return -1;
3940 
3941 }
3942 
3943 /***********************************************************************
3944  *      FILEDLG95_LOOKIN_InsertItemAfterParent
3945  *
3946  * Insert an item below its parent
3947  */
3948 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3949 {
3950 
3951   LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3952   int iParentPos;
3953 
3954   TRACE("\n");
3955 
3956   if (pidl == pidlParent)
3957     return -1;
3958 
3959   iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3960 
3961   if(iParentPos < 0)
3962   {
3963     iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3964   }
3965 
3966   ILFree(pidlParent);
3967 
3968   return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3969 }
3970 
3971 /***********************************************************************
3972  *      FILEDLG95_LOOKIN_SelectItem
3973  *
3974  * Adds an absolute pidl item to the lookin combo box
3975  * returns the index of the inserted item
3976  */
3977 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3978 {
3979   int iItemPos;
3980   LookInInfos *liInfos;
3981 
3982   TRACE("%p, %p\n", hwnd, pidl);
3983 
3984   iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3985 
3986   liInfos = GetPropA(hwnd,LookInInfosStr);
3987 
3988   if(iItemPos < 0)
3989   {
3990     while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3991     iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3992   }
3993 
3994   else
3995   {
3996     SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3997     while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3998     {
3999       int iRemovedItem;
4000 
4001       if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
4002         break;
4003       if(iRemovedItem < iItemPos)
4004         iItemPos--;
4005     }
4006   }
4007 
4008   SendMessageW(hwnd, CB_SETCURSEL, iItemPos, 0);
4009   liInfos->uSelectedItem = iItemPos;
4010 
4011   return 0;
4012 
4013 }
4014 
4015 /***********************************************************************
4016  *      FILEDLG95_LOOKIN_RemoveMostExpandedItem
4017  *
4018  * Remove the item with an expansion level over iExpansionLevel
4019  */
4020 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
4021 {
4022   int iItemPos;
4023   LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
4024 
4025   TRACE("\n");
4026 
4027   if(liInfos->iMaxIndentation <= 2)
4028     return -1;
4029 
4030   if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
4031   {
4032     SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
4033     ILFree(tmpFolder->pidlItem);
4034     heap_free(tmpFolder);
4035     SendMessageW(hwnd, CB_DELETESTRING, iItemPos, 0);
4036     liInfos->iMaxIndentation--;
4037 
4038     return iItemPos;
4039   }
4040 
4041   return -1;
4042 }
4043 
4044 /***********************************************************************
4045  *      FILEDLG95_LOOKIN_SearchItem
4046  *
4047  * Search for pidl in the lookin combo box
4048  * returns the index of the found item
4049  */
4050 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
4051 {
4052   int i = 0;
4053   int iCount;
4054 
4055   iCount = SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
4056 
4057   TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
4058 
4059   if (iCount != CB_ERR)
4060   {
4061     for(;i<iCount;i++)
4062     {
4063       LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
4064 
4065       if (iSearchMethod == SEARCH_PIDL && ILIsEqual((LPITEMIDLIST)searchArg, tmpFolder->pidlItem))
4066         return i;
4067       if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
4068         return i;
4069     }
4070   }
4071 
4072   return -1;
4073 }
4074 
4075 /***********************************************************************
4076  *      FILEDLG95_LOOKIN_Clean
4077  *
4078  * Clean the memory used by the lookin combo box
4079  */
4080 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
4081 {
4082     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
4083     LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
4084     int iPos, iCount;
4085 
4086     iCount = SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_GETCOUNT, 0, 0);
4087 
4088     TRACE("\n");
4089 
4090     /* Delete each string of the combo and their associated data */
4091     if (iCount != CB_ERR)
4092     {
4093       for(iPos = iCount-1;iPos>=0;iPos--)
4094       {
4095         SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
4096         ILFree(tmpFolder->pidlItem);
4097         heap_free(tmpFolder);
4098         SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_DELETESTRING, iPos, 0);
4099       }
4100     }
4101 
4102     /* LookInInfos structure */
4103     heap_free(liInfos);
4104     RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
4105 }
4106 
4107 /***********************************************************************
4108  *          get_def_format
4109  *
4110  * Fill the FORMATETC used in the shell id list
4111  */
4112 static FORMATETC get_def_format(void)
4113 {
4114     static CLIPFORMAT cfFormat;
4115     FORMATETC formatetc;
4116 
4117     if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA);
4118     formatetc.cfFormat = cfFormat;
4119     formatetc.ptd = 0;
4120     formatetc.dwAspect = DVASPECT_CONTENT;
4121     formatetc.lindex = -1;
4122     formatetc.tymed = TYMED_HGLOBAL;
4123     return formatetc;
4124 }
4125 
4126 /***********************************************************************
4127  * FILEDLG95_FILENAME_FillFromSelection
4128  *
4129  * fills the edit box from the cached DataObject
4130  */
4131 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
4132 {
4133     FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
4134     LPITEMIDLIST      pidl;
4135     LPWSTR            lpstrAllFiles, lpstrTmp;
4136     UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength;
4137     STGMEDIUM medium;
4138     LPIDA cida;
4139     FORMATETC formatetc = get_def_format();
4140 
4141     TRACE("\n");
4142 
4143     if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium)))
4144         return;
4145 
4146     cida = GlobalLock(medium.u.hGlobal);
4147     nFileSelected = cida->cidl;
4148 
4149     /* Allocate a buffer */
4150     nAllFilesMaxLength = MAX_PATH + 3;
4151     lpstrAllFiles = heap_alloc_zero(nAllFilesMaxLength * sizeof(WCHAR));
4152     if (!lpstrAllFiles)
4153         goto ret;
4154 
4155     /* Loop through the selection, handle only files (not folders) */
4156     for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++)
4157     {
4158         pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]);
4159         if (pidl)
4160         {
4161             if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl))
4162             {
4163                 if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength)
4164                 {
4165                     nAllFilesMaxLength *= 2;
4166                     lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR));
4167                     if (!lpstrTmp)
4168                         goto ret;
4169                     lpstrAllFiles = lpstrTmp;
4170                 }
4171                 nFiles += 1;
4172                 lpstrAllFiles[nAllFilesLength++] = '"';
4173                 GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength);
4174                 nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength);
4175                 nAllFilesLength += nThisFileLength;
4176                 lpstrAllFiles[nAllFilesLength++] = '"';
4177                 lpstrAllFiles[nAllFilesLength++] = ' ';
4178             }
4179         }
4180     }
4181 
4182     if (nFiles != 0)
4183     {
4184         /* If there's only one file, use the name as-is without quotes */
4185         lpstrTmp = lpstrAllFiles;
4186         if (nFiles == 1)
4187         {
4188             lpstrTmp += 1;
4189             lpstrTmp[nThisFileLength] = 0;
4190         }
4191         SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp);
4192         /* Select the file name like Windows does */
4193         if (filename_is_edit(fodInfos))
4194             SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
4195     }
4196 
4197 ret:
4198     heap_free(lpstrAllFiles);
4199     COMCTL32_ReleaseStgMedium(medium);
4200 }
4201 
4202 
4203 /* copied from shell32 to avoid linking to it
4204  * Although shell32 is already linked the behaviour of exported StrRetToStrN
4205  * is dependent on whether emulated OS is unicode or not.
4206  */
4207 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
4208 {
4209 	switch (src->uType)
4210 	{
4211 	  case STRRET_WSTR:
4212 	    lstrcpynW(dest, src->u.pOleStr, len);
4213 	    CoTaskMemFree(src->u.pOleStr);
4214 	    break;
4215 
4216 	  case STRRET_CSTR:
4217             if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
4218                   dest[len-1] = 0;
4219 	    break;
4220 
4221 	  case STRRET_OFFSET:
4222             if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
4223                   dest[len-1] = 0;
4224 	    break;
4225 
4226 	  default:
4227 	    FIXME("unknown type %x!\n", src->uType);
4228 	    if (len) *dest = '\0';
4229 	    return E_FAIL;
4230 	}
4231 	return S_OK;
4232 }
4233 
4234 /***********************************************************************
4235  * FILEDLG95_FILENAME_GetFileNames
4236  *
4237  * Copies the filenames to a delimited string list.
4238  */
4239 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
4240 {
4241 	FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
4242 	UINT nFileCount = 0;	/* number of files */
4243 	UINT nStrLen = 0;	/* length of string in edit control */
4244 	LPWSTR lpstrEdit;	/* buffer for string from edit control */
4245 
4246 	TRACE("\n");
4247 
4248 	/* get the filenames from the filename control */
4249 	nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
4250 	lpstrEdit = heap_alloc( (nStrLen+1)*sizeof(WCHAR) );
4251 	GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
4252 
4253 	TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
4254 
4255 	nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
4256 	heap_free(lpstrEdit);
4257 	return nFileCount;
4258 }
4259 
4260 /*
4261  * DATAOBJECT Helper functions
4262  */
4263 
4264 /***********************************************************************
4265  * COMCTL32_ReleaseStgMedium
4266  *
4267  * like ReleaseStgMedium from ole32
4268  */
4269 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
4270 {
4271       if(medium.pUnkForRelease)
4272       {
4273         IUnknown_Release(medium.pUnkForRelease);
4274       }
4275       else
4276       {
4277         GlobalUnlock(medium.u.hGlobal);
4278         GlobalFree(medium.u.hGlobal);
4279       }
4280 }
4281 
4282 /***********************************************************************
4283  *          GetPidlFromDataObject
4284  *
4285  * Return pidl(s) by number from the cached DataObject
4286  *
4287  * nPidlIndex=0 gets the fully qualified root path
4288  */
4289 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
4290 {
4291 
4292     STGMEDIUM medium;
4293     FORMATETC formatetc = get_def_format();
4294     LPITEMIDLIST pidl = NULL;
4295 
4296     TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
4297 
4298     if (!doSelected)
4299         return NULL;
4300 
4301     /* Get the pidls from IDataObject */
4302     if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
4303     {
4304       LPIDA cida = GlobalLock(medium.u.hGlobal);
4305       if(nPidlIndex <= cida->cidl)
4306       {
4307         pidl = ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
4308       }
4309       COMCTL32_ReleaseStgMedium(medium);
4310     }
4311     return pidl;
4312 }
4313 
4314 /***********************************************************************
4315  *          GetNumSelected
4316  *
4317  * Return the number of selected items in the DataObject.
4318  *
4319 */
4320 static UINT GetNumSelected( IDataObject *doSelected )
4321 {
4322     UINT retVal = 0;
4323     STGMEDIUM medium;
4324     FORMATETC formatetc = get_def_format();
4325 
4326     TRACE("sv=%p\n", doSelected);
4327 
4328     if (!doSelected) return 0;
4329 
4330     /* Get the pidls from IDataObject */
4331     if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
4332     {
4333       LPIDA cida = GlobalLock(medium.u.hGlobal);
4334       retVal = cida->cidl;
4335       COMCTL32_ReleaseStgMedium(medium);
4336       return retVal;
4337     }
4338     return 0;
4339 }
4340 
4341 /*
4342  * TOOLS
4343  */
4344 
4345 /***********************************************************************
4346  *      GetName
4347  *
4348  * Get the pidl's display name (relative to folder) and
4349  * put it in lpstrFileName.
4350  *
4351  * Return NOERROR on success,
4352  * E_FAIL otherwise
4353  */
4354 
4355 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
4356 {
4357   STRRET str;
4358   HRESULT hRes;
4359 
4360   TRACE("sf=%p pidl=%p\n", lpsf, pidl);
4361 
4362   if(!lpsf)
4363   {
4364     SHGetDesktopFolder(&lpsf);
4365     hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
4366     IShellFolder_Release(lpsf);
4367     return hRes;
4368   }
4369 
4370   /* Get the display name of the pidl relative to the folder */
4371   if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
4372   {
4373       return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
4374   }
4375   return E_FAIL;
4376 }
4377 
4378 /***********************************************************************
4379  *      GetShellFolderFromPidl
4380  *
4381  * pidlRel is the item pidl relative
4382  * Return the IShellFolder of the absolute pidl
4383  */
4384 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
4385 {
4386   IShellFolder *psf = NULL,*psfParent;
4387 
4388   TRACE("%p\n", pidlAbs);
4389 
4390   if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
4391   {
4392     psf = psfParent;
4393     if(pidlAbs && pidlAbs->mkid.cb)
4394     {
4395       if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
4396       {
4397 	IShellFolder_Release(psfParent);
4398         return psf;
4399       }
4400     }
4401     /* return the desktop */
4402     return psfParent;
4403   }
4404   return NULL;
4405 }
4406 
4407 /***********************************************************************
4408  *      GetParentPidl
4409  *
4410  * Return the LPITEMIDLIST to the parent of the pidl in the list
4411  */
4412 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
4413 {
4414   LPITEMIDLIST pidlParent;
4415 
4416   TRACE("%p\n", pidl);
4417 
4418   pidlParent = ILClone(pidl);
4419   ILRemoveLastID(pidlParent);
4420 
4421   return pidlParent;
4422 }
4423 
4424 /***********************************************************************
4425  *      GetPidlFromName
4426  *
4427  * returns the pidl of the file name relative to folder
4428  * NULL if an error occurred
4429  */
4430 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
4431 {
4432   LPITEMIDLIST pidl = NULL;
4433   ULONG ulEaten;
4434 
4435   TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
4436 
4437   if(!lpcstrFileName) return NULL;
4438   if(!*lpcstrFileName) return NULL;
4439 
4440   if(!lpsf)
4441   {
4442     if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
4443         IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
4444         IShellFolder_Release(lpsf);
4445     }
4446   }
4447   else
4448   {
4449     IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
4450   }
4451   return pidl;
4452 }
4453 
4454 /*
4455 */
4456 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
4457 {
4458 	ULONG uAttr  = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
4459 	HRESULT ret;
4460 
4461 	TRACE("%p, %p\n", psf, pidl);
4462 
4463   	ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
4464 
4465 	TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
4466 	/* see documentation shell 4.1*/
4467         return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
4468 }
4469 
4470 /***********************************************************************
4471  *      BrowseSelectedFolder
4472  */
4473 static BOOL BrowseSelectedFolder(HWND hwnd)
4474 {
4475   FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
4476   BOOL bBrowseSelFolder = FALSE;
4477 
4478   TRACE("\n");
4479 
4480   if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
4481   {
4482       LPITEMIDLIST pidlSelection;
4483 
4484       /* get the file selected */
4485       pidlSelection  = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
4486       if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
4487       {
4488           if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
4489                          pidlSelection, SBSP_RELATIVE ) ) )
4490           {
4491                WCHAR buf[64];
4492                LoadStringW( COMDLG32_hInstance, IDS_PATHNOTEXISTING, buf, ARRAY_SIZE(buf));
4493                MessageBoxW( hwnd, buf, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
4494           }
4495           bBrowseSelFolder = TRUE;
4496           if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
4497               SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
4498       }
4499       ILFree( pidlSelection );
4500   }
4501 
4502   return bBrowseSelFolder;
4503 }
4504 
4505 static inline BOOL valid_struct_size( DWORD size )
4506 {
4507     return (size == OPENFILENAME_SIZE_VERSION_400W) ||
4508         (size == sizeof( OPENFILENAMEW ));
4509 }
4510 
4511 static inline BOOL is_win16_looks(DWORD flags)
4512 {
4513     return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
4514             !(flags & OFN_EXPLORER));
4515 }
4516 
4517 /* ------------------ APIs ---------------------- */
4518 
4519 /***********************************************************************
4520  *            GetOpenFileNameA  (COMDLG32.@)
4521  *
4522  * Creates a dialog box for the user to select a file to open.
4523  *
4524  * RETURNS
4525  *    TRUE on success: user enters a valid file
4526  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4527  *
4528  */
4529 BOOL WINAPI GetOpenFileNameA(OPENFILENAMEA *ofn)
4530 {
4531     TRACE("flags 0x%08x\n", ofn->Flags);
4532 
4533     if (!valid_struct_size( ofn->lStructSize ))
4534     {
4535         COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4536         return FALSE;
4537     }
4538 
4539     /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4540     if (ofn->Flags & OFN_FILEMUSTEXIST)
4541         ofn->Flags |= OFN_PATHMUSTEXIST;
4542 
4543     if (is_win16_looks(ofn->Flags))
4544         return GetFileName31A(ofn, OPEN_DIALOG);
4545     else
4546     {
4547         FileOpenDlgInfos info;
4548 
4549         init_filedlg_infoA(ofn, &info);
4550         return GetFileDialog95(&info, OPEN_DIALOG);
4551     }
4552 }
4553 
4554 /***********************************************************************
4555  *            GetOpenFileNameW (COMDLG32.@)
4556  *
4557  * Creates a dialog box for the user to select a file to open.
4558  *
4559  * RETURNS
4560  *    TRUE on success: user enters a valid file
4561  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4562  *
4563  */
4564 BOOL WINAPI GetOpenFileNameW(OPENFILENAMEW *ofn)
4565 {
4566     TRACE("flags 0x%08x\n", ofn->Flags);
4567 
4568     if (!valid_struct_size( ofn->lStructSize ))
4569     {
4570         COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4571         return FALSE;
4572     }
4573 
4574     /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4575     if (ofn->Flags & OFN_FILEMUSTEXIST)
4576         ofn->Flags |= OFN_PATHMUSTEXIST;
4577 
4578     if (is_win16_looks(ofn->Flags))
4579         return GetFileName31W(ofn, OPEN_DIALOG);
4580     else
4581     {
4582         FileOpenDlgInfos info;
4583 
4584         init_filedlg_infoW(ofn, &info);
4585         return GetFileDialog95(&info, OPEN_DIALOG);
4586     }
4587 }
4588 
4589 
4590 /***********************************************************************
4591  *            GetSaveFileNameA  (COMDLG32.@)
4592  *
4593  * Creates a dialog box for the user to select a file to save.
4594  *
4595  * RETURNS
4596  *    TRUE on success: user enters a valid file
4597  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4598  *
4599  */
4600 BOOL WINAPI GetSaveFileNameA(OPENFILENAMEA *ofn)
4601 {
4602     if (!valid_struct_size( ofn->lStructSize ))
4603     {
4604         COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4605         return FALSE;
4606     }
4607 
4608     if (is_win16_looks(ofn->Flags))
4609         return GetFileName31A(ofn, SAVE_DIALOG);
4610     else
4611     {
4612         FileOpenDlgInfos info;
4613 
4614         init_filedlg_infoA(ofn, &info);
4615         return GetFileDialog95(&info, SAVE_DIALOG);
4616     }
4617 }
4618 
4619 /***********************************************************************
4620  *            GetSaveFileNameW  (COMDLG32.@)
4621  *
4622  * Creates a dialog box for the user to select a file to save.
4623  *
4624  * RETURNS
4625  *    TRUE on success: user enters a valid file
4626  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4627  *
4628  */
4629 BOOL WINAPI GetSaveFileNameW(
4630 	LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4631 {
4632     if (!valid_struct_size( ofn->lStructSize ))
4633     {
4634         COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4635         return FALSE;
4636     }
4637 
4638     if (is_win16_looks(ofn->Flags))
4639         return GetFileName31W(ofn, SAVE_DIALOG);
4640     else
4641     {
4642         FileOpenDlgInfos info;
4643 
4644         init_filedlg_infoW(ofn, &info);
4645         return GetFileDialog95(&info, SAVE_DIALOG);
4646     }
4647 }
4648 
4649 /***********************************************************************
4650  *	GetFileTitleA		(COMDLG32.@)
4651  *
4652  * See GetFileTitleW.
4653  */
4654 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4655 {
4656     int ret;
4657     UNICODE_STRING strWFile;
4658     LPWSTR lpWTitle;
4659 
4660     RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4661     lpWTitle = heap_alloc(cbBuf * sizeof(WCHAR));
4662     ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4663     if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4664     RtlFreeUnicodeString( &strWFile );
4665     heap_free( lpWTitle );
4666     return ret;
4667 }
4668 
4669 
4670 /***********************************************************************
4671  *	GetFileTitleW		(COMDLG32.@)
4672  *
4673  * Get the name of a file.
4674  *
4675  * PARAMS
4676  *  lpFile  [I] name and location of file
4677  *  lpTitle [O] returned file name
4678  *  cbBuf   [I] buffer size of lpTitle
4679  *
4680  * RETURNS
4681  *  Success: zero
4682  *  Failure: negative number.
4683  */
4684 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4685 {
4686 	int i, len;
4687         static const WCHAR brkpoint[] = {'*','[',']',0};
4688 	TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4689 
4690 	if(lpFile == NULL || lpTitle == NULL)
4691 		return -1;
4692 
4693 	len = lstrlenW(lpFile);
4694 
4695 	if (len == 0)
4696 		return -1;
4697 
4698 	if(strpbrkW(lpFile, brkpoint))
4699 		return -1;
4700 
4701 	len--;
4702 
4703 	if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4704 		return -1;
4705 
4706 	for(i = len; i >= 0; i--)
4707 	{
4708 		if (lpFile[i] == '/' ||  lpFile[i] == '\\' ||  lpFile[i] == ':')
4709 		{
4710 			i++;
4711 			break;
4712 		}
4713 	}
4714 
4715 	if(i == -1)
4716 		i++;
4717 
4718 	TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4719 
4720 	len = lstrlenW(lpFile+i)+1;
4721 	if(cbBuf < len)
4722 		return len;
4723 
4724 	lstrcpyW(lpTitle, &lpFile[i]);
4725 	return 0;
4726 }
4727