comdlg32: Move all of the win 3.1 style functionality into the same file.
[wine/multimedia.git] / dlls / comdlg32 / filedlg.c
blob3df67df5553954bd49074caa0a36b4f564ae7baa
1 /*
2 * COMMDLG - File Open Dialogs Win95 look and feel
4 * Copyright 1999 Francois Boisvert
5 * Copyright 1999, 2000 Juergen Schmied
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.
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.
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
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).
29 * FIXME: any hook gets a OPENFILENAMEA structure
31 * FIXME: CDN_FILEOK is wrong implemented, other CDN_ messages likely too
33 * FIXME: old style hook messages are not implemented (except FILEOKSTRING)
35 * FIXME: algorithm for selecting the initial directory is too simple
37 * FIXME: add to recent docs
39 * FIXME: flags not implemented: OFN_DONTADDTORECENT,
40 * OFN_NODEREFERENCELINKS, OFN_NOREADONLYRETURN,
41 * OFN_NOTESTFILECREATE, OFN_USEMONIKERS
43 * FIXME: lCustData for lpfnHook (WM_INITDIALOG)
48 #include "config.h"
49 #include "wine/port.h"
51 #include <ctype.h>
52 #include <stdlib.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <string.h>
57 #define COBJMACROS
58 #define NONAMELESSUNION
59 #define NONAMELESSSTRUCT
61 #include "windef.h"
62 #include "winbase.h"
63 #include "winternl.h"
64 #include "winnls.h"
65 #include "wingdi.h"
66 #include "winreg.h"
67 #include "winuser.h"
68 #include "commdlg.h"
69 #include "dlgs.h"
70 #include "cdlg.h"
71 #include "cderr.h"
72 #include "shellapi.h"
73 #include "shlobj.h"
74 #include "filedlgbrowser.h"
75 #include "shlwapi.h"
77 #include "wine/unicode.h"
78 #include "wine/debug.h"
80 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
82 #define UNIMPLEMENTED_FLAGS \
83 (OFN_DONTADDTORECENT |\
84 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
85 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
87 #define IsHooked(fodInfos) \
88 ((fodInfos->ofnInfos->Flags & OFN_ENABLEHOOK) && fodInfos->ofnInfos->lpfnHook)
89 /***********************************************************************
90 * Data structure and global variables
92 typedef struct SFolder
94 int m_iImageIndex; /* Index of picture in image list */
95 HIMAGELIST hImgList;
96 int m_iIndent; /* Indentation index */
97 LPITEMIDLIST pidlItem; /* absolute pidl of the item */
99 } SFOLDER,*LPSFOLDER;
101 typedef struct tagLookInInfo
103 int iMaxIndentation;
104 UINT uSelectedItem;
105 } LookInInfos;
108 /***********************************************************************
109 * Defines and global variables
112 /* Draw item constant */
113 #define ICONWIDTH 18
114 #define XTEXTOFFSET 3
116 /* AddItem flags*/
117 #define LISTEND -1
119 /* SearchItem methods */
120 #define SEARCH_PIDL 1
121 #define SEARCH_EXP 2
122 #define ITEM_NOTFOUND -1
124 /* Undefined windows message sent by CreateViewObject*/
125 #define WM_GETISHELLBROWSER WM_USER+7
127 /* NOTE
128 * Those macros exist in windowsx.h. However, you can't really use them since
129 * they rely on the UNICODE defines and can't be used inside Wine itself.
132 /* Combo box macros */
133 #define CBAddString(hwnd,str) \
134 SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)(str));
136 #define CBInsertString(hwnd,str,pos) \
137 SendMessageW(hwnd, CB_INSERTSTRING, (WPARAM)(pos), (LPARAM)(str));
139 #define CBDeleteString(hwnd,pos) \
140 SendMessageW(hwnd, CB_DELETESTRING, (WPARAM)(pos), 0);
142 #define CBSetItemDataPtr(hwnd,iItemId,dataPtr) \
143 SendMessageW(hwnd, CB_SETITEMDATA, (WPARAM)(iItemId), (LPARAM)(dataPtr));
145 #define CBGetItemDataPtr(hwnd,iItemId) \
146 SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
148 #define CBGetLBText(hwnd,iItemId,str) \
149 SendMessageW(hwnd, CB_GETLBTEXT, (WPARAM)(iItemId), (LPARAM)(str));
151 #define CBGetCurSel(hwnd) \
152 SendMessageW(hwnd, CB_GETCURSEL, 0, 0);
154 #define CBSetCurSel(hwnd,pos) \
155 SendMessageW(hwnd, CB_SETCURSEL, (WPARAM)(pos), 0);
157 #define CBGetCount(hwnd) \
158 SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
159 #define CBShowDropDown(hwnd,show) \
160 SendMessageW(hwnd, CB_SHOWDROPDOWN, (WPARAM)(show), 0);
161 #define CBSetItemHeight(hwnd,index,height) \
162 SendMessageW(hwnd, CB_SETITEMHEIGHT, (WPARAM)(index), (LPARAM)(height));
164 #define CBSetExtendedUI(hwnd,flag) \
165 SendMessageW(hwnd, CB_SETEXTENDEDUI, (WPARAM)(flag), 0)
167 const char FileOpenDlgInfosStr[] = "FileOpenDlgInfos"; /* windows property description string */
168 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
169 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
171 static const WCHAR LastVisitedMRUW[] =
172 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
173 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
174 'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
175 'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
176 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
178 /***********************************************************************
179 * Prototypes
182 /* Internal functions used by the dialog */
183 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
184 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
185 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
186 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
187 static BOOL FILEDLG95_OnOpen(HWND hwnd);
188 static LRESULT FILEDLG95_InitControls(HWND hwnd);
189 static void FILEDLG95_Clean(HWND hwnd);
191 /* Functions used by the shell navigation */
192 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
193 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd);
194 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
195 static void FILEDLG95_SHELL_Clean(HWND hwnd);
196 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd);
198 /* Functions used by the EDIT box */
199 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
201 /* Functions used by the filetype combo box */
202 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
203 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
204 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
205 static void FILEDLG95_FILETYPE_Clean(HWND hwnd);
207 /* Functions used by the Look In combo box */
208 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo);
209 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
210 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
211 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
212 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
213 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
214 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
215 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
216 static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
218 /* Functions for dealing with the most-recently-used registry keys */
219 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
220 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
221 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
223 /* Miscellaneous tool functions */
224 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
225 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
226 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl);
227 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
228 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
229 static UINT GetNumSelected( IDataObject *doSelected );
231 /* Shell memory allocation */
232 static void *MemAlloc(UINT size);
233 static void MemFree(void *mem);
235 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
236 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
237 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
238 static BOOL BrowseSelectedFolder(HWND hwnd);
240 /***********************************************************************
241 * GetFileName95
243 * Creates an Open common dialog box that lets the user select
244 * the drive, directory, and the name of a file or set of files to open.
246 * IN : The FileOpenDlgInfos structure associated with the dialog
247 * OUT : TRUE on success
248 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
250 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
253 LRESULT lRes;
254 LPCVOID origTemplate;
255 DWORD dwSize;
256 LPDLGTEMPLATEW template;
257 HRSRC hRes;
258 HANDLE hDlgTmpl = 0;
259 HRESULT hr;
261 /* test for missing functionality */
262 if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
264 FIXME("Flags 0x%08x not yet implemented\n",
265 fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
268 /* Create the dialog from a template */
270 if(!(hRes = FindResourceW(COMDLG32_hInstance,MAKEINTRESOURCEW(NEWFILEOPENORD),(LPCWSTR)RT_DIALOG)))
272 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
273 return FALSE;
275 if (!(dwSize = SizeofResource(COMDLG32_hInstance, hRes)) ||
276 !(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes)) ||
277 !(origTemplate = LockResource(hDlgTmpl)))
279 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
280 return FALSE;
282 if (!(template = HeapAlloc(GetProcessHeap(), 0, dwSize)))
284 COMDLG32_SetCommDlgExtendedError(CDERR_MEMALLOCFAILURE);
285 return FALSE;
287 memcpy(template, origTemplate, dwSize);
289 /* msdn: explorer style dialogs permit sizing by default.
290 * The OFN_ENABLESIZING flag is only needed when a hook or
291 * custom tmeplate is provided */
292 if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
293 !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
294 fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
296 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
298 template->style |= WS_SIZEBOX;
299 fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
300 fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
302 else
303 template->style &= ~WS_SIZEBOX;
306 /* old style hook messages */
307 if (IsHooked(fodInfos))
309 fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
310 fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
311 fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
312 fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
315 /* Some shell namespace extensions depend on COM being initialized. */
316 hr = OleInitialize(NULL);
318 if (fodInfos->unicode)
319 lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
320 template,
321 fodInfos->ofnInfos->hwndOwner,
322 FileOpenDlgProc95,
323 (LPARAM) fodInfos);
324 else
325 lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
326 template,
327 fodInfos->ofnInfos->hwndOwner,
328 FileOpenDlgProc95,
329 (LPARAM) fodInfos);
330 if (SUCCEEDED(hr))
331 OleUninitialize();
333 HeapFree(GetProcessHeap(), 0, template);
335 /* Unable to create the dialog */
336 if( lRes == -1)
337 return FALSE;
339 return lRes;
342 /***********************************************************************
343 * GetFileDialog95A
345 * Call GetFileName95 with this structure and clean the memory.
347 * IN : The OPENFILENAMEA initialisation structure passed to
348 * GetOpenFileNameA win api function (see filedlg.c)
350 static BOOL GetFileDialog95A(LPOPENFILENAMEA ofn,UINT iDlgType)
352 BOOL ret;
353 FileOpenDlgInfos fodInfos;
354 LPSTR lpstrSavDir = NULL;
355 LPWSTR title = NULL;
356 LPWSTR defext = NULL;
357 LPWSTR filter = NULL;
358 LPWSTR customfilter = NULL;
360 /* Initialize CommDlgExtendedError() */
361 COMDLG32_SetCommDlgExtendedError(0);
363 /* Initialize FileOpenDlgInfos structure */
364 ZeroMemory(&fodInfos, sizeof(FileOpenDlgInfos));
366 /* Pass in the original ofn */
367 fodInfos.ofnInfos = (LPOPENFILENAMEW)ofn;
369 /* save current directory */
370 if (ofn->Flags & OFN_NOCHANGEDIR)
372 lpstrSavDir = MemAlloc(MAX_PATH);
373 GetCurrentDirectoryA(MAX_PATH, lpstrSavDir);
376 fodInfos.unicode = FALSE;
378 /* convert all the input strings to unicode */
379 if(ofn->lpstrInitialDir)
381 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrInitialDir, -1, NULL, 0 );
382 fodInfos.initdir = MemAlloc((len+1)*sizeof(WCHAR));
383 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrInitialDir, -1, fodInfos.initdir, len);
385 else
386 fodInfos.initdir = NULL;
388 if(ofn->lpstrFile)
390 fodInfos.filename = MemAlloc(ofn->nMaxFile*sizeof(WCHAR));
391 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFile, -1, fodInfos.filename, ofn->nMaxFile);
393 else
394 fodInfos.filename = NULL;
396 if(ofn->lpstrDefExt)
398 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrDefExt, -1, NULL, 0 );
399 defext = MemAlloc((len+1)*sizeof(WCHAR));
400 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrDefExt, -1, defext, len);
402 fodInfos.defext = defext;
404 if(ofn->lpstrTitle)
406 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrTitle, -1, NULL, 0 );
407 title = MemAlloc((len+1)*sizeof(WCHAR));
408 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrTitle, -1, title, len);
410 fodInfos.title = title;
412 if (ofn->lpstrFilter)
414 LPCSTR s;
415 int n, len;
417 /* filter is a list... title\0ext\0......\0\0 */
418 s = ofn->lpstrFilter;
419 while (*s) s = s+strlen(s)+1;
420 s++;
421 n = s - ofn->lpstrFilter;
422 len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0 );
423 filter = MemAlloc(len*sizeof(WCHAR));
424 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFilter, n, filter, len );
426 fodInfos.filter = filter;
428 /* convert lpstrCustomFilter */
429 if (ofn->lpstrCustomFilter)
431 LPCSTR s;
432 int n, len;
434 /* customfilter contains a pair of strings... title\0ext\0 */
435 s = ofn->lpstrCustomFilter;
436 if (*s) s = s+strlen(s)+1;
437 if (*s) s = s+strlen(s)+1;
438 n = s - ofn->lpstrCustomFilter;
439 len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0 );
440 customfilter = MemAlloc(len*sizeof(WCHAR));
441 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrCustomFilter, n, customfilter, len );
443 fodInfos.customfilter = customfilter;
445 /* Initialize the dialog property */
446 fodInfos.DlgInfos.dwDlgProp = 0;
447 fodInfos.DlgInfos.hwndCustomDlg = NULL;
449 switch(iDlgType)
451 case OPEN_DIALOG :
452 ret = GetFileName95(&fodInfos);
453 break;
454 case SAVE_DIALOG :
455 fodInfos.DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
456 ret = GetFileName95(&fodInfos);
457 break;
458 default :
459 ret = 0;
462 if (lpstrSavDir)
464 SetCurrentDirectoryA(lpstrSavDir);
465 MemFree(lpstrSavDir);
468 MemFree(title);
469 MemFree(defext);
470 MemFree(filter);
471 MemFree(customfilter);
472 MemFree(fodInfos.initdir);
473 MemFree(fodInfos.filename);
475 TRACE("selected file: %s\n",ofn->lpstrFile);
477 return ret;
480 /***********************************************************************
481 * GetFileDialog95W
483 * Copy the OPENFILENAMEW structure in a FileOpenDlgInfos structure.
484 * Call GetFileName95 with this structure and clean the memory.
487 static BOOL GetFileDialog95W(LPOPENFILENAMEW ofn,UINT iDlgType)
489 BOOL ret;
490 FileOpenDlgInfos fodInfos;
491 LPWSTR lpstrSavDir = NULL;
493 /* Initialize CommDlgExtendedError() */
494 COMDLG32_SetCommDlgExtendedError(0);
496 /* Initialize FileOpenDlgInfos structure */
497 ZeroMemory(&fodInfos, sizeof(FileOpenDlgInfos));
499 /* Pass in the original ofn */
500 fodInfos.ofnInfos = ofn;
502 fodInfos.title = ofn->lpstrTitle;
503 fodInfos.defext = ofn->lpstrDefExt;
504 fodInfos.filter = ofn->lpstrFilter;
505 fodInfos.customfilter = ofn->lpstrCustomFilter;
507 /* convert string arguments, save others */
508 if(ofn->lpstrFile)
510 fodInfos.filename = MemAlloc(ofn->nMaxFile*sizeof(WCHAR));
511 lstrcpynW(fodInfos.filename,ofn->lpstrFile,ofn->nMaxFile);
513 else
514 fodInfos.filename = NULL;
516 if(ofn->lpstrInitialDir)
518 /* fodInfos.initdir = strdupW(ofn->lpstrInitialDir); */
519 DWORD len = lstrlenW(ofn->lpstrInitialDir)+1;
520 fodInfos.initdir = MemAlloc(len*sizeof(WCHAR));
521 memcpy(fodInfos.initdir,ofn->lpstrInitialDir,len*sizeof(WCHAR));
523 else
524 fodInfos.initdir = NULL;
526 /* save current directory */
527 if (ofn->Flags & OFN_NOCHANGEDIR)
529 lpstrSavDir = MemAlloc(MAX_PATH*sizeof(WCHAR));
530 GetCurrentDirectoryW(MAX_PATH, lpstrSavDir);
533 fodInfos.unicode = TRUE;
535 switch(iDlgType)
537 case OPEN_DIALOG :
538 ret = GetFileName95(&fodInfos);
539 break;
540 case SAVE_DIALOG :
541 fodInfos.DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
542 ret = GetFileName95(&fodInfos);
543 break;
544 default :
545 ret = 0;
548 if (lpstrSavDir)
550 SetCurrentDirectoryW(lpstrSavDir);
551 MemFree(lpstrSavDir);
554 /* restore saved IN arguments and convert OUT arguments back */
555 MemFree(fodInfos.filename);
556 MemFree(fodInfos.initdir);
557 return ret;
560 /******************************************************************************
561 * COMDLG32_GetDisplayNameOf [internal]
563 * Helper function to get the display name for a pidl.
565 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
566 LPSHELLFOLDER psfDesktop;
567 STRRET strret;
569 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
570 return FALSE;
572 if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
573 IShellFolder_Release(psfDesktop);
574 return FALSE;
577 IShellFolder_Release(psfDesktop);
578 return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
581 /******************************************************************************
582 * COMDLG32_GetCanonicalPath [internal]
584 * Helper function to get the canonical path.
586 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
587 LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
589 WCHAR lpstrTemp[MAX_PATH];
591 /* Get the current directory name */
592 if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
594 /* last fallback */
595 GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
597 PathAddBackslashW(lpstrPathAndFile);
599 TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile));
601 /* if the user specified a fully qualified path use it */
602 if(PathIsRelativeW(lpstrFile))
604 lstrcatW(lpstrPathAndFile, lpstrFile);
606 else
608 /* does the path have a drive letter? */
609 if (PathGetDriveNumberW(lpstrFile) == -1)
610 lstrcpyW(lpstrPathAndFile+2, lpstrFile);
611 else
612 lstrcpyW(lpstrPathAndFile, lpstrFile);
615 /* resolve "." and ".." */
616 PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
617 lstrcpyW(lpstrPathAndFile, lpstrTemp);
618 TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
621 /***********************************************************************
622 * COMDLG32_SplitFileNames [internal]
624 * Creates a delimited list of filenames.
626 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
628 UINT nStrCharCount = 0; /* index in src buffer */
629 UINT nFileIndex = 0; /* index in dest buffer */
630 UINT nFileCount = 0; /* number of files */
632 /* we might get single filename without any '"',
633 * so we need nStrLen + terminating \0 + end-of-list \0 */
634 *lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
635 *sizeUsed = 0;
637 /* build delimited file list from filenames */
638 while ( nStrCharCount <= nStrLen )
640 if ( lpstrEdit[nStrCharCount]=='"' )
642 nStrCharCount++;
643 while ((lpstrEdit[nStrCharCount]!='"') && (nStrCharCount <= nStrLen))
645 (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
646 nStrCharCount++;
648 (*lpstrFileList)[nFileIndex++] = 0;
649 nFileCount++;
651 nStrCharCount++;
654 /* single, unquoted string */
655 if ((nStrLen > 0) && (nFileIndex == 0) )
657 lstrcpyW(*lpstrFileList, lpstrEdit);
658 nFileIndex = lstrlenW(lpstrEdit) + 1;
659 nFileCount = 1;
662 /* trailing \0 */
663 (*lpstrFileList)[nFileIndex++] = '\0';
665 *sizeUsed = nFileIndex;
666 return nFileCount;
669 /***********************************************************************
670 * ArrangeCtrlPositions [internal]
672 * NOTE: Make sure to add testcases for any changes made here.
674 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
676 HWND hwndChild, hwndStc32;
677 RECT rectParent, rectChild, rectStc32;
678 INT help_fixup = 0;
679 int chgx, chgy;
681 /* Take into account if open as read only checkbox and help button
682 * are hidden
684 if (hide_help)
686 RECT rectHelp, rectCancel;
687 GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
688 GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
689 /* subtract the height of the help button plus the space between
690 * the help button and the cancel button to the height of the dialog
692 help_fixup = rectHelp.bottom - rectCancel.bottom;
696 There are two possibilities to add components to the default file dialog box.
698 By default, all the new components are added below the standard dialog box (the else case).
700 However, if there is a static text component with the stc32 id, a special case happens.
701 The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
702 in the window and the cx and cy indicate how to size the window.
703 Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
704 of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
708 GetClientRect(hwndParentDlg, &rectParent);
710 /* when arranging controls we have to use fixed parent size */
711 rectParent.bottom -= help_fixup;
713 hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
714 if (hwndStc32)
716 GetWindowRect(hwndStc32, &rectStc32);
717 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
719 /* set the size of the stc32 control according to the size of
720 * client area of the parent dialog
722 SetWindowPos(hwndStc32, 0,
723 0, 0,
724 rectParent.right, rectParent.bottom,
725 SWP_NOMOVE | SWP_NOZORDER);
727 else
728 SetRectEmpty(&rectStc32);
730 /* this part moves controls of the child dialog */
731 hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
732 while (hwndChild)
734 if (hwndChild != hwndStc32)
736 GetWindowRect(hwndChild, &rectChild);
737 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
739 /* move only if stc32 exist */
740 if (hwndStc32 && rectChild.left > rectStc32.right)
742 /* move to the right of visible controls of the parent dialog */
743 rectChild.left += rectParent.right;
744 rectChild.left -= rectStc32.right;
746 /* move even if stc32 doesn't exist */
747 if (rectChild.top >= rectStc32.bottom)
749 /* move below visible controls of the parent dialog */
750 rectChild.top += rectParent.bottom;
751 rectChild.top -= rectStc32.bottom - rectStc32.top;
754 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
755 0, 0, SWP_NOSIZE | SWP_NOZORDER);
757 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
760 /* this part moves controls of the parent dialog */
761 hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
762 while (hwndChild)
764 if (hwndChild != hwndChildDlg)
766 GetWindowRect(hwndChild, &rectChild);
767 MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
769 /* left,top of stc32 marks the position of controls
770 * from the parent dialog
772 rectChild.left += rectStc32.left;
773 rectChild.top += rectStc32.top;
775 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
776 0, 0, SWP_NOSIZE | SWP_NOZORDER);
778 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
781 /* calculate the size of the resulting dialog */
783 /* here we have to use original parent size */
784 GetClientRect(hwndParentDlg, &rectParent);
785 GetClientRect(hwndChildDlg, &rectChild);
786 TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
787 wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
789 if (hwndStc32)
791 /* width */
792 if (rectParent.right > rectStc32.right - rectStc32.left)
793 chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
794 else
795 chgx = rectChild.right - rectParent.right;
796 /* height */
797 if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
798 chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
799 else
800 /* Unconditionally set new dialog
801 * height to that of the child
803 chgy = rectChild.bottom - rectParent.bottom;
805 else
807 chgx = 0;
808 chgy = rectChild.bottom - help_fixup;
810 /* set the size of the parent dialog */
811 GetWindowRect(hwndParentDlg, &rectParent);
812 SetWindowPos(hwndParentDlg, 0,
813 0, 0,
814 rectParent.right - rectParent.left + chgx,
815 rectParent.bottom - rectParent.top + chgy,
816 SWP_NOMOVE | SWP_NOZORDER);
819 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
821 switch(uMsg) {
822 case WM_INITDIALOG:
823 return TRUE;
825 return FALSE;
828 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
830 LPCVOID template;
831 HRSRC hRes;
832 HANDLE hDlgTmpl = 0;
833 HWND hChildDlg = 0;
835 TRACE("\n");
838 * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
839 * structure's hInstance parameter is not a HINSTANCE, but
840 * instead a pointer to a template resource to use.
842 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
844 HINSTANCE hinst;
845 if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
847 hinst = COMDLG32_hInstance;
848 if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
850 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
851 return NULL;
854 else
856 hinst = fodInfos->ofnInfos->hInstance;
857 if(fodInfos->unicode)
859 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
860 hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
862 else
864 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
865 hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
867 if (!hRes)
869 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
870 return NULL;
872 if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
873 !(template = LockResource( hDlgTmpl )))
875 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
876 return NULL;
879 if (fodInfos->unicode)
880 hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
881 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
882 (LPARAM)fodInfos->ofnInfos);
883 else
884 hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
885 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
886 (LPARAM)fodInfos->ofnInfos);
887 return hChildDlg;
889 else if( IsHooked(fodInfos))
891 RECT rectHwnd;
892 struct {
893 DLGTEMPLATE tmplate;
894 WORD menu,class,title;
895 } temp;
896 GetClientRect(hwnd,&rectHwnd);
897 temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
898 temp.tmplate.dwExtendedStyle = 0;
899 temp.tmplate.cdit = 0;
900 temp.tmplate.x = 0;
901 temp.tmplate.y = 0;
902 temp.tmplate.cx = 0;
903 temp.tmplate.cy = 0;
904 temp.menu = temp.class = temp.title = 0;
906 hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
907 hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
909 return hChildDlg;
911 return NULL;
914 /***********************************************************************
915 * SendCustomDlgNotificationMessage
917 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
920 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
922 LRESULT hook_result = 0;
923 FileOpenDlgInfos *fodInfos = GetPropA(hwndParentDlg,FileOpenDlgInfosStr);
925 TRACE("%p 0x%04x\n",hwndParentDlg, uCode);
927 if(!fodInfos) return 0;
929 if(fodInfos->DlgInfos.hwndCustomDlg)
931 TRACE("CALL NOTIFY for %x\n", uCode);
932 if(fodInfos->unicode)
934 OFNOTIFYW ofnNotify;
935 ofnNotify.hdr.hwndFrom=hwndParentDlg;
936 ofnNotify.hdr.idFrom=0;
937 ofnNotify.hdr.code = uCode;
938 ofnNotify.lpOFN = fodInfos->ofnInfos;
939 ofnNotify.pszFile = NULL;
940 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
942 else
944 OFNOTIFYA ofnNotify;
945 ofnNotify.hdr.hwndFrom=hwndParentDlg;
946 ofnNotify.hdr.idFrom=0;
947 ofnNotify.hdr.code = uCode;
948 ofnNotify.lpOFN = (LPOPENFILENAMEA)fodInfos->ofnInfos;
949 ofnNotify.pszFile = NULL;
950 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
952 TRACE("RET NOTIFY\n");
954 TRACE("Retval: 0x%08lx\n", hook_result);
955 return hook_result;
958 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
960 UINT len, total;
961 WCHAR *p, *buffer;
962 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
964 TRACE("CDM_GETFILEPATH:\n");
966 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
967 return -1;
969 /* get path and filenames */
970 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
971 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
972 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
973 if (len)
975 p = buffer + strlenW(buffer);
976 *p++ = '\\';
977 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
979 if (fodInfos->unicode)
981 total = strlenW( buffer) + 1;
982 if (result) lstrcpynW( result, buffer, size );
983 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
985 else
987 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
988 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
989 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
991 HeapFree( GetProcessHeap(), 0, buffer );
992 return total;
995 /***********************************************************************
996 * FILEDLG95_HandleCustomDialogMessages
998 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
1000 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1002 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1003 WCHAR lpstrPath[MAX_PATH];
1004 INT_PTR retval;
1006 if(!fodInfos) return FALSE;
1008 switch(uMsg)
1010 case CDM_GETFILEPATH:
1011 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
1012 break;
1014 case CDM_GETFOLDERPATH:
1015 TRACE("CDM_GETFOLDERPATH:\n");
1016 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
1017 if (lParam)
1019 if (fodInfos->unicode)
1020 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
1021 else
1022 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
1023 (LPSTR)lParam, (int)wParam, NULL, NULL);
1025 retval = lstrlenW(lpstrPath) + 1;
1026 break;
1028 case CDM_GETFOLDERIDLIST:
1029 retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
1030 if (retval <= wParam)
1031 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
1032 break;
1034 case CDM_GETSPEC:
1035 TRACE("CDM_GETSPEC:\n");
1036 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
1037 if (lParam)
1039 if (fodInfos->unicode)
1040 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1041 else
1042 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1044 break;
1046 case CDM_SETCONTROLTEXT:
1047 TRACE("CDM_SETCONTROLTEXT:\n");
1048 if ( lParam )
1050 if( fodInfos->unicode )
1051 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
1052 else
1053 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
1055 retval = TRUE;
1056 break;
1058 case CDM_HIDECONTROL:
1059 /* MSDN states that it should fail for not OFN_EXPLORER case */
1060 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1062 HWND control = GetDlgItem( hwnd, wParam );
1063 if (control) ShowWindow( control, SW_HIDE );
1064 retval = TRUE;
1066 else retval = FALSE;
1067 break;
1069 default:
1070 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1071 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
1072 return FALSE;
1074 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
1075 return TRUE;
1078 /***********************************************************************
1079 * FILEDLG95_OnWMGetMMI
1081 * WM_GETMINMAXINFO message handler for resizable dialogs
1083 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
1085 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1086 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1087 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1089 mmiptr->ptMinTrackSize = fodInfos->initial_size;
1091 return TRUE;
1094 /***********************************************************************
1095 * FILEDLG95_OnWMSize
1097 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1099 * FIXME: this could be made more elaborate. Now use a simple scheme
1100 * where the file view is enlarged and the controls are either moved
1101 * vertically or horizontally to get out of the way. Only the "grip"
1102 * is moved in both directions to stay in the corner.
1104 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1106 RECT rc, rcview;
1107 int chgx, chgy;
1108 HWND ctrl;
1109 HDWP hdwp;
1110 FileOpenDlgInfos *fodInfos;
1112 if( wParam != SIZE_RESTORED) return FALSE;
1113 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1114 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1115 /* get the new dialog rectangle */
1116 GetWindowRect( hwnd, &rc);
1117 TRACE("Size from %d,%d to %d,%d\n", fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1118 rc.right -rc.left, rc.bottom -rc.top);
1119 /* not initialized yet */
1120 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1121 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1122 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1123 return FALSE;
1124 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1125 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1126 fodInfos->sizedlg.cx = rc.right - rc.left;
1127 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1128 /* change the size of the view window */
1129 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1130 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1131 hdwp = BeginDeferWindowPos( 10);
1132 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1133 rcview.right - rcview.left + chgx,
1134 rcview.bottom - rcview.top + chgy,
1135 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1136 /* change position and sizes of the controls */
1137 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1139 int ctrlid = GetDlgCtrlID( ctrl);
1140 GetWindowRect( ctrl, &rc);
1141 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1142 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1144 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1145 0, 0,
1146 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1148 else if( rc.top > rcview.bottom)
1150 /* if it was below the shell view
1151 * move to bottom */
1152 switch( ctrlid)
1154 /* file name box and file types combo change also width */
1155 case edt1:
1156 case cmb1:
1157 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1158 rc.right - rc.left + chgx, rc.bottom - rc.top,
1159 SWP_NOACTIVATE | SWP_NOZORDER);
1160 break;
1161 /* then these buttons must move out of the way */
1162 case IDOK:
1163 case IDCANCEL:
1164 case pshHelp:
1165 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1166 0, 0,
1167 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1168 break;
1169 default:
1170 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1171 0, 0,
1172 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1175 else if( rc.left > rcview.right)
1177 /* if it was to the right of the shell view
1178 * move to right */
1179 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1180 0, 0,
1181 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1183 else
1184 /* special cases */
1186 switch( ctrlid)
1188 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1189 case IDC_LOOKIN:
1190 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1191 rc.right - rc.left + chgx, rc.bottom - rc.top,
1192 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1193 break;
1194 case IDC_TOOLBARSTATIC:
1195 case IDC_TOOLBAR:
1196 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1197 0, 0,
1198 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1199 break;
1200 #endif
1201 /* not resized in windows. Since wine uses this invisible control
1202 * to size the browser view it needs to be resized */
1203 case IDC_SHELLSTATIC:
1204 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1205 rc.right - rc.left + chgx,
1206 rc.bottom - rc.top + chgy,
1207 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1208 break;
1212 if(fodInfos->DlgInfos.hwndCustomDlg &&
1213 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1215 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1216 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1218 GetWindowRect( ctrl, &rc);
1219 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1220 if( rc.top > rcview.bottom)
1222 /* if it was below the shell view
1223 * move to bottom */
1224 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1225 rc.right - rc.left, rc.bottom - rc.top,
1226 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1228 else if( rc.left > rcview.right)
1230 /* if it was to the right of the shell view
1231 * move to right */
1232 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1233 rc.right - rc.left, rc.bottom - rc.top,
1234 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1237 /* size the custom dialog at the end: some applications do some
1238 * control re-arranging at this point */
1239 GetClientRect(hwnd, &rc);
1240 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1241 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1243 EndDeferWindowPos( hdwp);
1244 /* should not be needed */
1245 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1246 return TRUE;
1249 /***********************************************************************
1250 * FileOpenDlgProc95
1252 * File open dialog procedure
1254 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1256 #if 0
1257 TRACE("%p 0x%04x\n", hwnd, uMsg);
1258 #endif
1260 switch(uMsg)
1262 case WM_INITDIALOG:
1264 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1265 RECT rc, rcstc;
1266 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1267 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1269 /* Adds the FileOpenDlgInfos in the property list of the dialog
1270 so it will be easily accessible through a GetPropA(...) */
1271 SetPropA(hwnd, FileOpenDlgInfosStr, fodInfos);
1273 FILEDLG95_InitControls(hwnd);
1275 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1277 GetWindowRect( hwnd, &rc);
1278 fodInfos->DlgInfos.hwndGrip =
1279 CreateWindowExA( 0, "SCROLLBAR", NULL,
1280 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1281 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1282 rc.right - gripx, rc.bottom - gripy,
1283 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1286 fodInfos->DlgInfos.hwndCustomDlg =
1287 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1289 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1290 FILEDLG95_FillControls(hwnd, wParam, lParam);
1292 if( fodInfos->DlgInfos.hwndCustomDlg)
1293 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1295 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1296 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1297 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1300 /* if the app has changed the position of the invisible listbox,
1301 * change that of the listview (browser) as well */
1302 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1303 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1304 if( !EqualRect( &rc, &rcstc))
1306 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1307 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1308 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1309 SWP_NOACTIVATE | SWP_NOZORDER);
1312 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1314 GetWindowRect( hwnd, &rc);
1315 fodInfos->sizedlg.cx = rc.right - rc.left;
1316 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1317 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1318 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1319 GetClientRect( hwnd, &rc);
1320 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1321 rc.right - gripx, rc.bottom - gripy,
1322 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1323 /* resize the dialog to the previous invocation */
1324 if( MemDialogSize.cx && MemDialogSize.cy)
1325 SetWindowPos( hwnd, NULL,
1326 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1327 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1330 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1331 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1333 return 0;
1335 case WM_SIZE:
1336 return FILEDLG95_OnWMSize(hwnd, wParam);
1337 case WM_GETMINMAXINFO:
1338 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1339 case WM_COMMAND:
1340 return FILEDLG95_OnWMCommand(hwnd, wParam);
1341 case WM_DRAWITEM:
1343 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1345 case IDC_LOOKIN:
1346 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1347 return TRUE;
1350 return FALSE;
1352 case WM_GETISHELLBROWSER:
1353 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1355 case WM_DESTROY:
1357 FileOpenDlgInfos * fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1358 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1359 MemDialogSize = fodInfos->sizedlg;
1360 RemovePropA(hwnd, FileOpenDlgInfosStr);
1361 return FALSE;
1363 case WM_NOTIFY:
1365 LPNMHDR lpnmh = (LPNMHDR)lParam;
1366 UINT stringId = -1;
1368 /* set up the button tooltips strings */
1369 if(TTN_GETDISPINFOA == lpnmh->code )
1371 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1372 switch(lpnmh->idFrom )
1374 /* Up folder button */
1375 case FCIDM_TB_UPFOLDER:
1376 stringId = IDS_UPFOLDER;
1377 break;
1378 /* New folder button */
1379 case FCIDM_TB_NEWFOLDER:
1380 stringId = IDS_NEWFOLDER;
1381 break;
1382 /* List option button */
1383 case FCIDM_TB_SMALLICON:
1384 stringId = IDS_LISTVIEW;
1385 break;
1386 /* Details option button */
1387 case FCIDM_TB_REPORTVIEW:
1388 stringId = IDS_REPORTVIEW;
1389 break;
1390 /* Desktop button */
1391 case FCIDM_TB_DESKTOP:
1392 stringId = IDS_TODESKTOP;
1393 break;
1394 default:
1395 stringId = 0;
1397 lpdi->hinst = COMDLG32_hInstance;
1398 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1400 return FALSE;
1402 default :
1403 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1404 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1405 return FALSE;
1409 /***********************************************************************
1410 * FILEDLG95_InitControls
1412 * WM_INITDIALOG message handler (before hook notification)
1414 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1416 int win2000plus = 0;
1417 int win98plus = 0;
1418 int handledPath = FALSE;
1419 OSVERSIONINFOW osVi;
1420 static const WCHAR szwSlash[] = { '\\', 0 };
1421 static const WCHAR szwStar[] = { '*',0 };
1423 static const TBBUTTON tbb[] =
1425 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1426 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1427 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1428 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1429 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1430 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1431 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1432 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1433 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1435 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1437 RECT rectTB;
1438 RECT rectlook;
1440 HIMAGELIST toolbarImageList;
1441 SHFILEINFOA shFileInfo;
1442 ITEMIDLIST *desktopPidl;
1444 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1446 TRACE("%p\n", fodInfos);
1448 /* Get windows version emulating */
1449 osVi.dwOSVersionInfoSize = sizeof(osVi);
1450 GetVersionExW(&osVi);
1451 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1452 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1453 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1454 win2000plus = (osVi.dwMajorVersion > 4);
1455 if (win2000plus) win98plus = TRUE;
1457 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1459 /* Get the hwnd of the controls */
1460 fodInfos->DlgInfos.hwndFileName = GetDlgItem(hwnd,IDC_FILENAME);
1461 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1462 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1464 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1465 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1467 /* construct the toolbar */
1468 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1469 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1471 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1472 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1473 rectTB.left = rectlook.right;
1474 rectTB.top = rectlook.top-1;
1476 if (fodInfos->unicode)
1477 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1478 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1479 rectTB.left, rectTB.top,
1480 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1481 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1482 else
1483 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1484 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1485 rectTB.left, rectTB.top,
1486 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1487 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1489 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1491 /* FIXME: use TB_LOADIMAGES when implemented */
1492 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1493 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1494 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1496 /* Retrieve and add desktop icon to the toolbar */
1497 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1498 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1499 SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1500 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1501 ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1503 DestroyIcon(shFileInfo.hIcon);
1504 CoTaskMemFree(desktopPidl);
1506 /* Finish Toolbar Construction */
1507 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1508 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1510 /* Set the window text with the text specified in the OPENFILENAME structure */
1511 if(fodInfos->title)
1513 SetWindowTextW(hwnd,fodInfos->title);
1515 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1517 WCHAR buf[64];
1518 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1519 SetWindowTextW(hwnd, buf);
1522 /* Initialise the file name edit control */
1523 handledPath = FALSE;
1524 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1526 if(fodInfos->filename)
1528 /* 1. If win2000 or higher and filename contains a path, use it
1529 in preference over the lpstrInitialDir */
1530 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1531 WCHAR tmpBuf[MAX_PATH];
1532 WCHAR *nameBit;
1533 DWORD result;
1535 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1536 if (result) {
1538 /* nameBit is always shorter than the original filename */
1539 lstrcpyW(fodInfos->filename,nameBit);
1541 *nameBit = 0x00;
1542 MemFree(fodInfos->initdir);
1543 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1544 lstrcpyW(fodInfos->initdir, tmpBuf);
1545 handledPath = TRUE;
1546 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1547 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1549 SetDlgItemTextW(hwnd, IDC_FILENAME, fodInfos->filename);
1551 } else {
1552 SetDlgItemTextW(hwnd, IDC_FILENAME, fodInfos->filename);
1556 /* 2. (All platforms) If initdir is not null, then use it */
1557 if ((handledPath == FALSE) && (fodInfos->initdir!=NULL) &&
1558 (*fodInfos->initdir!=0x00))
1560 /* Work out the proper path as supplied one might be relative */
1561 /* (Here because supplying '.' as dir browses to My Computer) */
1562 if (handledPath==FALSE) {
1563 WCHAR tmpBuf[MAX_PATH];
1564 WCHAR tmpBuf2[MAX_PATH];
1565 WCHAR *nameBit;
1566 DWORD result;
1568 lstrcpyW(tmpBuf, fodInfos->initdir);
1569 if( PathFileExistsW(tmpBuf) ) {
1570 /* initdir does not have to be a directory. If a file is
1571 * specified, the dir part is taken */
1572 if( PathIsDirectoryW(tmpBuf)) {
1573 if (tmpBuf[lstrlenW(tmpBuf)-1] != '\\') {
1574 lstrcatW(tmpBuf, szwSlash);
1576 lstrcatW(tmpBuf, szwStar);
1578 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1579 if (result) {
1580 *nameBit = 0x00;
1581 MemFree(fodInfos->initdir);
1582 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1)*sizeof(WCHAR));
1583 lstrcpyW(fodInfos->initdir, tmpBuf2);
1584 handledPath = TRUE;
1585 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1588 else if (fodInfos->initdir)
1590 MemFree(fodInfos->initdir);
1591 fodInfos->initdir = NULL;
1592 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1597 if ((handledPath == FALSE) && ((fodInfos->initdir==NULL) ||
1598 (*fodInfos->initdir==0x00)))
1600 /* 3. All except w2k+: if filename contains a path use it */
1601 if (!win2000plus && fodInfos->filename &&
1602 *fodInfos->filename &&
1603 strpbrkW(fodInfos->filename, szwSlash)) {
1604 WCHAR tmpBuf[MAX_PATH];
1605 WCHAR *nameBit;
1606 DWORD result;
1608 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1609 tmpBuf, &nameBit);
1610 if (result) {
1611 int len;
1613 /* nameBit is always shorter than the original filename */
1614 lstrcpyW(fodInfos->filename, nameBit);
1615 *nameBit = 0x00;
1617 len = lstrlenW(tmpBuf);
1618 MemFree(fodInfos->initdir);
1619 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1620 lstrcpyW(fodInfos->initdir, tmpBuf);
1622 handledPath = TRUE;
1623 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1624 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1626 SetDlgItemTextW(hwnd, IDC_FILENAME, fodInfos->filename);
1629 /* 4. Win2000+: Recently used */
1630 if (handledPath == FALSE && win2000plus) {
1631 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1632 fodInfos->initdir[0] = '\0';
1634 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1636 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1637 handledPath = TRUE;
1638 }else{
1639 MemFree(fodInfos->initdir);
1640 fodInfos->initdir = NULL;
1644 /* 5. win98+ and win2000+ if any files of specified filter types in
1645 current directory, use it */
1646 if ( win98plus && handledPath == FALSE &&
1647 fodInfos->filter && *fodInfos->filter) {
1649 LPCWSTR lpstrPos = fodInfos->filter;
1650 WIN32_FIND_DATAW FindFileData;
1651 HANDLE hFind;
1653 while (1)
1655 /* filter is a list... title\0ext\0......\0\0 */
1657 /* Skip the title */
1658 if(! *lpstrPos) break; /* end */
1659 lpstrPos += lstrlenW(lpstrPos) + 1;
1661 /* See if any files exist in the current dir with this extension */
1662 if(! *lpstrPos) break; /* end */
1664 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1666 if (hFind == INVALID_HANDLE_VALUE) {
1667 /* None found - continue search */
1668 lpstrPos += lstrlenW(lpstrPos) + 1;
1670 } else {
1672 MemFree(fodInfos->initdir);
1673 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1674 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1676 handledPath = TRUE;
1677 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1678 debugstr_w(lpstrPos));
1679 FindClose(hFind);
1680 break;
1685 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1686 if (handledPath == FALSE && (win2000plus || win98plus)) {
1687 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1689 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1691 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1693 /* last fallback */
1694 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1695 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1696 } else {
1697 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1699 } else {
1700 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1702 handledPath = TRUE;
1703 } else if (handledPath==FALSE) {
1704 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1705 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1706 handledPath = TRUE;
1707 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1710 SetFocus(GetDlgItem(hwnd, IDC_FILENAME));
1711 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1713 /* Must the open as read only check box be checked ?*/
1714 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1716 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1719 /* Must the open as read only check box be hidden? */
1720 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1722 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1723 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1726 /* Must the help button be hidden? */
1727 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1729 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1730 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1733 /* change Open to Save */
1734 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1736 WCHAR buf[16];
1737 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1738 SetDlgItemTextW(hwnd, IDOK, buf);
1739 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1740 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1743 /* Initialize the filter combo box */
1744 FILEDLG95_FILETYPE_Init(hwnd);
1746 return 0;
1749 /***********************************************************************
1750 * FILEDLG95_ResizeControls
1752 * WM_INITDIALOG message handler (after hook notification)
1754 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1756 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1758 if (fodInfos->DlgInfos.hwndCustomDlg)
1760 RECT rc;
1761 UINT flags = SWP_NOACTIVATE;
1763 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1764 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1766 /* resize the custom dialog to the parent size */
1767 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1768 GetClientRect(hwnd, &rc);
1769 else
1771 /* our own fake template is zero sized and doesn't have children, so
1772 * there is no need to resize it. Picasa depends on it.
1774 flags |= SWP_NOSIZE;
1775 SetRectEmpty(&rc);
1777 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1778 0, 0, rc.right, rc.bottom, flags);
1780 else
1782 /* Resize the height, if open as read only checkbox ad help button are
1783 * hidden and we are not using a custom template nor a customDialog
1785 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1786 (!(fodInfos->ofnInfos->Flags &
1787 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1789 RECT rectDlg, rectHelp, rectCancel;
1790 GetWindowRect(hwnd, &rectDlg);
1791 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1792 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1793 /* subtract the height of the help button plus the space between the help
1794 * button and the cancel button to the height of the dialog
1796 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1797 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1798 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1801 return TRUE;
1804 /***********************************************************************
1805 * FILEDLG95_FillControls
1807 * WM_INITDIALOG message handler (after hook notification)
1809 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1811 LPITEMIDLIST pidlItemId = NULL;
1813 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1815 TRACE("dir=%s file=%s\n",
1816 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1818 /* Get the initial directory pidl */
1820 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1822 WCHAR path[MAX_PATH];
1824 GetCurrentDirectoryW(MAX_PATH,path);
1825 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1828 /* Initialise shell objects */
1829 FILEDLG95_SHELL_Init(hwnd);
1831 /* Initialize the Look In combo box */
1832 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1834 /* Browse to the initial directory */
1835 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1837 /* Free pidlItem memory */
1838 COMDLG32_SHFree(pidlItemId);
1840 return TRUE;
1842 /***********************************************************************
1843 * FILEDLG95_Clean
1845 * Regroups all the cleaning functions of the filedlg
1847 void FILEDLG95_Clean(HWND hwnd)
1849 FILEDLG95_FILETYPE_Clean(hwnd);
1850 FILEDLG95_LOOKIN_Clean(hwnd);
1851 FILEDLG95_SHELL_Clean(hwnd);
1853 /***********************************************************************
1854 * FILEDLG95_OnWMCommand
1856 * WM_COMMAND message handler
1858 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1860 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1861 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1862 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1864 switch(wID)
1866 /* OK button */
1867 case IDOK:
1868 FILEDLG95_OnOpen(hwnd);
1869 break;
1870 /* Cancel button */
1871 case IDCANCEL:
1872 FILEDLG95_Clean(hwnd);
1873 EndDialog(hwnd, FALSE);
1874 break;
1875 /* Filetype combo box */
1876 case IDC_FILETYPE:
1877 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1878 break;
1879 /* LookIn combo box */
1880 case IDC_LOOKIN:
1881 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1882 break;
1884 /* --- toolbar --- */
1885 /* Up folder button */
1886 case FCIDM_TB_UPFOLDER:
1887 FILEDLG95_SHELL_UpFolder(hwnd);
1888 break;
1889 /* New folder button */
1890 case FCIDM_TB_NEWFOLDER:
1891 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1892 break;
1893 /* List option button */
1894 case FCIDM_TB_SMALLICON:
1895 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1896 break;
1897 /* Details option button */
1898 case FCIDM_TB_REPORTVIEW:
1899 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1900 break;
1901 /* Details option button */
1902 case FCIDM_TB_DESKTOP:
1903 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1904 break;
1906 case IDC_FILENAME:
1907 break;
1910 /* Do not use the listview selection anymore */
1911 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1912 return 0;
1915 /***********************************************************************
1916 * FILEDLG95_OnWMGetIShellBrowser
1918 * WM_GETISHELLBROWSER message handler
1920 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1922 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1924 TRACE("\n");
1926 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1928 return TRUE;
1932 /***********************************************************************
1933 * FILEDLG95_SendFileOK
1935 * Sends the CDN_FILEOK notification if required
1937 * RETURNS
1938 * TRUE if the dialog should close
1939 * FALSE if the dialog should not be closed
1941 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1943 /* ask the hook if we can close */
1944 if(IsHooked(fodInfos))
1946 LRESULT retval = 0;
1948 TRACE("---\n");
1949 /* First send CDN_FILEOK as MSDN doc says */
1950 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1951 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1952 if( retval)
1954 TRACE("canceled\n");
1955 return FALSE;
1958 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1959 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1960 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1961 if( retval)
1963 TRACE("canceled\n");
1964 return FALSE;
1967 return TRUE;
1970 /***********************************************************************
1971 * FILEDLG95_OnOpenMultipleFiles
1973 * Handles the opening of multiple files.
1975 * FIXME
1976 * check destination buffer size
1978 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
1980 WCHAR lpstrPathSpec[MAX_PATH] = {0};
1981 UINT nCount, nSizePath;
1982 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1984 TRACE("\n");
1986 if(fodInfos->unicode)
1988 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1989 ofn->lpstrFile[0] = '\0';
1991 else
1993 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
1994 ofn->lpstrFile[0] = '\0';
1997 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
1999 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2000 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
2001 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
2003 LPWSTR lpstrTemp = lpstrFileList;
2005 for ( nCount = 0; nCount < nFileCount; nCount++ )
2007 LPITEMIDLIST pidl;
2009 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
2010 if (!pidl)
2012 WCHAR lpstrNotFound[100];
2013 WCHAR lpstrMsg[100];
2014 WCHAR tmp[400];
2015 static const WCHAR nl[] = {'\n',0};
2017 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
2018 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
2020 lstrcpyW(tmp, lpstrTemp);
2021 lstrcatW(tmp, nl);
2022 lstrcatW(tmp, lpstrNotFound);
2023 lstrcatW(tmp, nl);
2024 lstrcatW(tmp, lpstrMsg);
2026 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
2027 return FALSE;
2030 /* move to the next file in the list of files */
2031 lpstrTemp += lstrlenW(lpstrTemp) + 1;
2032 COMDLG32_SHFree(pidl);
2036 nSizePath = lstrlenW(lpstrPathSpec) + 1;
2037 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2039 /* For "oldstyle" dialog the components have to
2040 be separated by blanks (not '\0'!) and short
2041 filenames have to be used! */
2042 FIXME("Components have to be separated by blanks\n");
2044 if(fodInfos->unicode)
2046 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2047 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2048 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2050 else
2052 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2054 if (ofn->lpstrFile != NULL)
2056 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2057 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2058 if (ofn->nMaxFile > nSizePath)
2060 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2061 ofn->lpstrFile + nSizePath,
2062 ofn->nMaxFile - nSizePath, NULL, NULL);
2067 fodInfos->ofnInfos->nFileOffset = nSizePath;
2068 fodInfos->ofnInfos->nFileExtension = 0;
2070 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2071 return FALSE;
2073 /* clean and exit */
2074 FILEDLG95_Clean(hwnd);
2075 return EndDialog(hwnd,TRUE);
2078 /* Returns the 'slot name' of the given module_name in the registry's
2079 * most-recently-used list. This will be an ASCII value in the
2080 * range ['a','z'). Returns zero on error.
2082 * The slot's value in the registry has the form:
2083 * module_name\0mru_path\0
2085 * If stored_path is given, then stored_path will contain the path name
2086 * stored in the registry's MRU list for the given module_name.
2088 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2089 * MRU list key for the given module_name.
2091 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2093 WCHAR mru_list[32], *cur_mru_slot;
2094 BOOL taken[25] = {0};
2095 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2096 HKEY hkey_tmp, *hkey;
2097 LONG ret;
2099 if(hkey_ret)
2100 hkey = hkey_ret;
2101 else
2102 hkey = &hkey_tmp;
2104 if(stored_path)
2105 *stored_path = '\0';
2107 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2108 if(ret){
2109 WARN("Unable to create MRU key: %d\n", ret);
2110 return 0;
2113 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2114 (LPBYTE)mru_list, &mru_list_size);
2115 if(ret || key_type != REG_SZ){
2116 if(ret == ERROR_FILE_NOT_FOUND)
2117 return 'a';
2119 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2120 RegCloseKey(*hkey);
2121 return 0;
2124 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2125 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2126 DWORD value_data_size = sizeof(value_data);
2128 *value_name = *cur_mru_slot;
2130 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2131 &key_type, (LPBYTE)value_data, &value_data_size);
2132 if(ret || key_type != REG_BINARY){
2133 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2134 continue;
2137 if(!strcmpiW(module_name, value_data)){
2138 if(!hkey_ret)
2139 RegCloseKey(*hkey);
2140 if(stored_path)
2141 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2142 return *value_name;
2146 if(!hkey_ret)
2147 RegCloseKey(*hkey);
2149 /* the module name isn't in the registry, so find the next open slot */
2150 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2151 taken[*cur_mru_slot - 'a'] = TRUE;
2152 for(i = 0; i < 25; ++i){
2153 if(!taken[i])
2154 return i + 'a';
2157 /* all slots are taken, so return the last one in MRUList */
2158 --cur_mru_slot;
2159 return *cur_mru_slot;
2162 /* save the given filename as most-recently-used path for this module */
2163 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2165 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2166 LONG ret;
2167 HKEY hkey;
2169 /* get the current executable's name */
2170 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2171 WARN("GotModuleFileName failed: %d\n", GetLastError());
2172 return;
2174 module_name = strrchrW(module_path, '\\');
2175 if(!module_name)
2176 module_name = module_path;
2177 else
2178 module_name += 1;
2180 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2181 if(!slot)
2182 return;
2183 *slot_name = slot;
2185 { /* update the slot's info */
2186 WCHAR *path_ends, *final;
2187 DWORD path_len, final_len;
2189 /* use only the path segment of `filename' */
2190 path_ends = strrchrW(filename, '\\');
2191 path_len = path_ends - filename;
2193 final_len = path_len + lstrlenW(module_name) + 2;
2195 final = MemAlloc(final_len * sizeof(WCHAR));
2196 if(!final)
2197 return;
2198 lstrcpyW(final, module_name);
2199 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2200 final[final_len-1] = '\0';
2202 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2203 final_len * sizeof(WCHAR));
2204 if(ret){
2205 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2206 MemFree(final);
2207 RegCloseKey(hkey);
2208 return;
2211 MemFree(final);
2214 { /* update MRUList value */
2215 WCHAR old_mru_list[32], new_mru_list[32];
2216 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2217 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2219 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2220 (LPBYTE)old_mru_list, &mru_list_size);
2221 if(ret || key_type != REG_SZ){
2222 if(ret == ERROR_FILE_NOT_FOUND){
2223 new_mru_list[0] = slot;
2224 new_mru_list[1] = '\0';
2225 }else{
2226 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2227 RegCloseKey(hkey);
2228 return;
2230 }else{
2231 /* copy old list data over so that the new slot is at the start
2232 * of the list */
2233 *new_mru_slot++ = slot;
2234 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2235 if(*old_mru_slot != slot)
2236 *new_mru_slot++ = *old_mru_slot;
2238 *new_mru_slot = '\0';
2241 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2242 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2243 if(ret){
2244 WARN("Error saving MRUList data: %d\n", ret);
2245 RegCloseKey(hkey);
2246 return;
2251 /* load the most-recently-used path for this module */
2252 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2254 WCHAR module_path[MAX_PATH], *module_name;
2256 /* get the current executable's name */
2257 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2258 WARN("GotModuleFileName failed: %d\n", GetLastError());
2259 return;
2261 module_name = strrchrW(module_path, '\\');
2262 if(!module_name)
2263 module_name = module_path;
2264 else
2265 module_name += 1;
2267 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2268 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2271 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2273 WCHAR strMsgTitle[MAX_PATH];
2274 WCHAR strMsgText [MAX_PATH];
2275 if (idCaption)
2276 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2277 else
2278 strMsgTitle[0] = '\0';
2279 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2280 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2283 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2284 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2286 int nOpenAction = defAction;
2287 LPWSTR lpszTemp, lpszTemp1;
2288 LPITEMIDLIST pidl = NULL;
2289 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2291 /* check for invalid chars */
2292 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2294 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2295 return FALSE;
2298 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2300 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2301 while (lpszTemp1)
2303 LPSHELLFOLDER lpsfChild;
2304 WCHAR lpwstrTemp[MAX_PATH];
2305 DWORD dwEaten, dwAttributes;
2306 LPWSTR p;
2308 lstrcpyW(lpwstrTemp, lpszTemp);
2309 p = PathFindNextComponentW(lpwstrTemp);
2311 if (!p) break; /* end of path */
2313 *p = 0;
2314 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2316 /* There are no wildcards when OFN_NOVALIDATE is set */
2317 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2319 static const WCHAR wszWild[] = { '*', '?', 0 };
2320 /* if the last element is a wildcard do a search */
2321 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2323 nOpenAction = ONOPEN_SEARCH;
2324 break;
2327 lpszTemp1 = lpszTemp;
2329 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2331 /* append a backslash to drive letters */
2332 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2333 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2334 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2336 PathAddBackslashW(lpwstrTemp);
2339 dwAttributes = SFGAO_FOLDER;
2340 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2342 /* the path component is valid, we have a pidl of the next path component */
2343 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2344 if(dwAttributes & SFGAO_FOLDER)
2346 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2348 ERR("bind to failed\n"); /* should not fail */
2349 break;
2351 IShellFolder_Release(*ppsf);
2352 *ppsf = lpsfChild;
2353 lpsfChild = NULL;
2355 else
2357 TRACE("value\n");
2359 /* end dialog, return value */
2360 nOpenAction = ONOPEN_OPEN;
2361 break;
2363 COMDLG32_SHFree(pidl);
2364 pidl = NULL;
2366 else if (!(flags & OFN_NOVALIDATE))
2368 if(*lpszTemp || /* points to trailing null for last path element */
2369 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2371 if(flags & OFN_PATHMUSTEXIST)
2373 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2374 break;
2377 else
2379 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2381 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2382 break;
2385 /* change to the current folder */
2386 nOpenAction = ONOPEN_OPEN;
2387 break;
2389 else
2391 nOpenAction = ONOPEN_OPEN;
2392 break;
2395 if(pidl) COMDLG32_SHFree(pidl);
2397 return nOpenAction;
2400 /***********************************************************************
2401 * FILEDLG95_OnOpen
2403 * Ok button WM_COMMAND message handler
2405 * If the function succeeds, the return value is nonzero.
2407 BOOL FILEDLG95_OnOpen(HWND hwnd)
2409 LPWSTR lpstrFileList;
2410 UINT nFileCount = 0;
2411 UINT sizeUsed = 0;
2412 BOOL ret = TRUE;
2413 WCHAR lpstrPathAndFile[MAX_PATH];
2414 LPSHELLFOLDER lpsf = NULL;
2415 int nOpenAction;
2416 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2418 TRACE("hwnd=%p\n", hwnd);
2420 /* try to browse the selected item */
2421 if(BrowseSelectedFolder(hwnd))
2422 return FALSE;
2424 /* get the files from the edit control */
2425 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2427 if(nFileCount == 0)
2428 return FALSE;
2430 if(nFileCount > 1)
2432 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2433 goto ret;
2436 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2439 Step 1: Build a complete path name from the current folder and
2440 the filename or path in the edit box.
2441 Special cases:
2442 - the path in the edit box is a root path
2443 (with or without drive letter)
2444 - the edit box contains ".." (or a path with ".." in it)
2447 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2448 MemFree(lpstrFileList);
2451 Step 2: here we have a cleaned up path
2453 We have to parse the path step by step to see if we have to browse
2454 to a folder if the path points to a directory or the last
2455 valid element is a directory.
2457 valid variables:
2458 lpstrPathAndFile: cleaned up path
2461 if (nFileCount &&
2462 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2463 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2464 nOpenAction = ONOPEN_OPEN;
2465 else
2466 nOpenAction = ONOPEN_BROWSE;
2468 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2469 fodInfos->ofnInfos->Flags,
2470 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2471 nOpenAction);
2472 if(!nOpenAction)
2473 goto ret;
2476 Step 3: here we have a cleaned up and validated path
2478 valid variables:
2479 lpsf: ShellFolder bound to the rightmost valid path component
2480 lpstrPathAndFile: cleaned up path
2481 nOpenAction: action to do
2483 TRACE("end validate sf=%p\n", lpsf);
2485 switch(nOpenAction)
2487 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2488 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2490 int iPos;
2491 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2492 DWORD len;
2494 /* replace the current filter */
2495 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2496 len = lstrlenW(lpszTemp)+1;
2497 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2498 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2500 /* set the filter cb to the extension when possible */
2501 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2502 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2504 /* fall through */
2505 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2506 TRACE("ONOPEN_BROWSE\n");
2508 IPersistFolder2 * ppf2;
2509 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2511 LPITEMIDLIST pidlCurrent;
2512 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2513 IPersistFolder2_Release(ppf2);
2514 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2516 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2517 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2519 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2522 else if( nOpenAction == ONOPEN_SEARCH )
2524 if (fodInfos->Shell.FOIShellView)
2525 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2527 COMDLG32_SHFree(pidlCurrent);
2528 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2531 ret = FALSE;
2532 break;
2533 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2534 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2536 WCHAR *ext = NULL;
2538 /* update READONLY check box flag */
2539 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2540 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2541 else
2542 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2544 /* Attach the file extension with file name*/
2545 ext = PathFindExtensionW(lpstrPathAndFile);
2546 if (! *ext && fodInfos->defext)
2548 /* if no extension is specified with file name, then */
2549 /* attach the extension from file filter or default one */
2551 WCHAR *filterExt = NULL;
2552 LPWSTR lpstrFilter = NULL;
2553 static const WCHAR szwDot[] = {'.',0};
2554 int PathLength = lstrlenW(lpstrPathAndFile);
2556 /*Get the file extension from file type filter*/
2557 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2558 fodInfos->ofnInfos->nFilterIndex-1);
2560 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2562 WCHAR* filterSearchIndex;
2563 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2564 strcpyW(filterExt, lpstrFilter);
2566 /* if a semicolon-separated list of file extensions was given, do not include the
2567 semicolon or anything after it in the extension.
2568 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2569 filterSearchIndex = strchrW(filterExt, ';');
2570 if (filterSearchIndex)
2572 filterSearchIndex[0] = '\0';
2575 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2576 /* if the extension is invalid or contains a glob, ignore it */
2577 filterSearchIndex = PathFindExtensionW(filterExt);
2578 if (*filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2580 strcpyW(filterExt, filterSearchIndex);
2582 else
2584 HeapFree(GetProcessHeap(), 0, filterExt);
2585 filterExt = NULL;
2589 if (!filterExt)
2591 /* use the default file extension */
2592 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2593 strcpyW(filterExt, fodInfos->defext);
2596 if (*filterExt) /* ignore filterExt="" */
2598 /* Attach the dot*/
2599 lstrcatW(lpstrPathAndFile, szwDot);
2600 /* Attach the extension */
2601 lstrcatW(lpstrPathAndFile, filterExt);
2604 HeapFree(GetProcessHeap(), 0, filterExt);
2606 /* In Open dialog: if file does not exist try without extension */
2607 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2608 lpstrPathAndFile[PathLength] = '\0';
2610 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2611 if (*ext)
2612 ext++;
2613 if (!lstrcmpiW(fodInfos->defext, ext))
2614 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2615 else
2616 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2619 /* In Save dialog: check if the file already exists */
2620 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2621 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2622 && PathFileExistsW(lpstrPathAndFile))
2624 WCHAR lpstrOverwrite[100];
2625 int answer;
2627 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2628 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2629 MB_YESNO | MB_ICONEXCLAMATION);
2630 if (answer == IDNO || answer == IDCANCEL)
2632 ret = FALSE;
2633 goto ret;
2637 /* In Open dialog: check if it should be created if it doesn't exist */
2638 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2639 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2640 && !PathFileExistsW(lpstrPathAndFile))
2642 WCHAR lpstrCreate[100];
2643 int answer;
2645 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2646 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2647 MB_YESNO | MB_ICONEXCLAMATION);
2648 if (answer == IDNO || answer == IDCANCEL)
2650 ret = FALSE;
2651 goto ret;
2655 /* Check that the size of the file does not exceed buffer size.
2656 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2657 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2658 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2661 /* fill destination buffer */
2662 if (fodInfos->ofnInfos->lpstrFile)
2664 if(fodInfos->unicode)
2666 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2668 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2669 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2670 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2672 else
2674 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2676 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2677 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2678 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2679 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2683 if(fodInfos->unicode)
2685 LPWSTR lpszTemp;
2687 /* set filename offset */
2688 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2689 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2691 /* set extension offset */
2692 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2693 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2695 else
2697 LPSTR lpszTemp;
2698 CHAR tempFileA[MAX_PATH];
2700 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2701 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2702 tempFileA, sizeof(tempFileA), NULL, NULL);
2704 /* set filename offset */
2705 lpszTemp = PathFindFileNameA(tempFileA);
2706 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2708 /* set extension offset */
2709 lpszTemp = PathFindExtensionA(tempFileA);
2710 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2713 /* set the lpstrFileTitle */
2714 if(fodInfos->ofnInfos->lpstrFileTitle)
2716 LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2717 if(fodInfos->unicode)
2719 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2720 lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2722 else
2724 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2725 WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2726 ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2730 /* copy currently selected filter to lpstrCustomFilter */
2731 if (fodInfos->ofnInfos->lpstrCustomFilter)
2733 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2734 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2735 NULL, 0, NULL, NULL);
2736 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2738 LPSTR s = ofn->lpstrCustomFilter;
2739 s += strlen(ofn->lpstrCustomFilter)+1;
2740 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2741 s, len, NULL, NULL);
2746 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2747 goto ret;
2749 FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2751 TRACE("close\n");
2752 FILEDLG95_Clean(hwnd);
2753 ret = EndDialog(hwnd, TRUE);
2755 else
2757 WORD size;
2759 size = lstrlenW(lpstrPathAndFile) + 1;
2760 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2761 size += 1;
2762 /* return needed size in first two bytes of lpstrFile */
2763 if(fodInfos->ofnInfos->lpstrFile)
2764 *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2765 FILEDLG95_Clean(hwnd);
2766 ret = EndDialog(hwnd, FALSE);
2767 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2770 break;
2773 ret:
2774 if(lpsf) IShellFolder_Release(lpsf);
2775 return ret;
2778 /***********************************************************************
2779 * FILEDLG95_SHELL_Init
2781 * Initialisation of the shell objects
2783 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2785 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2787 TRACE("\n");
2790 * Initialisation of the FileOpenDialogInfos structure
2793 /* Shell */
2795 /*ShellInfos */
2796 fodInfos->ShellInfos.hwndOwner = hwnd;
2798 /* Disable multi-select if flag not set */
2799 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2801 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2803 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2804 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2806 /* Construct the IShellBrowser interface */
2807 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2809 return NOERROR;
2812 /***********************************************************************
2813 * FILEDLG95_SHELL_ExecuteCommand
2815 * Change the folder option and refresh the view
2816 * If the function succeeds, the return value is nonzero.
2818 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2820 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2821 IContextMenu * pcm;
2823 TRACE("(%p,%p)\n", hwnd, lpVerb);
2825 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2826 SVGIO_BACKGROUND,
2827 &IID_IContextMenu,
2828 (LPVOID*)&pcm)))
2830 CMINVOKECOMMANDINFO ci;
2831 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2832 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2833 ci.lpVerb = lpVerb;
2834 ci.hwnd = hwnd;
2836 IContextMenu_InvokeCommand(pcm, &ci);
2837 IContextMenu_Release(pcm);
2840 return FALSE;
2843 /***********************************************************************
2844 * FILEDLG95_SHELL_UpFolder
2846 * Browse to the specified object
2847 * If the function succeeds, the return value is nonzero.
2849 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2851 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2853 TRACE("\n");
2855 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2856 NULL,
2857 SBSP_PARENT)))
2859 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2860 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2861 return TRUE;
2863 return FALSE;
2866 /***********************************************************************
2867 * FILEDLG95_SHELL_BrowseToDesktop
2869 * Browse to the Desktop
2870 * If the function succeeds, the return value is nonzero.
2872 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2874 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2875 LPITEMIDLIST pidl;
2876 HRESULT hres;
2878 TRACE("\n");
2880 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2881 hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2882 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2883 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2884 COMDLG32_SHFree(pidl);
2885 return SUCCEEDED(hres);
2887 /***********************************************************************
2888 * FILEDLG95_SHELL_Clean
2890 * Cleans the memory used by shell objects
2892 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2894 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2896 TRACE("\n");
2898 COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2900 /* clean Shell interfaces */
2901 if (fodInfos->Shell.FOIShellView)
2903 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2904 IShellView_Release(fodInfos->Shell.FOIShellView);
2906 IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2907 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2908 if (fodInfos->Shell.FOIDataObject)
2909 IDataObject_Release(fodInfos->Shell.FOIDataObject);
2912 /***********************************************************************
2913 * FILEDLG95_FILETYPE_Init
2915 * Initialisation of the file type combo box
2917 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2919 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2920 int nFilters = 0; /* number of filters */
2921 int nFilterIndexCB;
2923 TRACE("\n");
2925 if(fodInfos->customfilter)
2927 /* customfilter has one entry... title\0ext\0
2928 * Set first entry of combo box item with customfilter
2930 LPWSTR lpstrExt;
2931 LPCWSTR lpstrPos = fodInfos->customfilter;
2933 /* Get the title */
2934 lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2936 /* Copy the extensions */
2937 if (! *lpstrPos) return E_FAIL; /* malformed filter */
2938 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2939 lstrcpyW(lpstrExt,lpstrPos);
2941 /* Add the item at the end of the combo */
2942 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2943 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2944 nFilters++;
2946 if(fodInfos->filter)
2948 LPCWSTR lpstrPos = fodInfos->filter;
2950 for(;;)
2952 /* filter is a list... title\0ext\0......\0\0
2953 * Set the combo item text to the title and the item data
2954 * to the ext
2956 LPCWSTR lpstrDisplay;
2957 LPWSTR lpstrExt;
2959 /* Get the title */
2960 if(! *lpstrPos) break; /* end */
2961 lpstrDisplay = lpstrPos;
2962 lpstrPos += lstrlenW(lpstrPos) + 1;
2964 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2966 nFilters++;
2968 /* Copy the extensions */
2969 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2970 lstrcpyW(lpstrExt,lpstrPos);
2971 lpstrPos += lstrlenW(lpstrPos) + 1;
2973 /* Add the item at the end of the combo */
2974 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
2976 /* malformed filters are added anyway... */
2977 if (!*lpstrExt) break;
2982 * Set the current filter to the one specified
2983 * in the initialisation structure
2985 if (fodInfos->filter || fodInfos->customfilter)
2987 LPWSTR lpstrFilter;
2989 /* Check to make sure our index isn't out of bounds. */
2990 if ( fodInfos->ofnInfos->nFilterIndex >
2991 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
2992 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
2994 /* set default filter index */
2995 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
2996 fodInfos->ofnInfos->nFilterIndex = 1;
2998 /* calculate index of Combo Box item */
2999 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
3000 if (fodInfos->customfilter == NULL)
3001 nFilterIndexCB--;
3003 /* Set the current index selection. */
3004 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
3006 /* Get the corresponding text string from the combo box. */
3007 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3008 nFilterIndexCB);
3010 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
3011 lpstrFilter = NULL;
3013 if(lpstrFilter)
3015 DWORD len;
3016 CharLowerW(lpstrFilter); /* lowercase */
3017 len = lstrlenW(lpstrFilter)+1;
3018 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3019 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3021 } else
3022 fodInfos->ofnInfos->nFilterIndex = 0;
3023 return S_OK;
3026 /***********************************************************************
3027 * FILEDLG95_FILETYPE_OnCommand
3029 * WM_COMMAND of the file type combo box
3030 * If the function succeeds, the return value is nonzero.
3032 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
3034 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3036 switch(wNotifyCode)
3038 case CBN_SELENDOK:
3040 LPWSTR lpstrFilter;
3042 /* Get the current item of the filetype combo box */
3043 int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
3045 /* set the current filter index */
3046 fodInfos->ofnInfos->nFilterIndex = iItem +
3047 (fodInfos->customfilter == NULL ? 1 : 0);
3049 /* Set the current filter with the current selection */
3050 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3052 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3053 iItem);
3054 if((INT_PTR)lpstrFilter != CB_ERR)
3056 DWORD len;
3057 CharLowerW(lpstrFilter); /* lowercase */
3058 len = lstrlenW(lpstrFilter)+1;
3059 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3060 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3061 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3062 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3065 /* Refresh the actual view to display the included items*/
3066 if (fodInfos->Shell.FOIShellView)
3067 IShellView_Refresh(fodInfos->Shell.FOIShellView);
3070 return FALSE;
3072 /***********************************************************************
3073 * FILEDLG95_FILETYPE_SearchExt
3075 * searches for an extension in the filetype box
3077 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3079 int i, iCount = CBGetCount(hwnd);
3081 TRACE("%s\n", debugstr_w(lpstrExt));
3083 if(iCount != CB_ERR)
3085 for(i=0;i<iCount;i++)
3087 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3088 return i;
3091 return -1;
3094 /***********************************************************************
3095 * FILEDLG95_FILETYPE_Clean
3097 * Clean the memory used by the filetype combo box
3099 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3101 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3102 int iPos;
3103 int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3105 TRACE("\n");
3107 /* Delete each string of the combo and their associated data */
3108 if(iCount != CB_ERR)
3110 for(iPos = iCount-1;iPos>=0;iPos--)
3112 MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3113 CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3116 /* Current filter */
3117 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3121 /***********************************************************************
3122 * FILEDLG95_LOOKIN_Init
3124 * Initialisation of the look in combo box
3127 /* Small helper function, to determine if the unixfs shell extension is rooted
3128 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3130 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3131 HKEY hKey;
3132 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3133 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3134 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3135 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3136 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3137 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3138 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3140 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3141 return FALSE;
3143 RegCloseKey(hKey);
3144 return TRUE;
3147 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3149 IShellFolder *psfRoot, *psfDrives;
3150 IEnumIDList *lpeRoot, *lpeDrives;
3151 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3153 LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3155 TRACE("\n");
3157 liInfos->iMaxIndentation = 0;
3159 SetPropA(hwndCombo, LookInInfosStr, liInfos);
3161 /* set item height for both text field and listbox */
3162 CBSetItemHeight(hwndCombo,-1,GetSystemMetrics(SM_CYSMICON));
3163 CBSetItemHeight(hwndCombo,0,GetSystemMetrics(SM_CYSMICON));
3165 /* Turn on the extended UI for the combo box like Windows does */
3166 CBSetExtendedUI(hwndCombo, TRUE);
3168 /* Initialise data of Desktop folder */
3169 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3170 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3171 COMDLG32_SHFree(pidlTmp);
3173 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3175 SHGetDesktopFolder(&psfRoot);
3177 if (psfRoot)
3179 /* enumerate the contents of the desktop */
3180 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3182 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3184 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3186 /* If the unixfs extension is rooted, we don't expand the drives by default */
3187 if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3189 /* special handling for CSIDL_DRIVES */
3190 if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3192 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3194 /* enumerate the drives */
3195 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3197 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3199 pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3200 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3201 COMDLG32_SHFree(pidlAbsTmp);
3202 COMDLG32_SHFree(pidlTmp1);
3204 IEnumIDList_Release(lpeDrives);
3206 IShellFolder_Release(psfDrives);
3211 COMDLG32_SHFree(pidlTmp);
3213 IEnumIDList_Release(lpeRoot);
3215 IShellFolder_Release(psfRoot);
3218 COMDLG32_SHFree(pidlDrives);
3221 /***********************************************************************
3222 * FILEDLG95_LOOKIN_DrawItem
3224 * WM_DRAWITEM message handler
3226 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3228 COLORREF crWin = GetSysColor(COLOR_WINDOW);
3229 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3230 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3231 RECT rectText;
3232 RECT rectIcon;
3233 SHFILEINFOW sfi;
3234 HIMAGELIST ilItemImage;
3235 int iIndentation;
3236 TEXTMETRICW tm;
3237 LPSFOLDER tmpFolder;
3238 LookInInfos *liInfos = GetPropA(pDIStruct->hwndItem,LookInInfosStr);
3240 TRACE("\n");
3242 if(pDIStruct->itemID == -1)
3243 return 0;
3245 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3246 pDIStruct->itemID)))
3247 return 0;
3250 if(pDIStruct->itemID == liInfos->uSelectedItem)
3252 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3254 &sfi,
3255 sizeof (sfi),
3256 SHGFI_PIDL | SHGFI_SMALLICON |
3257 SHGFI_OPENICON | SHGFI_SYSICONINDEX |
3258 SHGFI_DISPLAYNAME );
3260 else
3262 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3264 &sfi,
3265 sizeof (sfi),
3266 SHGFI_PIDL | SHGFI_SMALLICON |
3267 SHGFI_SYSICONINDEX |
3268 SHGFI_DISPLAYNAME);
3271 /* Is this item selected ? */
3272 if(pDIStruct->itemState & ODS_SELECTED)
3274 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3275 SetBkColor(pDIStruct->hDC,crHighLight);
3276 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3278 else
3280 SetTextColor(pDIStruct->hDC,crText);
3281 SetBkColor(pDIStruct->hDC,crWin);
3282 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3285 /* Do not indent item if drawing in the edit of the combo */
3286 if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3288 iIndentation = 0;
3289 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3291 &sfi,
3292 sizeof (sfi),
3293 SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_OPENICON
3294 | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME );
3297 else
3299 iIndentation = tmpFolder->m_iIndent;
3301 /* Draw text and icon */
3303 /* Initialise the icon display area */
3304 rectIcon.left = pDIStruct->rcItem.left + ICONWIDTH/2 * iIndentation;
3305 rectIcon.top = pDIStruct->rcItem.top;
3306 rectIcon.right = rectIcon.left + ICONWIDTH;
3307 rectIcon.bottom = pDIStruct->rcItem.bottom;
3309 /* Initialise the text display area */
3310 GetTextMetricsW(pDIStruct->hDC, &tm);
3311 rectText.left = rectIcon.right;
3312 rectText.top =
3313 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3314 rectText.right = pDIStruct->rcItem.right + XTEXTOFFSET;
3315 rectText.bottom =
3316 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3318 /* Draw the icon from the image list */
3319 ImageList_Draw(ilItemImage,
3320 sfi.iIcon,
3321 pDIStruct->hDC,
3322 rectIcon.left,
3323 rectIcon.top,
3324 ILD_TRANSPARENT );
3326 /* Draw the associated text */
3327 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3328 return NOERROR;
3331 /***********************************************************************
3332 * FILEDLG95_LOOKIN_OnCommand
3334 * LookIn combo box WM_COMMAND message handler
3335 * If the function succeeds, the return value is nonzero.
3337 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3339 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3341 TRACE("%p\n", fodInfos);
3343 switch(wNotifyCode)
3345 case CBN_SELENDOK:
3347 LPSFOLDER tmpFolder;
3348 int iItem;
3350 iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3352 if( iItem == CB_ERR) return FALSE;
3354 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3355 iItem)))
3356 return FALSE;
3359 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3360 tmpFolder->pidlItem,
3361 SBSP_ABSOLUTE)))
3363 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3364 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3365 return TRUE;
3367 break;
3371 return FALSE;
3374 /***********************************************************************
3375 * FILEDLG95_LOOKIN_AddItem
3377 * Adds an absolute pidl item to the lookin combo box
3378 * returns the index of the inserted item
3380 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3382 LPITEMIDLIST pidlNext;
3383 SHFILEINFOW sfi;
3384 SFOLDER *tmpFolder;
3385 LookInInfos *liInfos;
3387 TRACE("%08x\n", iInsertId);
3389 if(!pidl)
3390 return -1;
3392 if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3393 return -1;
3395 tmpFolder = MemAlloc(sizeof(SFOLDER));
3396 tmpFolder->m_iIndent = 0;
3398 /* Calculate the indentation of the item in the lookin*/
3399 pidlNext = pidl;
3400 while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3402 tmpFolder->m_iIndent++;
3405 tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3407 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3408 liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3410 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3411 SHGetFileInfoW((LPCWSTR)pidl,
3413 &sfi,
3414 sizeof(sfi),
3415 SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX
3416 | SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3418 TRACE("-- Add %s attr=%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3420 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3422 int iItemID;
3424 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3426 /* Add the item at the end of the list */
3427 if(iInsertId < 0)
3429 iItemID = CBAddString(hwnd,sfi.szDisplayName);
3431 /* Insert the item at the iInsertId position*/
3432 else
3434 iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3437 CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3438 return iItemID;
3441 COMDLG32_SHFree( tmpFolder->pidlItem );
3442 MemFree( tmpFolder );
3443 return -1;
3447 /***********************************************************************
3448 * FILEDLG95_LOOKIN_InsertItemAfterParent
3450 * Insert an item below its parent
3452 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3455 LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3456 int iParentPos;
3458 TRACE("\n");
3460 if (pidl == pidlParent)
3461 return -1;
3463 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3465 if(iParentPos < 0)
3467 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3470 /* Free pidlParent memory */
3471 COMDLG32_SHFree(pidlParent);
3473 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3476 /***********************************************************************
3477 * FILEDLG95_LOOKIN_SelectItem
3479 * Adds an absolute pidl item to the lookin combo box
3480 * returns the index of the inserted item
3482 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3484 int iItemPos;
3485 LookInInfos *liInfos;
3487 TRACE("\n");
3489 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3491 liInfos = GetPropA(hwnd,LookInInfosStr);
3493 if(iItemPos < 0)
3495 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3496 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3499 else
3501 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3502 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3504 int iRemovedItem;
3506 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3507 break;
3508 if(iRemovedItem < iItemPos)
3509 iItemPos--;
3513 CBSetCurSel(hwnd,iItemPos);
3514 liInfos->uSelectedItem = iItemPos;
3516 return 0;
3520 /***********************************************************************
3521 * FILEDLG95_LOOKIN_RemoveMostExpandedItem
3523 * Remove the item with an expansion level over iExpansionLevel
3525 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3527 int iItemPos;
3528 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3530 TRACE("\n");
3532 if(liInfos->iMaxIndentation <= 2)
3533 return -1;
3535 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3537 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3538 COMDLG32_SHFree(tmpFolder->pidlItem);
3539 MemFree(tmpFolder);
3540 CBDeleteString(hwnd,iItemPos);
3541 liInfos->iMaxIndentation--;
3543 return iItemPos;
3546 return -1;
3549 /***********************************************************************
3550 * FILEDLG95_LOOKIN_SearchItem
3552 * Search for pidl in the lookin combo box
3553 * returns the index of the found item
3555 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3557 int i = 0;
3558 int iCount = CBGetCount(hwnd);
3560 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3562 if (iCount != CB_ERR)
3564 for(;i<iCount;i++)
3566 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3568 if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3569 return i;
3570 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3571 return i;
3575 return -1;
3578 /***********************************************************************
3579 * FILEDLG95_LOOKIN_Clean
3581 * Clean the memory used by the lookin combo box
3583 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3585 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3586 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3587 int iPos;
3588 int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3590 TRACE("\n");
3592 /* Delete each string of the combo and their associated data */
3593 if (iCount != CB_ERR)
3595 for(iPos = iCount-1;iPos>=0;iPos--)
3597 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3598 COMDLG32_SHFree(tmpFolder->pidlItem);
3599 MemFree(tmpFolder);
3600 CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3604 /* LookInInfos structure */
3605 MemFree(liInfos);
3606 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3609 /***********************************************************************
3610 * FILEDLG95_FILENAME_FillFromSelection
3612 * fills the edit box from the cached DataObject
3614 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3616 FileOpenDlgInfos *fodInfos;
3617 LPITEMIDLIST pidl;
3618 UINT nFiles = 0, nFileToOpen, nFileSelected, nLength = 0;
3619 WCHAR lpstrTemp[MAX_PATH];
3620 LPWSTR lpstrAllFile, lpstrCurrFile;
3622 TRACE("\n");
3623 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3625 /* Count how many files we have */
3626 nFileSelected = GetNumSelected( fodInfos->Shell.FOIDataObject );
3628 /* calculate the string length, count files */
3629 if (nFileSelected >= 1)
3631 nLength += 3; /* first and last quotes, trailing \0 */
3632 for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ )
3634 pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 );
3636 if (pidl)
3638 /* get the total length of the selected file names */
3639 lpstrTemp[0] = '\0';
3640 GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp );
3642 if ( ! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl) ) /* Ignore folders */
3644 nLength += lstrlenW( lpstrTemp ) + 3;
3645 nFiles++;
3647 COMDLG32_SHFree( pidl );
3652 /* allocate the buffer */
3653 if (nFiles <= 1) nLength = MAX_PATH;
3654 lpstrAllFile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength * sizeof(WCHAR));
3656 /* Generate the string for the edit control */
3657 if(nFiles >= 1)
3659 lpstrCurrFile = lpstrAllFile;
3660 for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ )
3662 pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 );
3664 if (pidl)
3666 /* get the file name */
3667 lpstrTemp[0] = '\0';
3668 GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp );
3670 if (! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl)) /* Ignore folders */
3672 if ( nFiles > 1)
3674 *lpstrCurrFile++ = '\"';
3675 lstrcpyW( lpstrCurrFile, lpstrTemp );
3676 lpstrCurrFile += lstrlenW( lpstrTemp );
3677 *lpstrCurrFile++ = '\"';
3678 *lpstrCurrFile++ = ' ';
3679 *lpstrCurrFile = 0;
3681 else
3683 lstrcpyW( lpstrAllFile, lpstrTemp );
3686 COMDLG32_SHFree( pidl );
3689 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrAllFile );
3691 /* Select the file name like Windows does */
3692 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3694 HeapFree(GetProcessHeap(),0, lpstrAllFile );
3698 /* copied from shell32 to avoid linking to it
3699 * Although shell32 is already linked the behaviour of exported StrRetToStrN
3700 * is dependent on whether emulated OS is unicode or not.
3702 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3704 switch (src->uType)
3706 case STRRET_WSTR:
3707 lstrcpynW(dest, src->u.pOleStr, len);
3708 COMDLG32_SHFree(src->u.pOleStr);
3709 break;
3711 case STRRET_CSTR:
3712 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3713 dest[len-1] = 0;
3714 break;
3716 case STRRET_OFFSET:
3717 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3718 dest[len-1] = 0;
3719 break;
3721 default:
3722 FIXME("unknown type %x!\n", src->uType);
3723 if (len) *dest = '\0';
3724 return E_FAIL;
3726 return S_OK;
3729 /***********************************************************************
3730 * FILEDLG95_FILENAME_GetFileNames
3732 * Copies the filenames to a delimited string list.
3734 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3736 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3737 UINT nFileCount = 0; /* number of files */
3738 UINT nStrLen = 0; /* length of string in edit control */
3739 LPWSTR lpstrEdit; /* buffer for string from edit control */
3741 TRACE("\n");
3743 /* get the filenames from the edit control */
3744 nStrLen = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0);
3745 lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3746 GetDlgItemTextW(hwnd, IDC_FILENAME, lpstrEdit, nStrLen+1);
3748 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3750 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3751 MemFree(lpstrEdit);
3752 return nFileCount;
3755 #define SETDefFormatEtc(fe,cf,med) \
3757 (fe).cfFormat = cf;\
3758 (fe).dwAspect = DVASPECT_CONTENT; \
3759 (fe).ptd =NULL;\
3760 (fe).tymed = med;\
3761 (fe).lindex = -1;\
3765 * DATAOBJECT Helper functions
3768 /***********************************************************************
3769 * COMCTL32_ReleaseStgMedium
3771 * like ReleaseStgMedium from ole32
3773 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3775 if(medium.pUnkForRelease)
3777 IUnknown_Release(medium.pUnkForRelease);
3779 else
3781 GlobalUnlock(medium.u.hGlobal);
3782 GlobalFree(medium.u.hGlobal);
3786 /***********************************************************************
3787 * GetPidlFromDataObject
3789 * Return pidl(s) by number from the cached DataObject
3791 * nPidlIndex=0 gets the fully qualified root path
3793 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3796 STGMEDIUM medium;
3797 FORMATETC formatetc;
3798 LPITEMIDLIST pidl = NULL;
3800 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3802 if (!doSelected)
3803 return NULL;
3805 /* Set the FORMATETC structure*/
3806 SETDefFormatEtc(formatetc, RegisterClipboardFormatA(CFSTR_SHELLIDLISTA), TYMED_HGLOBAL);
3808 /* Get the pidls from IDataObject */
3809 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3811 LPIDA cida = GlobalLock(medium.u.hGlobal);
3812 if(nPidlIndex <= cida->cidl)
3814 pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3816 COMCTL32_ReleaseStgMedium(medium);
3818 return pidl;
3821 /***********************************************************************
3822 * GetNumSelected
3824 * Return the number of selected items in the DataObject.
3827 static UINT GetNumSelected( IDataObject *doSelected )
3829 UINT retVal = 0;
3830 STGMEDIUM medium;
3831 FORMATETC formatetc;
3833 TRACE("sv=%p\n", doSelected);
3835 if (!doSelected) return 0;
3837 /* Set the FORMATETC structure*/
3838 SETDefFormatEtc(formatetc, RegisterClipboardFormatA(CFSTR_SHELLIDLISTA), TYMED_HGLOBAL);
3840 /* Get the pidls from IDataObject */
3841 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3843 LPIDA cida = GlobalLock(medium.u.hGlobal);
3844 retVal = cida->cidl;
3845 COMCTL32_ReleaseStgMedium(medium);
3846 return retVal;
3848 return 0;
3852 * TOOLS
3855 /***********************************************************************
3856 * GetName
3858 * Get the pidl's display name (relative to folder) and
3859 * put it in lpstrFileName.
3861 * Return NOERROR on success,
3862 * E_FAIL otherwise
3865 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3867 STRRET str;
3868 HRESULT hRes;
3870 TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3872 if(!lpsf)
3874 SHGetDesktopFolder(&lpsf);
3875 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3876 IShellFolder_Release(lpsf);
3877 return hRes;
3880 /* Get the display name of the pidl relative to the folder */
3881 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3883 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3885 return E_FAIL;
3888 /***********************************************************************
3889 * GetShellFolderFromPidl
3891 * pidlRel is the item pidl relative
3892 * Return the IShellFolder of the absolute pidl
3894 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3896 IShellFolder *psf = NULL,*psfParent;
3898 TRACE("%p\n", pidlAbs);
3900 if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3902 psf = psfParent;
3903 if(pidlAbs && pidlAbs->mkid.cb)
3905 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3907 IShellFolder_Release(psfParent);
3908 return psf;
3911 /* return the desktop */
3912 return psfParent;
3914 return NULL;
3917 /***********************************************************************
3918 * GetParentPidl
3920 * Return the LPITEMIDLIST to the parent of the pidl in the list
3922 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3924 LPITEMIDLIST pidlParent;
3926 TRACE("%p\n", pidl);
3928 pidlParent = COMDLG32_PIDL_ILClone(pidl);
3929 COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3931 return pidlParent;
3934 /***********************************************************************
3935 * GetPidlFromName
3937 * returns the pidl of the file name relative to folder
3938 * NULL if an error occurred
3940 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3942 LPITEMIDLIST pidl = NULL;
3943 ULONG ulEaten;
3945 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3947 if(!lpcstrFileName) return NULL;
3948 if(!*lpcstrFileName) return NULL;
3950 if(!lpsf)
3952 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3953 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3954 IShellFolder_Release(lpsf);
3957 else
3959 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3961 return pidl;
3966 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3968 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3969 HRESULT ret;
3971 TRACE("%p, %p\n", psf, pidl);
3973 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3975 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
3976 /* see documentation shell 4.1*/
3977 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3980 /***********************************************************************
3981 * BrowseSelectedFolder
3983 static BOOL BrowseSelectedFolder(HWND hwnd)
3985 BOOL bBrowseSelFolder = FALSE;
3986 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3988 TRACE("\n");
3990 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
3992 LPITEMIDLIST pidlSelection;
3994 /* get the file selected */
3995 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
3996 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
3998 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
3999 pidlSelection, SBSP_RELATIVE ) ) )
4001 static const WCHAR notexist[] = {'P','a','t','h',' ','d','o','e','s',
4002 ' ','n','o','t',' ','e','x','i','s','t',0};
4003 MessageBoxW( hwnd, notexist, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
4005 bBrowseSelFolder = TRUE;
4006 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
4007 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
4009 COMDLG32_SHFree( pidlSelection );
4012 return bBrowseSelFolder;
4016 * Memory allocation methods */
4017 static void *MemAlloc(UINT size)
4019 return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
4022 static void MemFree(void *mem)
4024 HeapFree(GetProcessHeap(),0,mem);
4027 static inline BOOL is_win16_looks(DWORD flags)
4029 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
4030 !(flags & OFN_EXPLORER));
4033 /* ------------------ APIs ---------------------- */
4035 /***********************************************************************
4036 * GetOpenFileNameA (COMDLG32.@)
4038 * Creates a dialog box for the user to select a file to open.
4040 * RETURNS
4041 * TRUE on success: user enters a valid file
4042 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4045 BOOL WINAPI GetOpenFileNameA(
4046 LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
4048 TRACE("flags %08x\n", ofn->Flags);
4050 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4051 if (ofn->Flags & OFN_FILEMUSTEXIST)
4052 ofn->Flags |= OFN_PATHMUSTEXIST;
4054 if (is_win16_looks(ofn->Flags))
4055 return GetFileName31A(ofn, OPEN_DIALOG);
4056 else
4057 return GetFileDialog95A(ofn, OPEN_DIALOG);
4060 /***********************************************************************
4061 * GetOpenFileNameW (COMDLG32.@)
4063 * Creates a dialog box for the user to select a file to open.
4065 * RETURNS
4066 * TRUE on success: user enters a valid file
4067 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4070 BOOL WINAPI GetOpenFileNameW(
4071 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4073 TRACE("flags %08x\n", ofn->Flags);
4075 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4076 if (ofn->Flags & OFN_FILEMUSTEXIST)
4077 ofn->Flags |= OFN_PATHMUSTEXIST;
4079 if (is_win16_looks(ofn->Flags))
4080 return GetFileName31W(ofn, OPEN_DIALOG);
4081 else
4082 return GetFileDialog95W(ofn, OPEN_DIALOG);
4086 /***********************************************************************
4087 * GetSaveFileNameA (COMDLG32.@)
4089 * Creates a dialog box for the user to select a file to save.
4091 * RETURNS
4092 * TRUE on success: user enters a valid file
4093 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4096 BOOL WINAPI GetSaveFileNameA(
4097 LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
4099 if (is_win16_looks(ofn->Flags))
4100 return GetFileName31A(ofn, SAVE_DIALOG);
4101 else
4102 return GetFileDialog95A(ofn, SAVE_DIALOG);
4105 /***********************************************************************
4106 * GetSaveFileNameW (COMDLG32.@)
4108 * Creates a dialog box for the user to select a file to save.
4110 * RETURNS
4111 * TRUE on success: user enters a valid file
4112 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4115 BOOL WINAPI GetSaveFileNameW(
4116 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4118 if (is_win16_looks(ofn->Flags))
4119 return GetFileName31W(ofn, SAVE_DIALOG);
4120 else
4121 return GetFileDialog95W(ofn, SAVE_DIALOG);
4124 /***********************************************************************
4125 * GetFileTitleA (COMDLG32.@)
4127 * See GetFileTitleW.
4129 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4131 int ret;
4132 UNICODE_STRING strWFile;
4133 LPWSTR lpWTitle;
4135 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4136 lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4137 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4138 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4139 RtlFreeUnicodeString( &strWFile );
4140 RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4141 return ret;
4145 /***********************************************************************
4146 * GetFileTitleW (COMDLG32.@)
4148 * Get the name of a file.
4150 * PARAMS
4151 * lpFile [I] name and location of file
4152 * lpTitle [O] returned file name
4153 * cbBuf [I] buffer size of lpTitle
4155 * RETURNS
4156 * Success: zero
4157 * Failure: negative number.
4159 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4161 int i, len;
4162 static const WCHAR brkpoint[] = {'*','[',']',0};
4163 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4165 if(lpFile == NULL || lpTitle == NULL)
4166 return -1;
4168 len = lstrlenW(lpFile);
4170 if (len == 0)
4171 return -1;
4173 if(strpbrkW(lpFile, brkpoint))
4174 return -1;
4176 len--;
4178 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4179 return -1;
4181 for(i = len; i >= 0; i--)
4183 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':')
4185 i++;
4186 break;
4190 if(i == -1)
4191 i++;
4193 TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4195 len = lstrlenW(lpFile+i)+1;
4196 if(cbBuf < len)
4197 return len;
4199 lstrcpyW(lpTitle, &lpFile[i]);
4200 return 0;