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