kernel32: Do not allow to combine NORM_IGNORENONSPACE and/or NORM_IGNORESYMBOLS with...
[wine.git] / dlls / comdlg32 / filedlg.c
blob95eacd3d7bb9015a2ba7f25c650b2b3c1fd2f356
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
60 #include "windef.h"
61 #include "winbase.h"
62 #include "winternl.h"
63 #include "winnls.h"
64 #include "wingdi.h"
65 #include "winreg.h"
66 #include "winuser.h"
67 #include "commdlg.h"
68 #include "dlgs.h"
69 #include "cdlg.h"
70 #include "cderr.h"
71 #include "shellapi.h"
72 #include "shlobj.h"
73 #include "filedlgbrowser.h"
74 #include "shlwapi.h"
76 #include "wine/unicode.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
81 #define UNIMPLEMENTED_FLAGS \
82 (OFN_DONTADDTORECENT |\
83 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
84 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
86 #define IsHooked(fodInfos) \
87 ((fodInfos->ofnInfos->Flags & OFN_ENABLEHOOK) && fodInfos->ofnInfos->lpfnHook)
88 /***********************************************************************
89 * Data structure and global variables
91 typedef struct SFolder
93 int m_iImageIndex; /* Index of picture in image list */
94 HIMAGELIST hImgList;
95 int m_iIndent; /* Indentation index */
96 LPITEMIDLIST pidlItem; /* absolute pidl of the item */
98 } SFOLDER,*LPSFOLDER;
100 typedef struct tagLookInInfo
102 int iMaxIndentation;
103 UINT uSelectedItem;
104 } LookInInfos;
107 /***********************************************************************
108 * Defines and global variables
111 /* Draw item constant */
112 #define XTEXTOFFSET 3
114 /* AddItem flags*/
115 #define LISTEND -1
117 /* SearchItem methods */
118 #define SEARCH_PIDL 1
119 #define SEARCH_EXP 2
120 #define ITEM_NOTFOUND -1
122 /* Undefined windows message sent by CreateViewObject*/
123 #define WM_GETISHELLBROWSER WM_USER+7
125 /* NOTE
126 * Those macros exist in windowsx.h. However, you can't really use them since
127 * they rely on the UNICODE defines and can't be used inside Wine itself.
130 /* Combo box macros */
131 #define CBAddString(hwnd,str) \
132 SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)(str));
134 #define CBInsertString(hwnd,str,pos) \
135 SendMessageW(hwnd, CB_INSERTSTRING, (WPARAM)(pos), (LPARAM)(str));
137 #define CBDeleteString(hwnd,pos) \
138 SendMessageW(hwnd, CB_DELETESTRING, (WPARAM)(pos), 0);
140 #define CBSetItemDataPtr(hwnd,iItemId,dataPtr) \
141 SendMessageW(hwnd, CB_SETITEMDATA, (WPARAM)(iItemId), (LPARAM)(dataPtr));
143 #define CBGetItemDataPtr(hwnd,iItemId) \
144 SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
146 #define CBGetLBText(hwnd,iItemId,str) \
147 SendMessageW(hwnd, CB_GETLBTEXT, (WPARAM)(iItemId), (LPARAM)(str));
149 #define CBGetCurSel(hwnd) \
150 SendMessageW(hwnd, CB_GETCURSEL, 0, 0);
152 #define CBSetCurSel(hwnd,pos) \
153 SendMessageW(hwnd, CB_SETCURSEL, (WPARAM)(pos), 0);
155 #define CBGetCount(hwnd) \
156 SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
157 #define CBShowDropDown(hwnd,show) \
158 SendMessageW(hwnd, CB_SHOWDROPDOWN, (WPARAM)(show), 0);
159 #define CBSetItemHeight(hwnd,index,height) \
160 SendMessageW(hwnd, CB_SETITEMHEIGHT, (WPARAM)(index), (LPARAM)(height));
162 #define CBSetExtendedUI(hwnd,flag) \
163 SendMessageW(hwnd, CB_SETEXTENDEDUI, (WPARAM)(flag), 0)
165 const char FileOpenDlgInfosStr[] = "FileOpenDlgInfos"; /* windows property description string */
166 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
167 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
169 static const WCHAR LastVisitedMRUW[] =
170 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
171 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
172 'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
173 'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
174 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
176 /***********************************************************************
177 * Prototypes
180 /* Internal functions used by the dialog */
181 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
182 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
183 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
184 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
185 static BOOL FILEDLG95_OnOpen(HWND hwnd);
186 static LRESULT FILEDLG95_InitControls(HWND hwnd);
187 static void FILEDLG95_Clean(HWND hwnd);
189 /* Functions used by the shell navigation */
190 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
191 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd);
192 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
193 static void FILEDLG95_SHELL_Clean(HWND hwnd);
194 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd);
196 /* Functions used by the EDIT box */
197 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
199 /* Functions used by the filetype combo box */
200 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
201 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
202 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
203 static void FILEDLG95_FILETYPE_Clean(HWND hwnd);
205 /* Functions used by the Look In combo box */
206 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo);
207 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
208 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
209 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
210 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
211 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
212 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
213 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
214 static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
216 /* Functions for dealing with the most-recently-used registry keys */
217 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
218 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
219 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
221 /* Miscellaneous tool functions */
222 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
223 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
224 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl);
225 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
226 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
227 static UINT GetNumSelected( IDataObject *doSelected );
228 static void COMCTL32_ReleaseStgMedium(STGMEDIUM medium);
230 /* Shell memory allocation */
231 static void *MemAlloc(UINT size);
232 static void MemFree(void *mem);
234 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
235 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
236 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
237 static BOOL BrowseSelectedFolder(HWND hwnd);
239 /***********************************************************************
240 * GetFileName95
242 * Creates an Open common dialog box that lets the user select
243 * the drive, directory, and the name of a file or set of files to open.
245 * IN : The FileOpenDlgInfos structure associated with the dialog
246 * OUT : TRUE on success
247 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
249 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
252 LRESULT lRes;
253 LPCVOID origTemplate;
254 DWORD dwSize;
255 LPDLGTEMPLATEW template;
256 HRSRC hRes;
257 HANDLE hDlgTmpl = 0;
258 HRESULT hr;
260 /* test for missing functionality */
261 if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
263 FIXME("Flags 0x%08x not yet implemented\n",
264 fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
267 /* Create the dialog from a template */
269 if(!(hRes = FindResourceW(COMDLG32_hInstance,MAKEINTRESOURCEW(NEWFILEOPENORD),(LPCWSTR)RT_DIALOG)))
271 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
272 return FALSE;
274 if (!(dwSize = SizeofResource(COMDLG32_hInstance, hRes)) ||
275 !(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes)) ||
276 !(origTemplate = LockResource(hDlgTmpl)))
278 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
279 return FALSE;
281 if (!(template = HeapAlloc(GetProcessHeap(), 0, dwSize)))
283 COMDLG32_SetCommDlgExtendedError(CDERR_MEMALLOCFAILURE);
284 return FALSE;
286 memcpy(template, origTemplate, dwSize);
288 /* msdn: explorer style dialogs permit sizing by default.
289 * The OFN_ENABLESIZING flag is only needed when a hook or
290 * custom tmeplate is provided */
291 if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
292 !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
293 fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
295 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
297 template->style |= WS_SIZEBOX;
298 fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
299 fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
301 else
302 template->style &= ~WS_SIZEBOX;
305 /* old style hook messages */
306 if (IsHooked(fodInfos))
308 fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
309 fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
310 fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
311 fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
314 /* Some shell namespace extensions depend on COM being initialized. */
315 hr = OleInitialize(NULL);
317 if (fodInfos->unicode)
318 lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
319 template,
320 fodInfos->ofnInfos->hwndOwner,
321 FileOpenDlgProc95,
322 (LPARAM) fodInfos);
323 else
324 lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
325 template,
326 fodInfos->ofnInfos->hwndOwner,
327 FileOpenDlgProc95,
328 (LPARAM) fodInfos);
329 if (SUCCEEDED(hr))
330 OleUninitialize();
332 HeapFree(GetProcessHeap(), 0, template);
334 /* Unable to create the dialog */
335 if( lRes == -1)
336 return FALSE;
338 return lRes;
341 /***********************************************************************
342 * GetFileDialog95A
344 * Call GetFileName95 with this structure and clean the memory.
346 * IN : The OPENFILENAMEA initialisation structure passed to
347 * GetOpenFileNameA win api function (see filedlg.c)
349 static BOOL GetFileDialog95A(LPOPENFILENAMEA ofn,UINT iDlgType)
351 BOOL ret;
352 FileOpenDlgInfos fodInfos;
353 LPSTR lpstrSavDir = NULL;
354 LPWSTR title = NULL;
355 LPWSTR defext = NULL;
356 LPWSTR filter = NULL;
357 LPWSTR customfilter = NULL;
358 INITCOMMONCONTROLSEX icc;
360 /* Initialize ComboBoxEx32 */
361 icc.dwSize = sizeof(icc);
362 icc.dwICC = ICC_USEREX_CLASSES;
363 InitCommonControlsEx(&icc);
365 /* Initialize CommDlgExtendedError() */
366 COMDLG32_SetCommDlgExtendedError(0);
368 /* Initialize FileOpenDlgInfos structure */
369 ZeroMemory(&fodInfos, sizeof(FileOpenDlgInfos));
371 /* Pass in the original ofn */
372 fodInfos.ofnInfos = (LPOPENFILENAMEW)ofn;
374 /* save current directory */
375 if (ofn->Flags & OFN_NOCHANGEDIR)
377 lpstrSavDir = MemAlloc(MAX_PATH);
378 GetCurrentDirectoryA(MAX_PATH, lpstrSavDir);
381 fodInfos.unicode = FALSE;
383 /* convert all the input strings to unicode */
384 if(ofn->lpstrInitialDir)
386 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrInitialDir, -1, NULL, 0 );
387 fodInfos.initdir = MemAlloc((len+1)*sizeof(WCHAR));
388 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrInitialDir, -1, fodInfos.initdir, len);
390 else
391 fodInfos.initdir = NULL;
393 if(ofn->lpstrFile)
395 fodInfos.filename = MemAlloc(ofn->nMaxFile*sizeof(WCHAR));
396 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFile, -1, fodInfos.filename, ofn->nMaxFile);
398 else
399 fodInfos.filename = NULL;
401 if(ofn->lpstrDefExt)
403 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrDefExt, -1, NULL, 0 );
404 defext = MemAlloc((len+1)*sizeof(WCHAR));
405 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrDefExt, -1, defext, len);
407 fodInfos.defext = defext;
409 if(ofn->lpstrTitle)
411 DWORD len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrTitle, -1, NULL, 0 );
412 title = MemAlloc((len+1)*sizeof(WCHAR));
413 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrTitle, -1, title, len);
415 fodInfos.title = title;
417 if (ofn->lpstrFilter)
419 LPCSTR s;
420 int n, len;
422 /* filter is a list... title\0ext\0......\0\0 */
423 s = ofn->lpstrFilter;
424 while (*s) s = s+strlen(s)+1;
425 s++;
426 n = s - ofn->lpstrFilter;
427 len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0 );
428 filter = MemAlloc(len*sizeof(WCHAR));
429 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrFilter, n, filter, len );
431 fodInfos.filter = filter;
433 /* convert lpstrCustomFilter */
434 if (ofn->lpstrCustomFilter)
436 LPCSTR s;
437 int n, len;
439 /* customfilter contains a pair of strings... title\0ext\0 */
440 s = ofn->lpstrCustomFilter;
441 if (*s) s = s+strlen(s)+1;
442 if (*s) s = s+strlen(s)+1;
443 n = s - ofn->lpstrCustomFilter;
444 len = MultiByteToWideChar( CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0 );
445 customfilter = MemAlloc(len*sizeof(WCHAR));
446 MultiByteToWideChar( CP_ACP, 0, ofn->lpstrCustomFilter, n, customfilter, len );
448 fodInfos.customfilter = customfilter;
450 /* Initialize the dialog property */
451 fodInfos.DlgInfos.dwDlgProp = 0;
452 fodInfos.DlgInfos.hwndCustomDlg = NULL;
454 switch(iDlgType)
456 case OPEN_DIALOG :
457 ret = GetFileName95(&fodInfos);
458 break;
459 case SAVE_DIALOG :
460 fodInfos.DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
461 ret = GetFileName95(&fodInfos);
462 break;
463 default :
464 ret = FALSE;
467 if (lpstrSavDir)
469 SetCurrentDirectoryA(lpstrSavDir);
470 MemFree(lpstrSavDir);
473 MemFree(title);
474 MemFree(defext);
475 MemFree(filter);
476 MemFree(customfilter);
477 MemFree(fodInfos.initdir);
478 MemFree(fodInfos.filename);
480 TRACE("selected file: %s\n",ofn->lpstrFile);
482 return ret;
485 /***********************************************************************
486 * GetFileDialog95W
488 * Copy the OPENFILENAMEW structure in a FileOpenDlgInfos structure.
489 * Call GetFileName95 with this structure and clean the memory.
492 static BOOL GetFileDialog95W(LPOPENFILENAMEW ofn,UINT iDlgType)
494 BOOL ret;
495 FileOpenDlgInfos fodInfos;
496 LPWSTR lpstrSavDir = NULL;
497 INITCOMMONCONTROLSEX icc;
499 /* Initialize ComboBoxEx32 */
500 icc.dwSize = sizeof(icc);
501 icc.dwICC = ICC_USEREX_CLASSES;
502 InitCommonControlsEx(&icc);
504 /* Initialize CommDlgExtendedError() */
505 COMDLG32_SetCommDlgExtendedError(0);
507 /* Initialize FileOpenDlgInfos structure */
508 ZeroMemory(&fodInfos, sizeof(FileOpenDlgInfos));
510 /* Pass in the original ofn */
511 fodInfos.ofnInfos = ofn;
513 fodInfos.title = ofn->lpstrTitle;
514 fodInfos.defext = ofn->lpstrDefExt;
515 fodInfos.filter = ofn->lpstrFilter;
516 fodInfos.customfilter = ofn->lpstrCustomFilter;
518 /* convert string arguments, save others */
519 if(ofn->lpstrFile)
521 fodInfos.filename = MemAlloc(ofn->nMaxFile*sizeof(WCHAR));
522 lstrcpynW(fodInfos.filename,ofn->lpstrFile,ofn->nMaxFile);
524 else
525 fodInfos.filename = NULL;
527 if(ofn->lpstrInitialDir)
529 /* fodInfos.initdir = strdupW(ofn->lpstrInitialDir); */
530 DWORD len = lstrlenW(ofn->lpstrInitialDir)+1;
531 fodInfos.initdir = MemAlloc(len*sizeof(WCHAR));
532 memcpy(fodInfos.initdir,ofn->lpstrInitialDir,len*sizeof(WCHAR));
534 else
535 fodInfos.initdir = NULL;
537 /* save current directory */
538 if (ofn->Flags & OFN_NOCHANGEDIR)
540 lpstrSavDir = MemAlloc(MAX_PATH*sizeof(WCHAR));
541 GetCurrentDirectoryW(MAX_PATH, lpstrSavDir);
544 fodInfos.unicode = TRUE;
546 switch(iDlgType)
548 case OPEN_DIALOG :
549 ret = GetFileName95(&fodInfos);
550 break;
551 case SAVE_DIALOG :
552 fodInfos.DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
553 ret = GetFileName95(&fodInfos);
554 break;
555 default :
556 ret = FALSE;
559 if (lpstrSavDir)
561 SetCurrentDirectoryW(lpstrSavDir);
562 MemFree(lpstrSavDir);
565 /* restore saved IN arguments and convert OUT arguments back */
566 MemFree(fodInfos.filename);
567 MemFree(fodInfos.initdir);
568 return ret;
571 /******************************************************************************
572 * COMDLG32_GetDisplayNameOf [internal]
574 * Helper function to get the display name for a pidl.
576 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
577 LPSHELLFOLDER psfDesktop;
578 STRRET strret;
580 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
581 return FALSE;
583 if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
584 IShellFolder_Release(psfDesktop);
585 return FALSE;
588 IShellFolder_Release(psfDesktop);
589 return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
592 /******************************************************************************
593 * COMDLG32_GetCanonicalPath [internal]
595 * Helper function to get the canonical path.
597 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
598 LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
600 WCHAR lpstrTemp[MAX_PATH];
602 /* Get the current directory name */
603 if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
605 /* last fallback */
606 GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
608 PathAddBackslashW(lpstrPathAndFile);
610 TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile));
612 /* if the user specified a fully qualified path use it */
613 if(PathIsRelativeW(lpstrFile))
615 lstrcatW(lpstrPathAndFile, lpstrFile);
617 else
619 /* does the path have a drive letter? */
620 if (PathGetDriveNumberW(lpstrFile) == -1)
621 lstrcpyW(lpstrPathAndFile+2, lpstrFile);
622 else
623 lstrcpyW(lpstrPathAndFile, lpstrFile);
626 /* resolve "." and ".." */
627 PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
628 lstrcpyW(lpstrPathAndFile, lpstrTemp);
629 TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
632 /***********************************************************************
633 * COMDLG32_SplitFileNames [internal]
635 * Creates a delimited list of filenames.
637 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
639 UINT nStrCharCount = 0; /* index in src buffer */
640 UINT nFileIndex = 0; /* index in dest buffer */
641 UINT nFileCount = 0; /* number of files */
643 /* we might get single filename without any '"',
644 * so we need nStrLen + terminating \0 + end-of-list \0 */
645 *lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
646 *sizeUsed = 0;
648 /* build delimited file list from filenames */
649 while ( nStrCharCount <= nStrLen )
651 if ( lpstrEdit[nStrCharCount]=='"' )
653 nStrCharCount++;
654 while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
656 (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
657 nStrCharCount++;
659 (*lpstrFileList)[nFileIndex++] = 0;
660 nFileCount++;
662 nStrCharCount++;
665 /* single, unquoted string */
666 if ((nStrLen > 0) && (nFileIndex == 0) )
668 lstrcpyW(*lpstrFileList, lpstrEdit);
669 nFileIndex = lstrlenW(lpstrEdit) + 1;
670 nFileCount = 1;
673 /* trailing \0 */
674 (*lpstrFileList)[nFileIndex++] = '\0';
676 *sizeUsed = nFileIndex;
677 return nFileCount;
680 /***********************************************************************
681 * ArrangeCtrlPositions [internal]
683 * NOTE: Make sure to add testcases for any changes made here.
685 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
687 HWND hwndChild, hwndStc32;
688 RECT rectParent, rectChild, rectStc32;
689 INT help_fixup = 0;
690 int chgx, chgy;
692 /* Take into account if open as read only checkbox and help button
693 * are hidden
695 if (hide_help)
697 RECT rectHelp, rectCancel;
698 GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
699 GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
700 /* subtract the height of the help button plus the space between
701 * the help button and the cancel button to the height of the dialog
703 help_fixup = rectHelp.bottom - rectCancel.bottom;
707 There are two possibilities to add components to the default file dialog box.
709 By default, all the new components are added below the standard dialog box (the else case).
711 However, if there is a static text component with the stc32 id, a special case happens.
712 The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
713 in the window and the cx and cy indicate how to size the window.
714 Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
715 of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
719 GetClientRect(hwndParentDlg, &rectParent);
721 /* when arranging controls we have to use fixed parent size */
722 rectParent.bottom -= help_fixup;
724 hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
725 if (hwndStc32)
727 GetWindowRect(hwndStc32, &rectStc32);
728 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
730 /* set the size of the stc32 control according to the size of
731 * client area of the parent dialog
733 SetWindowPos(hwndStc32, 0,
734 0, 0,
735 rectParent.right, rectParent.bottom,
736 SWP_NOMOVE | SWP_NOZORDER);
738 else
739 SetRectEmpty(&rectStc32);
741 /* this part moves controls of the child dialog */
742 hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
743 while (hwndChild)
745 if (hwndChild != hwndStc32)
747 GetWindowRect(hwndChild, &rectChild);
748 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
750 /* move only if stc32 exist */
751 if (hwndStc32 && rectChild.left > rectStc32.right)
753 /* move to the right of visible controls of the parent dialog */
754 rectChild.left += rectParent.right;
755 rectChild.left -= rectStc32.right;
757 /* move even if stc32 doesn't exist */
758 if (rectChild.top >= rectStc32.bottom)
760 /* move below visible controls of the parent dialog */
761 rectChild.top += rectParent.bottom;
762 rectChild.top -= rectStc32.bottom - rectStc32.top;
765 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
766 0, 0, SWP_NOSIZE | SWP_NOZORDER);
768 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
771 /* this part moves controls of the parent dialog */
772 hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
773 while (hwndChild)
775 if (hwndChild != hwndChildDlg)
777 GetWindowRect(hwndChild, &rectChild);
778 MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
780 /* left,top of stc32 marks the position of controls
781 * from the parent dialog
783 rectChild.left += rectStc32.left;
784 rectChild.top += rectStc32.top;
786 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
787 0, 0, SWP_NOSIZE | SWP_NOZORDER);
789 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
792 /* calculate the size of the resulting dialog */
794 /* here we have to use original parent size */
795 GetClientRect(hwndParentDlg, &rectParent);
796 GetClientRect(hwndChildDlg, &rectChild);
797 TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
798 wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
800 if (hwndStc32)
802 /* width */
803 if (rectParent.right > rectStc32.right - rectStc32.left)
804 chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
805 else
806 chgx = rectChild.right - rectParent.right;
807 /* height */
808 if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
809 chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
810 else
811 /* Unconditionally set new dialog
812 * height to that of the child
814 chgy = rectChild.bottom - rectParent.bottom;
816 else
818 chgx = 0;
819 chgy = rectChild.bottom - help_fixup;
821 /* set the size of the parent dialog */
822 GetWindowRect(hwndParentDlg, &rectParent);
823 SetWindowPos(hwndParentDlg, 0,
824 0, 0,
825 rectParent.right - rectParent.left + chgx,
826 rectParent.bottom - rectParent.top + chgy,
827 SWP_NOMOVE | SWP_NOZORDER);
830 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
832 switch(uMsg) {
833 case WM_INITDIALOG:
834 return TRUE;
836 return FALSE;
839 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
841 LPCVOID template;
842 HRSRC hRes;
843 HANDLE hDlgTmpl = 0;
844 HWND hChildDlg = 0;
846 TRACE("\n");
849 * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
850 * structure's hInstance parameter is not a HINSTANCE, but
851 * instead a pointer to a template resource to use.
853 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
855 HINSTANCE hinst;
856 if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
858 hinst = COMDLG32_hInstance;
859 if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
861 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
862 return NULL;
865 else
867 hinst = fodInfos->ofnInfos->hInstance;
868 if(fodInfos->unicode)
870 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
871 hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
873 else
875 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
876 hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
878 if (!hRes)
880 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
881 return NULL;
883 if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
884 !(template = LockResource( hDlgTmpl )))
886 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
887 return NULL;
890 if (fodInfos->unicode)
891 hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
892 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
893 (LPARAM)fodInfos->ofnInfos);
894 else
895 hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
896 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
897 (LPARAM)fodInfos->ofnInfos);
898 return hChildDlg;
900 else if( IsHooked(fodInfos))
902 RECT rectHwnd;
903 struct {
904 DLGTEMPLATE tmplate;
905 WORD menu,class,title;
906 } temp;
907 GetClientRect(hwnd,&rectHwnd);
908 temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
909 temp.tmplate.dwExtendedStyle = 0;
910 temp.tmplate.cdit = 0;
911 temp.tmplate.x = 0;
912 temp.tmplate.y = 0;
913 temp.tmplate.cx = 0;
914 temp.tmplate.cy = 0;
915 temp.menu = temp.class = temp.title = 0;
917 hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
918 hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
920 return hChildDlg;
922 return NULL;
925 /***********************************************************************
926 * SendCustomDlgNotificationMessage
928 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
931 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
933 LRESULT hook_result = 0;
934 FileOpenDlgInfos *fodInfos = GetPropA(hwndParentDlg,FileOpenDlgInfosStr);
936 TRACE("%p 0x%04x\n",hwndParentDlg, uCode);
938 if(!fodInfos) return 0;
940 if(fodInfos->DlgInfos.hwndCustomDlg)
942 TRACE("CALL NOTIFY for %x\n", uCode);
943 if(fodInfos->unicode)
945 OFNOTIFYW ofnNotify;
946 ofnNotify.hdr.hwndFrom=hwndParentDlg;
947 ofnNotify.hdr.idFrom=0;
948 ofnNotify.hdr.code = uCode;
949 ofnNotify.lpOFN = fodInfos->ofnInfos;
950 ofnNotify.pszFile = NULL;
951 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
953 else
955 OFNOTIFYA ofnNotify;
956 ofnNotify.hdr.hwndFrom=hwndParentDlg;
957 ofnNotify.hdr.idFrom=0;
958 ofnNotify.hdr.code = uCode;
959 ofnNotify.lpOFN = (LPOPENFILENAMEA)fodInfos->ofnInfos;
960 ofnNotify.pszFile = NULL;
961 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
963 TRACE("RET NOTIFY\n");
965 TRACE("Retval: 0x%08lx\n", hook_result);
966 return hook_result;
969 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
971 UINT len, total;
972 WCHAR *p, *buffer;
973 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
975 TRACE("CDM_GETFILEPATH:\n");
977 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
978 return -1;
980 /* get path and filenames */
981 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
982 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
983 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
984 if (len)
986 p = buffer + strlenW(buffer);
987 *p++ = '\\';
988 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
990 if (fodInfos->unicode)
992 total = strlenW( buffer) + 1;
993 if (result) lstrcpynW( result, buffer, size );
994 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
996 else
998 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
999 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
1000 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
1002 HeapFree( GetProcessHeap(), 0, buffer );
1003 return total;
1006 /***********************************************************************
1007 * FILEDLG95_HandleCustomDialogMessages
1009 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
1011 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1013 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1014 WCHAR lpstrPath[MAX_PATH];
1015 INT_PTR retval;
1017 if(!fodInfos) return FALSE;
1019 switch(uMsg)
1021 case CDM_GETFILEPATH:
1022 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
1023 break;
1025 case CDM_GETFOLDERPATH:
1026 TRACE("CDM_GETFOLDERPATH:\n");
1027 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
1028 if (lParam)
1030 if (fodInfos->unicode)
1031 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
1032 else
1033 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
1034 (LPSTR)lParam, (int)wParam, NULL, NULL);
1036 retval = lstrlenW(lpstrPath) + 1;
1037 break;
1039 case CDM_GETFOLDERIDLIST:
1040 retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
1041 if (retval <= wParam)
1042 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
1043 break;
1045 case CDM_GETSPEC:
1046 TRACE("CDM_GETSPEC:\n");
1047 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
1048 if (lParam)
1050 if (fodInfos->unicode)
1051 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1052 else
1053 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1055 break;
1057 case CDM_SETCONTROLTEXT:
1058 TRACE("CDM_SETCONTROLTEXT:\n");
1059 if ( lParam )
1061 if( fodInfos->unicode )
1062 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
1063 else
1064 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
1066 retval = TRUE;
1067 break;
1069 case CDM_HIDECONTROL:
1070 /* MSDN states that it should fail for not OFN_EXPLORER case */
1071 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1073 HWND control = GetDlgItem( hwnd, wParam );
1074 if (control) ShowWindow( control, SW_HIDE );
1075 retval = TRUE;
1077 else retval = FALSE;
1078 break;
1080 default:
1081 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1082 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
1083 return FALSE;
1085 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
1086 return TRUE;
1089 /***********************************************************************
1090 * FILEDLG95_OnWMGetMMI
1092 * WM_GETMINMAXINFO message handler for resizable dialogs
1094 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
1096 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1097 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1098 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1100 mmiptr->ptMinTrackSize = fodInfos->initial_size;
1102 return TRUE;
1105 /***********************************************************************
1106 * FILEDLG95_OnWMSize
1108 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1110 * FIXME: this could be made more elaborate. Now use a simple scheme
1111 * where the file view is enlarged and the controls are either moved
1112 * vertically or horizontally to get out of the way. Only the "grip"
1113 * is moved in both directions to stay in the corner.
1115 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1117 RECT rc, rcview;
1118 int chgx, chgy;
1119 HWND ctrl;
1120 HDWP hdwp;
1121 FileOpenDlgInfos *fodInfos;
1123 if( wParam != SIZE_RESTORED) return FALSE;
1124 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1125 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1126 /* get the new dialog rectangle */
1127 GetWindowRect( hwnd, &rc);
1128 TRACE("Size from %d,%d to %d,%d\n", fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1129 rc.right -rc.left, rc.bottom -rc.top);
1130 /* not initialized yet */
1131 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1132 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1133 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1134 return FALSE;
1135 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1136 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1137 fodInfos->sizedlg.cx = rc.right - rc.left;
1138 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1139 /* change the size of the view window */
1140 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1141 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1142 hdwp = BeginDeferWindowPos( 10);
1143 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1144 rcview.right - rcview.left + chgx,
1145 rcview.bottom - rcview.top + chgy,
1146 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1147 /* change position and sizes of the controls */
1148 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1150 int ctrlid = GetDlgCtrlID( ctrl);
1151 GetWindowRect( ctrl, &rc);
1152 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1153 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1155 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1156 0, 0,
1157 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1159 else if( rc.top > rcview.bottom)
1161 /* if it was below the shell view
1162 * move to bottom */
1163 switch( ctrlid)
1165 /* file name (edit or comboboxex) and file types combo change also width */
1166 case edt1:
1167 case cmb13:
1168 case cmb1:
1169 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1170 rc.right - rc.left + chgx, rc.bottom - rc.top,
1171 SWP_NOACTIVATE | SWP_NOZORDER);
1172 break;
1173 /* then these buttons must move out of the way */
1174 case IDOK:
1175 case IDCANCEL:
1176 case pshHelp:
1177 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1178 0, 0,
1179 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1180 break;
1181 default:
1182 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1183 0, 0,
1184 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1187 else if( rc.left > rcview.right)
1189 /* if it was to the right of the shell view
1190 * move to right */
1191 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1192 0, 0,
1193 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1195 else
1196 /* special cases */
1198 switch( ctrlid)
1200 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1201 case IDC_LOOKIN:
1202 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1203 rc.right - rc.left + chgx, rc.bottom - rc.top,
1204 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1205 break;
1206 case IDC_TOOLBARSTATIC:
1207 case IDC_TOOLBAR:
1208 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1209 0, 0,
1210 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1211 break;
1212 #endif
1213 /* not resized in windows. Since wine uses this invisible control
1214 * to size the browser view it needs to be resized */
1215 case IDC_SHELLSTATIC:
1216 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1217 rc.right - rc.left + chgx,
1218 rc.bottom - rc.top + chgy,
1219 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1220 break;
1224 if(fodInfos->DlgInfos.hwndCustomDlg &&
1225 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1227 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1228 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1230 GetWindowRect( ctrl, &rc);
1231 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1232 if( rc.top > rcview.bottom)
1234 /* if it was below the shell view
1235 * move to bottom */
1236 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1237 rc.right - rc.left, rc.bottom - rc.top,
1238 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1240 else if( rc.left > rcview.right)
1242 /* if it was to the right of the shell view
1243 * move to right */
1244 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1245 rc.right - rc.left, rc.bottom - rc.top,
1246 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1249 /* size the custom dialog at the end: some applications do some
1250 * control re-arranging at this point */
1251 GetClientRect(hwnd, &rc);
1252 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1253 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1255 EndDeferWindowPos( hdwp);
1256 /* should not be needed */
1257 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1258 return TRUE;
1261 /***********************************************************************
1262 * FileOpenDlgProc95
1264 * File open dialog procedure
1266 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1268 #if 0
1269 TRACE("%p 0x%04x\n", hwnd, uMsg);
1270 #endif
1272 switch(uMsg)
1274 case WM_INITDIALOG:
1276 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1277 RECT rc, rcstc;
1278 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1279 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1281 /* Adds the FileOpenDlgInfos in the property list of the dialog
1282 so it will be easily accessible through a GetPropA(...) */
1283 SetPropA(hwnd, FileOpenDlgInfosStr, fodInfos);
1285 FILEDLG95_InitControls(hwnd);
1287 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1289 GetWindowRect( hwnd, &rc);
1290 fodInfos->DlgInfos.hwndGrip =
1291 CreateWindowExA( 0, "SCROLLBAR", NULL,
1292 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1293 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1294 rc.right - gripx, rc.bottom - gripy,
1295 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1298 fodInfos->DlgInfos.hwndCustomDlg =
1299 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1301 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1302 FILEDLG95_FillControls(hwnd, wParam, lParam);
1304 if( fodInfos->DlgInfos.hwndCustomDlg)
1305 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1307 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1308 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1309 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1312 /* if the app has changed the position of the invisible listbox,
1313 * change that of the listview (browser) as well */
1314 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1315 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1316 if( !EqualRect( &rc, &rcstc))
1318 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1319 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1320 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1321 SWP_NOACTIVATE | SWP_NOZORDER);
1324 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1326 GetWindowRect( hwnd, &rc);
1327 fodInfos->sizedlg.cx = rc.right - rc.left;
1328 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1329 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1330 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1331 GetClientRect( hwnd, &rc);
1332 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1333 rc.right - gripx, rc.bottom - gripy,
1334 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1335 /* resize the dialog to the previous invocation */
1336 if( MemDialogSize.cx && MemDialogSize.cy)
1337 SetWindowPos( hwnd, NULL,
1338 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1339 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1342 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1343 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1345 return 0;
1347 case WM_SIZE:
1348 return FILEDLG95_OnWMSize(hwnd, wParam);
1349 case WM_GETMINMAXINFO:
1350 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1351 case WM_COMMAND:
1352 return FILEDLG95_OnWMCommand(hwnd, wParam);
1353 case WM_DRAWITEM:
1355 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1357 case IDC_LOOKIN:
1358 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1359 return TRUE;
1362 return FALSE;
1364 case WM_GETISHELLBROWSER:
1365 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1367 case WM_DESTROY:
1369 FileOpenDlgInfos * fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1370 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1371 MemDialogSize = fodInfos->sizedlg;
1372 RemovePropA(hwnd, FileOpenDlgInfosStr);
1373 return FALSE;
1375 case WM_NOTIFY:
1377 LPNMHDR lpnmh = (LPNMHDR)lParam;
1378 UINT stringId = -1;
1380 /* set up the button tooltips strings */
1381 if(TTN_GETDISPINFOA == lpnmh->code )
1383 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1384 switch(lpnmh->idFrom )
1386 /* Up folder button */
1387 case FCIDM_TB_UPFOLDER:
1388 stringId = IDS_UPFOLDER;
1389 break;
1390 /* New folder button */
1391 case FCIDM_TB_NEWFOLDER:
1392 stringId = IDS_NEWFOLDER;
1393 break;
1394 /* List option button */
1395 case FCIDM_TB_SMALLICON:
1396 stringId = IDS_LISTVIEW;
1397 break;
1398 /* Details option button */
1399 case FCIDM_TB_REPORTVIEW:
1400 stringId = IDS_REPORTVIEW;
1401 break;
1402 /* Desktop button */
1403 case FCIDM_TB_DESKTOP:
1404 stringId = IDS_TODESKTOP;
1405 break;
1406 default:
1407 stringId = 0;
1409 lpdi->hinst = COMDLG32_hInstance;
1410 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1412 return FALSE;
1414 default :
1415 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1416 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1417 return FALSE;
1421 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1423 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1424 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1427 /***********************************************************************
1428 * FILEDLG95_InitControls
1430 * WM_INITDIALOG message handler (before hook notification)
1432 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1434 BOOL win2000plus = FALSE;
1435 BOOL win98plus = FALSE;
1436 BOOL handledPath = FALSE;
1437 OSVERSIONINFOW osVi;
1438 static const WCHAR szwSlash[] = { '\\', 0 };
1439 static const WCHAR szwStar[] = { '*',0 };
1441 static const TBBUTTON tbb[] =
1443 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1444 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1445 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1446 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1447 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1448 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1449 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1450 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1451 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1453 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1455 RECT rectTB;
1456 RECT rectlook;
1458 HIMAGELIST toolbarImageList;
1459 SHFILEINFOA shFileInfo;
1460 ITEMIDLIST *desktopPidl;
1462 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1464 TRACE("%p\n", fodInfos);
1466 /* Get windows version emulating */
1467 osVi.dwOSVersionInfoSize = sizeof(osVi);
1468 GetVersionExW(&osVi);
1469 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1470 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1471 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1472 win2000plus = (osVi.dwMajorVersion > 4);
1473 if (win2000plus) win98plus = TRUE;
1475 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1478 /* Use either the edit or the comboboxex for the filename control */
1479 if (filename_is_edit( fodInfos ))
1481 DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1482 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1484 else
1486 DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1487 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1490 /* Get the hwnd of the controls */
1491 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1492 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1494 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1495 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1497 /* construct the toolbar */
1498 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1499 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1501 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1502 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1503 rectTB.left = rectlook.right;
1504 rectTB.top = rectlook.top-1;
1506 if (fodInfos->unicode)
1507 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1508 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1509 rectTB.left, rectTB.top,
1510 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1511 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1512 else
1513 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1514 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1515 rectTB.left, rectTB.top,
1516 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1517 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1519 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1521 /* FIXME: use TB_LOADIMAGES when implemented */
1522 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1523 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1524 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1526 /* Retrieve and add desktop icon to the toolbar */
1527 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1528 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1529 SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1530 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1531 ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1533 DestroyIcon(shFileInfo.hIcon);
1534 CoTaskMemFree(desktopPidl);
1536 /* Finish Toolbar Construction */
1537 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1538 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1540 /* Set the window text with the text specified in the OPENFILENAME structure */
1541 if(fodInfos->title)
1543 SetWindowTextW(hwnd,fodInfos->title);
1545 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1547 WCHAR buf[64];
1548 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1549 SetWindowTextW(hwnd, buf);
1552 /* Initialise the file name edit control */
1553 handledPath = FALSE;
1554 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1556 if(fodInfos->filename)
1558 /* 1. If win2000 or higher and filename contains a path, use it
1559 in preference over the lpstrInitialDir */
1560 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1561 WCHAR tmpBuf[MAX_PATH];
1562 WCHAR *nameBit;
1563 DWORD result;
1565 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1566 if (result) {
1568 /* nameBit is always shorter than the original filename */
1569 lstrcpyW(fodInfos->filename,nameBit);
1571 *nameBit = 0x00;
1572 MemFree(fodInfos->initdir);
1573 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1574 lstrcpyW(fodInfos->initdir, tmpBuf);
1575 handledPath = TRUE;
1576 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1577 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1579 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1581 } else {
1582 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1586 /* 2. (All platforms) If initdir is not null, then use it */
1587 if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1589 /* Work out the proper path as supplied one might be relative */
1590 /* (Here because supplying '.' as dir browses to My Computer) */
1591 WCHAR tmpBuf[MAX_PATH];
1592 WCHAR tmpBuf2[MAX_PATH];
1593 WCHAR *nameBit;
1594 DWORD result;
1596 lstrcpyW(tmpBuf, fodInfos->initdir);
1597 if (PathFileExistsW(tmpBuf)) {
1598 /* initdir does not have to be a directory. If a file is
1599 * specified, the dir part is taken */
1600 if (PathIsDirectoryW(tmpBuf)) {
1601 PathAddBackslashW(tmpBuf);
1602 lstrcatW(tmpBuf, szwStar);
1604 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1605 if (result) {
1606 *nameBit = 0x00;
1607 MemFree(fodInfos->initdir);
1608 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1609 lstrcpyW(fodInfos->initdir, tmpBuf2);
1610 handledPath = TRUE;
1611 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1614 else if (fodInfos->initdir)
1616 MemFree(fodInfos->initdir);
1617 fodInfos->initdir = NULL;
1618 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1622 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1624 /* 3. All except w2k+: if filename contains a path use it */
1625 if (!win2000plus && fodInfos->filename &&
1626 *fodInfos->filename &&
1627 strpbrkW(fodInfos->filename, szwSlash)) {
1628 WCHAR tmpBuf[MAX_PATH];
1629 WCHAR *nameBit;
1630 DWORD result;
1632 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1633 tmpBuf, &nameBit);
1634 if (result) {
1635 int len;
1637 /* nameBit is always shorter than the original filename */
1638 lstrcpyW(fodInfos->filename, nameBit);
1639 *nameBit = 0x00;
1641 len = lstrlenW(tmpBuf);
1642 MemFree(fodInfos->initdir);
1643 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1644 lstrcpyW(fodInfos->initdir, tmpBuf);
1646 handledPath = TRUE;
1647 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1648 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1650 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1653 /* 4. Win2000+: Recently used */
1654 if (!handledPath && win2000plus) {
1655 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1656 fodInfos->initdir[0] = '\0';
1658 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1660 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1661 handledPath = TRUE;
1662 }else{
1663 MemFree(fodInfos->initdir);
1664 fodInfos->initdir = NULL;
1668 /* 5. win98+ and win2000+ if any files of specified filter types in
1669 current directory, use it */
1670 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1672 LPCWSTR lpstrPos = fodInfos->filter;
1673 WIN32_FIND_DATAW FindFileData;
1674 HANDLE hFind;
1676 while (1)
1678 /* filter is a list... title\0ext\0......\0\0 */
1680 /* Skip the title */
1681 if(! *lpstrPos) break; /* end */
1682 lpstrPos += lstrlenW(lpstrPos) + 1;
1684 /* See if any files exist in the current dir with this extension */
1685 if(! *lpstrPos) break; /* end */
1687 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1689 if (hFind == INVALID_HANDLE_VALUE) {
1690 /* None found - continue search */
1691 lpstrPos += lstrlenW(lpstrPos) + 1;
1693 } else {
1695 MemFree(fodInfos->initdir);
1696 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1697 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1699 handledPath = TRUE;
1700 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1701 debugstr_w(lpstrPos));
1702 FindClose(hFind);
1703 break;
1708 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1709 if (!handledPath && (win2000plus || win98plus)) {
1710 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1712 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1714 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1716 /* last fallback */
1717 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1718 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1719 } else {
1720 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1722 } else {
1723 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1725 handledPath = TRUE;
1726 } else if (!handledPath) {
1727 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1728 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1729 handledPath = TRUE;
1730 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1733 SetFocus( fodInfos->DlgInfos.hwndFileName );
1734 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1736 /* Must the open as read only check box be checked ?*/
1737 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1739 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1742 /* Must the open as read only check box be hidden? */
1743 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1745 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1746 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1749 /* Must the help button be hidden? */
1750 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1752 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1753 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1756 /* change Open to Save */
1757 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1759 WCHAR buf[16];
1760 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1761 SetDlgItemTextW(hwnd, IDOK, buf);
1762 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1763 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1766 /* Initialize the filter combo box */
1767 FILEDLG95_FILETYPE_Init(hwnd);
1769 return 0;
1772 /***********************************************************************
1773 * FILEDLG95_ResizeControls
1775 * WM_INITDIALOG message handler (after hook notification)
1777 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1779 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1781 if (fodInfos->DlgInfos.hwndCustomDlg)
1783 RECT rc;
1784 UINT flags = SWP_NOACTIVATE;
1786 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1787 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1789 /* resize the custom dialog to the parent size */
1790 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1791 GetClientRect(hwnd, &rc);
1792 else
1794 /* our own fake template is zero sized and doesn't have children, so
1795 * there is no need to resize it. Picasa depends on it.
1797 flags |= SWP_NOSIZE;
1798 SetRectEmpty(&rc);
1800 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1801 0, 0, rc.right, rc.bottom, flags);
1803 else
1805 /* Resize the height; if opened as read-only, checkbox and help button are
1806 * hidden and we are not using a custom template nor a customDialog
1808 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1809 (!(fodInfos->ofnInfos->Flags &
1810 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1812 RECT rectDlg, rectHelp, rectCancel;
1813 GetWindowRect(hwnd, &rectDlg);
1814 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1815 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1816 /* subtract the height of the help button plus the space between the help
1817 * button and the cancel button to the height of the dialog
1819 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1820 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1821 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1824 return TRUE;
1827 /***********************************************************************
1828 * FILEDLG95_FillControls
1830 * WM_INITDIALOG message handler (after hook notification)
1832 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1834 LPITEMIDLIST pidlItemId = NULL;
1836 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1838 TRACE("dir=%s file=%s\n",
1839 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1841 /* Get the initial directory pidl */
1843 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1845 WCHAR path[MAX_PATH];
1847 GetCurrentDirectoryW(MAX_PATH,path);
1848 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1851 /* Initialise shell objects */
1852 FILEDLG95_SHELL_Init(hwnd);
1854 /* Initialize the Look In combo box */
1855 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1857 /* Browse to the initial directory */
1858 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1860 /* Free pidlItem memory */
1861 COMDLG32_SHFree(pidlItemId);
1863 return TRUE;
1865 /***********************************************************************
1866 * FILEDLG95_Clean
1868 * Regroups all the cleaning functions of the filedlg
1870 void FILEDLG95_Clean(HWND hwnd)
1872 FILEDLG95_FILETYPE_Clean(hwnd);
1873 FILEDLG95_LOOKIN_Clean(hwnd);
1874 FILEDLG95_SHELL_Clean(hwnd);
1876 /***********************************************************************
1877 * FILEDLG95_OnWMCommand
1879 * WM_COMMAND message handler
1881 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1883 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1884 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1885 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1887 switch(wID)
1889 /* OK button */
1890 case IDOK:
1891 FILEDLG95_OnOpen(hwnd);
1892 break;
1893 /* Cancel button */
1894 case IDCANCEL:
1895 FILEDLG95_Clean(hwnd);
1896 EndDialog(hwnd, FALSE);
1897 break;
1898 /* Filetype combo box */
1899 case IDC_FILETYPE:
1900 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1901 break;
1902 /* LookIn combo box */
1903 case IDC_LOOKIN:
1904 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1905 break;
1907 /* --- toolbar --- */
1908 /* Up folder button */
1909 case FCIDM_TB_UPFOLDER:
1910 FILEDLG95_SHELL_UpFolder(hwnd);
1911 break;
1912 /* New folder button */
1913 case FCIDM_TB_NEWFOLDER:
1914 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1915 break;
1916 /* List option button */
1917 case FCIDM_TB_SMALLICON:
1918 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1919 break;
1920 /* Details option button */
1921 case FCIDM_TB_REPORTVIEW:
1922 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1923 break;
1924 /* Details option button */
1925 case FCIDM_TB_DESKTOP:
1926 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1927 break;
1929 case edt1:
1930 case cmb13:
1931 break;
1934 /* Do not use the listview selection anymore */
1935 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1936 return 0;
1939 /***********************************************************************
1940 * FILEDLG95_OnWMGetIShellBrowser
1942 * WM_GETISHELLBROWSER message handler
1944 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1946 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1948 TRACE("\n");
1950 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1952 return TRUE;
1956 /***********************************************************************
1957 * FILEDLG95_SendFileOK
1959 * Sends the CDN_FILEOK notification if required
1961 * RETURNS
1962 * TRUE if the dialog should close
1963 * FALSE if the dialog should not be closed
1965 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1967 /* ask the hook if we can close */
1968 if(IsHooked(fodInfos))
1970 LRESULT retval = 0;
1972 TRACE("---\n");
1973 /* First send CDN_FILEOK as MSDN doc says */
1974 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1975 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1976 if( retval)
1978 TRACE("canceled\n");
1979 return FALSE;
1982 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1983 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1984 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1985 if( retval)
1987 TRACE("canceled\n");
1988 return FALSE;
1991 return TRUE;
1994 /***********************************************************************
1995 * FILEDLG95_OnOpenMultipleFiles
1997 * Handles the opening of multiple files.
1999 * FIXME
2000 * check destination buffer size
2002 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
2004 WCHAR lpstrPathSpec[MAX_PATH] = {0};
2005 UINT nCount, nSizePath;
2006 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2008 TRACE("\n");
2010 if(fodInfos->unicode)
2012 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2013 ofn->lpstrFile[0] = '\0';
2015 else
2017 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
2018 ofn->lpstrFile[0] = '\0';
2021 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
2023 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2024 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
2025 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
2027 LPWSTR lpstrTemp = lpstrFileList;
2029 for ( nCount = 0; nCount < nFileCount; nCount++ )
2031 LPITEMIDLIST pidl;
2033 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
2034 if (!pidl)
2036 WCHAR lpstrNotFound[100];
2037 WCHAR lpstrMsg[100];
2038 WCHAR tmp[400];
2039 static const WCHAR nl[] = {'\n',0};
2041 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
2042 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
2044 lstrcpyW(tmp, lpstrTemp);
2045 lstrcatW(tmp, nl);
2046 lstrcatW(tmp, lpstrNotFound);
2047 lstrcatW(tmp, nl);
2048 lstrcatW(tmp, lpstrMsg);
2050 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
2051 return FALSE;
2054 /* move to the next file in the list of files */
2055 lpstrTemp += lstrlenW(lpstrTemp) + 1;
2056 COMDLG32_SHFree(pidl);
2060 nSizePath = lstrlenW(lpstrPathSpec) + 1;
2061 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2063 /* For "oldstyle" dialog the components have to
2064 be separated by blanks (not '\0'!) and short
2065 filenames have to be used! */
2066 FIXME("Components have to be separated by blanks\n");
2068 if(fodInfos->unicode)
2070 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2071 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2072 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2074 else
2076 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2078 if (ofn->lpstrFile != NULL)
2080 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2081 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2082 if (ofn->nMaxFile > nSizePath)
2084 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2085 ofn->lpstrFile + nSizePath,
2086 ofn->nMaxFile - nSizePath, NULL, NULL);
2091 fodInfos->ofnInfos->nFileOffset = nSizePath;
2092 fodInfos->ofnInfos->nFileExtension = 0;
2094 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2095 return FALSE;
2097 /* clean and exit */
2098 FILEDLG95_Clean(hwnd);
2099 return EndDialog(hwnd,TRUE);
2102 /* Returns the 'slot name' of the given module_name in the registry's
2103 * most-recently-used list. This will be an ASCII value in the
2104 * range ['a','z'). Returns zero on error.
2106 * The slot's value in the registry has the form:
2107 * module_name\0mru_path\0
2109 * If stored_path is given, then stored_path will contain the path name
2110 * stored in the registry's MRU list for the given module_name.
2112 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2113 * MRU list key for the given module_name.
2115 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2117 WCHAR mru_list[32], *cur_mru_slot;
2118 BOOL taken[25] = {0};
2119 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2120 HKEY hkey_tmp, *hkey;
2121 LONG ret;
2123 if(hkey_ret)
2124 hkey = hkey_ret;
2125 else
2126 hkey = &hkey_tmp;
2128 if(stored_path)
2129 *stored_path = '\0';
2131 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2132 if(ret){
2133 WARN("Unable to create MRU key: %d\n", ret);
2134 return 0;
2137 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2138 (LPBYTE)mru_list, &mru_list_size);
2139 if(ret || key_type != REG_SZ){
2140 if(ret == ERROR_FILE_NOT_FOUND)
2141 return 'a';
2143 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2144 RegCloseKey(*hkey);
2145 return 0;
2148 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2149 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2150 DWORD value_data_size = sizeof(value_data);
2152 *value_name = *cur_mru_slot;
2154 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2155 &key_type, (LPBYTE)value_data, &value_data_size);
2156 if(ret || key_type != REG_BINARY){
2157 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2158 continue;
2161 if(!strcmpiW(module_name, value_data)){
2162 if(!hkey_ret)
2163 RegCloseKey(*hkey);
2164 if(stored_path)
2165 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2166 return *value_name;
2170 if(!hkey_ret)
2171 RegCloseKey(*hkey);
2173 /* the module name isn't in the registry, so find the next open slot */
2174 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2175 taken[*cur_mru_slot - 'a'] = TRUE;
2176 for(i = 0; i < 25; ++i){
2177 if(!taken[i])
2178 return i + 'a';
2181 /* all slots are taken, so return the last one in MRUList */
2182 --cur_mru_slot;
2183 return *cur_mru_slot;
2186 /* save the given filename as most-recently-used path for this module */
2187 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2189 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2190 LONG ret;
2191 HKEY hkey;
2193 /* get the current executable's name */
2194 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2195 WARN("GotModuleFileName failed: %d\n", GetLastError());
2196 return;
2198 module_name = strrchrW(module_path, '\\');
2199 if(!module_name)
2200 module_name = module_path;
2201 else
2202 module_name += 1;
2204 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2205 if(!slot)
2206 return;
2207 *slot_name = slot;
2209 { /* update the slot's info */
2210 WCHAR *path_ends, *final;
2211 DWORD path_len, final_len;
2213 /* use only the path segment of `filename' */
2214 path_ends = strrchrW(filename, '\\');
2215 path_len = path_ends - filename;
2217 final_len = path_len + lstrlenW(module_name) + 2;
2219 final = MemAlloc(final_len * sizeof(WCHAR));
2220 if(!final)
2221 return;
2222 lstrcpyW(final, module_name);
2223 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2224 final[final_len-1] = '\0';
2226 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2227 final_len * sizeof(WCHAR));
2228 if(ret){
2229 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2230 MemFree(final);
2231 RegCloseKey(hkey);
2232 return;
2235 MemFree(final);
2238 { /* update MRUList value */
2239 WCHAR old_mru_list[32], new_mru_list[32];
2240 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2241 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2243 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2244 (LPBYTE)old_mru_list, &mru_list_size);
2245 if(ret || key_type != REG_SZ){
2246 if(ret == ERROR_FILE_NOT_FOUND){
2247 new_mru_list[0] = slot;
2248 new_mru_list[1] = '\0';
2249 }else{
2250 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2251 RegCloseKey(hkey);
2252 return;
2254 }else{
2255 /* copy old list data over so that the new slot is at the start
2256 * of the list */
2257 *new_mru_slot++ = slot;
2258 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2259 if(*old_mru_slot != slot)
2260 *new_mru_slot++ = *old_mru_slot;
2262 *new_mru_slot = '\0';
2265 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2266 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2267 if(ret){
2268 WARN("Error saving MRUList data: %d\n", ret);
2269 RegCloseKey(hkey);
2270 return;
2275 /* load the most-recently-used path for this module */
2276 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2278 WCHAR module_path[MAX_PATH], *module_name;
2280 /* get the current executable's name */
2281 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2282 WARN("GotModuleFileName failed: %d\n", GetLastError());
2283 return;
2285 module_name = strrchrW(module_path, '\\');
2286 if(!module_name)
2287 module_name = module_path;
2288 else
2289 module_name += 1;
2291 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2292 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2295 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2297 WCHAR strMsgTitle[MAX_PATH];
2298 WCHAR strMsgText [MAX_PATH];
2299 if (idCaption)
2300 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2301 else
2302 strMsgTitle[0] = '\0';
2303 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2304 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2307 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2308 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2310 int nOpenAction = defAction;
2311 LPWSTR lpszTemp, lpszTemp1;
2312 LPITEMIDLIST pidl = NULL;
2313 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2315 /* check for invalid chars */
2316 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2318 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2319 return FALSE;
2322 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2324 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2325 while (lpszTemp1)
2327 LPSHELLFOLDER lpsfChild;
2328 WCHAR lpwstrTemp[MAX_PATH];
2329 DWORD dwEaten, dwAttributes;
2330 LPWSTR p;
2332 lstrcpyW(lpwstrTemp, lpszTemp);
2333 p = PathFindNextComponentW(lpwstrTemp);
2335 if (!p) break; /* end of path */
2337 *p = 0;
2338 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2340 /* There are no wildcards when OFN_NOVALIDATE is set */
2341 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2343 static const WCHAR wszWild[] = { '*', '?', 0 };
2344 /* if the last element is a wildcard do a search */
2345 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2347 nOpenAction = ONOPEN_SEARCH;
2348 break;
2351 lpszTemp1 = lpszTemp;
2353 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2355 /* append a backslash to drive letters */
2356 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2357 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2358 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2360 PathAddBackslashW(lpwstrTemp);
2363 dwAttributes = SFGAO_FOLDER;
2364 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2366 /* the path component is valid, we have a pidl of the next path component */
2367 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2368 if(dwAttributes & SFGAO_FOLDER)
2370 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2372 ERR("bind to failed\n"); /* should not fail */
2373 break;
2375 IShellFolder_Release(*ppsf);
2376 *ppsf = lpsfChild;
2377 lpsfChild = NULL;
2379 else
2381 TRACE("value\n");
2383 /* end dialog, return value */
2384 nOpenAction = ONOPEN_OPEN;
2385 break;
2387 COMDLG32_SHFree(pidl);
2388 pidl = NULL;
2390 else if (!(flags & OFN_NOVALIDATE))
2392 if(*lpszTemp || /* points to trailing null for last path element */
2393 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2395 if(flags & OFN_PATHMUSTEXIST)
2397 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2398 break;
2401 else
2403 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2405 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2406 break;
2409 /* change to the current folder */
2410 nOpenAction = ONOPEN_OPEN;
2411 break;
2413 else
2415 nOpenAction = ONOPEN_OPEN;
2416 break;
2419 if(pidl) COMDLG32_SHFree(pidl);
2421 return nOpenAction;
2424 /***********************************************************************
2425 * FILEDLG95_OnOpen
2427 * Ok button WM_COMMAND message handler
2429 * If the function succeeds, the return value is nonzero.
2431 BOOL FILEDLG95_OnOpen(HWND hwnd)
2433 LPWSTR lpstrFileList;
2434 UINT nFileCount = 0;
2435 UINT sizeUsed = 0;
2436 BOOL ret = TRUE;
2437 WCHAR lpstrPathAndFile[MAX_PATH];
2438 LPSHELLFOLDER lpsf = NULL;
2439 int nOpenAction;
2440 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2442 TRACE("hwnd=%p\n", hwnd);
2444 /* try to browse the selected item */
2445 if(BrowseSelectedFolder(hwnd))
2446 return FALSE;
2448 /* get the files from the edit control */
2449 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2451 if(nFileCount == 0)
2452 return FALSE;
2454 if(nFileCount > 1)
2456 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2457 goto ret;
2460 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2463 Step 1: Build a complete path name from the current folder and
2464 the filename or path in the edit box.
2465 Special cases:
2466 - the path in the edit box is a root path
2467 (with or without drive letter)
2468 - the edit box contains ".." (or a path with ".." in it)
2471 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2472 MemFree(lpstrFileList);
2475 Step 2: here we have a cleaned up path
2477 We have to parse the path step by step to see if we have to browse
2478 to a folder if the path points to a directory or the last
2479 valid element is a directory.
2481 valid variables:
2482 lpstrPathAndFile: cleaned up path
2485 if (nFileCount &&
2486 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2487 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2488 nOpenAction = ONOPEN_OPEN;
2489 else
2490 nOpenAction = ONOPEN_BROWSE;
2492 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2493 fodInfos->ofnInfos->Flags,
2494 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2495 nOpenAction);
2496 if(!nOpenAction)
2497 goto ret;
2500 Step 3: here we have a cleaned up and validated path
2502 valid variables:
2503 lpsf: ShellFolder bound to the rightmost valid path component
2504 lpstrPathAndFile: cleaned up path
2505 nOpenAction: action to do
2507 TRACE("end validate sf=%p\n", lpsf);
2509 switch(nOpenAction)
2511 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2512 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2514 int iPos;
2515 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2516 DWORD len;
2518 /* replace the current filter */
2519 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2520 len = lstrlenW(lpszTemp)+1;
2521 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2522 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2524 /* set the filter cb to the extension when possible */
2525 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2526 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2528 /* fall through */
2529 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2530 TRACE("ONOPEN_BROWSE\n");
2532 IPersistFolder2 * ppf2;
2533 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2535 LPITEMIDLIST pidlCurrent;
2536 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2537 IPersistFolder2_Release(ppf2);
2538 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2540 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2541 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2543 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2544 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2547 else if( nOpenAction == ONOPEN_SEARCH )
2549 if (fodInfos->Shell.FOIShellView)
2550 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2552 COMDLG32_SHFree(pidlCurrent);
2553 if (filename_is_edit( fodInfos ))
2554 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2555 else
2557 HWND hwnd;
2559 hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2560 SendMessageW(hwnd, EM_SETSEL, 0, -1);
2564 ret = FALSE;
2565 break;
2566 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2567 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2569 WCHAR *ext = NULL;
2571 /* update READONLY check box flag */
2572 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2573 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2574 else
2575 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2577 /* Attach the file extension with file name*/
2578 ext = PathFindExtensionW(lpstrPathAndFile);
2579 if (! *ext && fodInfos->defext)
2581 /* if no extension is specified with file name, then */
2582 /* attach the extension from file filter or default one */
2584 WCHAR *filterExt = NULL;
2585 LPWSTR lpstrFilter = NULL;
2586 static const WCHAR szwDot[] = {'.',0};
2587 int PathLength = lstrlenW(lpstrPathAndFile);
2589 /*Get the file extension from file type filter*/
2590 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2591 fodInfos->ofnInfos->nFilterIndex-1);
2593 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2595 WCHAR* filterSearchIndex;
2596 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2597 strcpyW(filterExt, lpstrFilter);
2599 /* if a semicolon-separated list of file extensions was given, do not include the
2600 semicolon or anything after it in the extension.
2601 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2602 filterSearchIndex = strchrW(filterExt, ';');
2603 if (filterSearchIndex)
2605 filterSearchIndex[0] = '\0';
2608 /* find the file extension by searching for the first dot in filterExt */
2609 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2610 /* if the extension is invalid or contains a glob, ignore it */
2611 filterSearchIndex = strchrW(filterExt, '.');
2612 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2614 strcpyW(filterExt, filterSearchIndex);
2616 else
2618 HeapFree(GetProcessHeap(), 0, filterExt);
2619 filterExt = NULL;
2623 if (!filterExt)
2625 /* use the default file extension */
2626 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2627 strcpyW(filterExt, fodInfos->defext);
2630 if (*filterExt) /* ignore filterExt="" */
2632 /* Attach the dot*/
2633 lstrcatW(lpstrPathAndFile, szwDot);
2634 /* Attach the extension */
2635 lstrcatW(lpstrPathAndFile, filterExt);
2638 HeapFree(GetProcessHeap(), 0, filterExt);
2640 /* In Open dialog: if file does not exist try without extension */
2641 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2642 lpstrPathAndFile[PathLength] = '\0';
2644 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2645 if (*ext)
2646 ext++;
2647 if (!lstrcmpiW(fodInfos->defext, ext))
2648 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2649 else
2650 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2653 /* In Save dialog: check if the file already exists */
2654 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2655 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2656 && PathFileExistsW(lpstrPathAndFile))
2658 WCHAR lpstrOverwrite[100];
2659 int answer;
2661 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2662 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2663 MB_YESNO | MB_ICONEXCLAMATION);
2664 if (answer == IDNO || answer == IDCANCEL)
2666 ret = FALSE;
2667 goto ret;
2671 /* In Open dialog: check if it should be created if it doesn't exist */
2672 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2673 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2674 && !PathFileExistsW(lpstrPathAndFile))
2676 WCHAR lpstrCreate[100];
2677 int answer;
2679 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2680 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2681 MB_YESNO | MB_ICONEXCLAMATION);
2682 if (answer == IDNO || answer == IDCANCEL)
2684 ret = FALSE;
2685 goto ret;
2689 /* Check that the size of the file does not exceed buffer size.
2690 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2691 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2692 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2695 /* fill destination buffer */
2696 if (fodInfos->ofnInfos->lpstrFile)
2698 if(fodInfos->unicode)
2700 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2702 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2703 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2704 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2706 else
2708 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2710 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2711 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2712 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2713 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2717 if(fodInfos->unicode)
2719 LPWSTR lpszTemp;
2721 /* set filename offset */
2722 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2723 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2725 /* set extension offset */
2726 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2727 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2729 else
2731 LPSTR lpszTemp;
2732 CHAR tempFileA[MAX_PATH];
2734 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2735 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2736 tempFileA, sizeof(tempFileA), NULL, NULL);
2738 /* set filename offset */
2739 lpszTemp = PathFindFileNameA(tempFileA);
2740 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2742 /* set extension offset */
2743 lpszTemp = PathFindExtensionA(tempFileA);
2744 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2747 /* set the lpstrFileTitle */
2748 if(fodInfos->ofnInfos->lpstrFileTitle)
2750 LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2751 if(fodInfos->unicode)
2753 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2754 lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2756 else
2758 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2759 WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2760 ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2764 /* copy currently selected filter to lpstrCustomFilter */
2765 if (fodInfos->ofnInfos->lpstrCustomFilter)
2767 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2768 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2769 NULL, 0, NULL, NULL);
2770 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2772 LPSTR s = ofn->lpstrCustomFilter;
2773 s += strlen(ofn->lpstrCustomFilter)+1;
2774 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2775 s, len, NULL, NULL);
2780 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2781 goto ret;
2783 FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2785 TRACE("close\n");
2786 FILEDLG95_Clean(hwnd);
2787 ret = EndDialog(hwnd, TRUE);
2789 else
2791 WORD size;
2793 size = lstrlenW(lpstrPathAndFile) + 1;
2794 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2795 size += 1;
2796 /* return needed size in first two bytes of lpstrFile */
2797 if(fodInfos->ofnInfos->lpstrFile)
2798 *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2799 FILEDLG95_Clean(hwnd);
2800 ret = EndDialog(hwnd, FALSE);
2801 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2804 break;
2807 ret:
2808 if(lpsf) IShellFolder_Release(lpsf);
2809 return ret;
2812 /***********************************************************************
2813 * FILEDLG95_SHELL_Init
2815 * Initialisation of the shell objects
2817 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2819 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2821 TRACE("\n");
2824 * Initialisation of the FileOpenDialogInfos structure
2827 /* Shell */
2829 /*ShellInfos */
2830 fodInfos->ShellInfos.hwndOwner = hwnd;
2832 /* Disable multi-select if flag not set */
2833 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2835 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2837 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2838 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2840 /* Construct the IShellBrowser interface */
2841 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2843 return NOERROR;
2846 /***********************************************************************
2847 * FILEDLG95_SHELL_ExecuteCommand
2849 * Change the folder option and refresh the view
2850 * If the function succeeds, the return value is nonzero.
2852 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2854 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2855 IContextMenu * pcm;
2857 TRACE("(%p,%p)\n", hwnd, lpVerb);
2859 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2860 SVGIO_BACKGROUND,
2861 &IID_IContextMenu,
2862 (LPVOID*)&pcm)))
2864 CMINVOKECOMMANDINFO ci;
2865 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2866 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2867 ci.lpVerb = lpVerb;
2868 ci.hwnd = hwnd;
2870 IContextMenu_InvokeCommand(pcm, &ci);
2871 IContextMenu_Release(pcm);
2874 return FALSE;
2877 /***********************************************************************
2878 * FILEDLG95_SHELL_UpFolder
2880 * Browse to the specified object
2881 * If the function succeeds, the return value is nonzero.
2883 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2885 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2887 TRACE("\n");
2889 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2890 NULL,
2891 SBSP_PARENT)))
2893 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2894 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2895 return TRUE;
2897 return FALSE;
2900 /***********************************************************************
2901 * FILEDLG95_SHELL_BrowseToDesktop
2903 * Browse to the Desktop
2904 * If the function succeeds, the return value is nonzero.
2906 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2908 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2909 LPITEMIDLIST pidl;
2910 HRESULT hres;
2912 TRACE("\n");
2914 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2915 hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2916 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2917 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2918 COMDLG32_SHFree(pidl);
2919 return SUCCEEDED(hres);
2921 /***********************************************************************
2922 * FILEDLG95_SHELL_Clean
2924 * Cleans the memory used by shell objects
2926 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2928 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2930 TRACE("\n");
2932 COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2934 /* clean Shell interfaces */
2935 if (fodInfos->Shell.FOIShellView)
2937 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2938 IShellView_Release(fodInfos->Shell.FOIShellView);
2940 IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2941 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2942 if (fodInfos->Shell.FOIDataObject)
2943 IDataObject_Release(fodInfos->Shell.FOIDataObject);
2946 /***********************************************************************
2947 * FILEDLG95_FILETYPE_Init
2949 * Initialisation of the file type combo box
2951 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2953 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2954 int nFilters = 0; /* number of filters */
2955 int nFilterIndexCB;
2957 TRACE("\n");
2959 if(fodInfos->customfilter)
2961 /* customfilter has one entry... title\0ext\0
2962 * Set first entry of combo box item with customfilter
2964 LPWSTR lpstrExt;
2965 LPCWSTR lpstrPos = fodInfos->customfilter;
2967 /* Get the title */
2968 lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2970 /* Copy the extensions */
2971 if (! *lpstrPos) return E_FAIL; /* malformed filter */
2972 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2973 lstrcpyW(lpstrExt,lpstrPos);
2975 /* Add the item at the end of the combo */
2976 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2977 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2978 nFilters++;
2980 if(fodInfos->filter)
2982 LPCWSTR lpstrPos = fodInfos->filter;
2984 for(;;)
2986 /* filter is a list... title\0ext\0......\0\0
2987 * Set the combo item text to the title and the item data
2988 * to the ext
2990 LPCWSTR lpstrDisplay;
2991 LPWSTR lpstrExt;
2993 /* Get the title */
2994 if(! *lpstrPos) break; /* end */
2995 lpstrDisplay = lpstrPos;
2996 lpstrPos += lstrlenW(lpstrPos) + 1;
2998 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
3000 nFilters++;
3002 /* Copy the extensions */
3003 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
3004 lstrcpyW(lpstrExt,lpstrPos);
3005 lpstrPos += lstrlenW(lpstrPos) + 1;
3007 /* Add the item at the end of the combo */
3008 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
3010 /* malformed filters are added anyway... */
3011 if (!*lpstrExt) break;
3016 * Set the current filter to the one specified
3017 * in the initialisation structure
3019 if (fodInfos->filter || fodInfos->customfilter)
3021 LPWSTR lpstrFilter;
3023 /* Check to make sure our index isn't out of bounds. */
3024 if ( fodInfos->ofnInfos->nFilterIndex >
3025 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
3026 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
3028 /* set default filter index */
3029 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
3030 fodInfos->ofnInfos->nFilterIndex = 1;
3032 /* calculate index of Combo Box item */
3033 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
3034 if (fodInfos->customfilter == NULL)
3035 nFilterIndexCB--;
3037 /* Set the current index selection. */
3038 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
3040 /* Get the corresponding text string from the combo box. */
3041 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3042 nFilterIndexCB);
3044 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
3045 lpstrFilter = NULL;
3047 if(lpstrFilter)
3049 DWORD len;
3050 CharLowerW(lpstrFilter); /* lowercase */
3051 len = lstrlenW(lpstrFilter)+1;
3052 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3053 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3055 } else
3056 fodInfos->ofnInfos->nFilterIndex = 0;
3057 return S_OK;
3060 /***********************************************************************
3061 * FILEDLG95_FILETYPE_OnCommand
3063 * WM_COMMAND of the file type combo box
3064 * If the function succeeds, the return value is nonzero.
3066 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
3068 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3070 switch(wNotifyCode)
3072 case CBN_SELENDOK:
3074 LPWSTR lpstrFilter;
3076 /* Get the current item of the filetype combo box */
3077 int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
3079 /* set the current filter index */
3080 fodInfos->ofnInfos->nFilterIndex = iItem +
3081 (fodInfos->customfilter == NULL ? 1 : 0);
3083 /* Set the current filter with the current selection */
3084 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3086 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3087 iItem);
3088 if((INT_PTR)lpstrFilter != CB_ERR)
3090 DWORD len;
3091 CharLowerW(lpstrFilter); /* lowercase */
3092 len = lstrlenW(lpstrFilter)+1;
3093 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3094 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3095 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3096 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3099 /* Refresh the actual view to display the included items*/
3100 if (fodInfos->Shell.FOIShellView)
3101 IShellView_Refresh(fodInfos->Shell.FOIShellView);
3104 return FALSE;
3106 /***********************************************************************
3107 * FILEDLG95_FILETYPE_SearchExt
3109 * searches for an extension in the filetype box
3111 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3113 int i, iCount = CBGetCount(hwnd);
3115 TRACE("%s\n", debugstr_w(lpstrExt));
3117 if(iCount != CB_ERR)
3119 for(i=0;i<iCount;i++)
3121 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3122 return i;
3125 return -1;
3128 /***********************************************************************
3129 * FILEDLG95_FILETYPE_Clean
3131 * Clean the memory used by the filetype combo box
3133 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3135 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3136 int iPos;
3137 int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3139 TRACE("\n");
3141 /* Delete each string of the combo and their associated data */
3142 if(iCount != CB_ERR)
3144 for(iPos = iCount-1;iPos>=0;iPos--)
3146 MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3147 CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3150 /* Current filter */
3151 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3155 /***********************************************************************
3156 * FILEDLG95_LOOKIN_Init
3158 * Initialisation of the look in combo box
3161 /* Small helper function, to determine if the unixfs shell extension is rooted
3162 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3164 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3165 HKEY hKey;
3166 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3167 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3168 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3169 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3170 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3171 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3172 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3174 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3175 return FALSE;
3177 RegCloseKey(hKey);
3178 return TRUE;
3181 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3183 IShellFolder *psfRoot, *psfDrives;
3184 IEnumIDList *lpeRoot, *lpeDrives;
3185 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3186 HDC hdc;
3187 TEXTMETRICW tm;
3188 LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3190 TRACE("\n");
3192 liInfos->iMaxIndentation = 0;
3194 SetPropA(hwndCombo, LookInInfosStr, liInfos);
3196 hdc = GetDC( hwndCombo );
3197 SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3198 GetTextMetricsW( hdc, &tm );
3199 ReleaseDC( hwndCombo, hdc );
3201 /* set item height for both text field and listbox */
3202 CBSetItemHeight( hwndCombo, -1, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3203 CBSetItemHeight( hwndCombo, 0, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3205 /* Turn on the extended UI for the combo box like Windows does */
3206 CBSetExtendedUI(hwndCombo, TRUE);
3208 /* Initialise data of Desktop folder */
3209 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3210 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3211 COMDLG32_SHFree(pidlTmp);
3213 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3215 SHGetDesktopFolder(&psfRoot);
3217 if (psfRoot)
3219 /* enumerate the contents of the desktop */
3220 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3222 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3224 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3226 /* If the unixfs extension is rooted, we don't expand the drives by default */
3227 if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3229 /* special handling for CSIDL_DRIVES */
3230 if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3232 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3234 /* enumerate the drives */
3235 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3237 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3239 pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3240 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3241 COMDLG32_SHFree(pidlAbsTmp);
3242 COMDLG32_SHFree(pidlTmp1);
3244 IEnumIDList_Release(lpeDrives);
3246 IShellFolder_Release(psfDrives);
3251 COMDLG32_SHFree(pidlTmp);
3253 IEnumIDList_Release(lpeRoot);
3255 IShellFolder_Release(psfRoot);
3258 COMDLG32_SHFree(pidlDrives);
3261 /***********************************************************************
3262 * FILEDLG95_LOOKIN_DrawItem
3264 * WM_DRAWITEM message handler
3266 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3268 COLORREF crWin = GetSysColor(COLOR_WINDOW);
3269 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3270 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3271 RECT rectText;
3272 RECT rectIcon;
3273 SHFILEINFOW sfi;
3274 HIMAGELIST ilItemImage;
3275 int iIndentation;
3276 TEXTMETRICW tm;
3277 LPSFOLDER tmpFolder;
3278 UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3279 UINT icon_width, icon_height;
3281 TRACE("\n");
3283 if(pDIStruct->itemID == -1)
3284 return 0;
3286 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3287 pDIStruct->itemID)))
3288 return 0;
3291 icon_width = GetSystemMetrics(SM_CXICON);
3292 icon_height = GetSystemMetrics(SM_CYICON);
3293 if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3295 icon_width = GetSystemMetrics(SM_CXSMICON);
3296 icon_height = GetSystemMetrics(SM_CYSMICON);
3297 shgfi_flags |= SHGFI_SMALLICON;
3300 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3301 0, &sfi, sizeof (sfi), shgfi_flags );
3303 /* Is this item selected ? */
3304 if(pDIStruct->itemState & ODS_SELECTED)
3306 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3307 SetBkColor(pDIStruct->hDC,crHighLight);
3308 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3310 else
3312 SetTextColor(pDIStruct->hDC,crText);
3313 SetBkColor(pDIStruct->hDC,crWin);
3314 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3317 /* Do not indent item if drawing in the edit of the combo */
3318 if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3319 iIndentation = 0;
3320 else
3321 iIndentation = tmpFolder->m_iIndent;
3323 /* Draw text and icon */
3325 /* Initialise the icon display area */
3326 rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3327 rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3328 rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3329 rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3331 /* Initialise the text display area */
3332 GetTextMetricsW(pDIStruct->hDC, &tm);
3333 rectText.left = rectIcon.right;
3334 rectText.top =
3335 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3336 rectText.right = pDIStruct->rcItem.right;
3337 rectText.bottom =
3338 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3340 /* Draw the icon from the image list */
3341 ImageList_Draw(ilItemImage,
3342 sfi.iIcon,
3343 pDIStruct->hDC,
3344 rectIcon.left,
3345 rectIcon.top,
3346 ILD_TRANSPARENT );
3348 /* Draw the associated text */
3349 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3350 return NOERROR;
3353 /***********************************************************************
3354 * FILEDLG95_LOOKIN_OnCommand
3356 * LookIn combo box WM_COMMAND message handler
3357 * If the function succeeds, the return value is nonzero.
3359 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3361 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3363 TRACE("%p\n", fodInfos);
3365 switch(wNotifyCode)
3367 case CBN_SELENDOK:
3369 LPSFOLDER tmpFolder;
3370 int iItem;
3372 iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3374 if( iItem == CB_ERR) return FALSE;
3376 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3377 iItem)))
3378 return FALSE;
3381 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3382 tmpFolder->pidlItem,
3383 SBSP_ABSOLUTE)))
3385 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3386 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3387 return TRUE;
3389 break;
3393 return FALSE;
3396 /***********************************************************************
3397 * FILEDLG95_LOOKIN_AddItem
3399 * Adds an absolute pidl item to the lookin combo box
3400 * returns the index of the inserted item
3402 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3404 LPITEMIDLIST pidlNext;
3405 SHFILEINFOW sfi;
3406 SFOLDER *tmpFolder;
3407 LookInInfos *liInfos;
3409 TRACE("%08x\n", iInsertId);
3411 if(!pidl)
3412 return -1;
3414 if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3415 return -1;
3417 tmpFolder = MemAlloc(sizeof(SFOLDER));
3418 tmpFolder->m_iIndent = 0;
3420 /* Calculate the indentation of the item in the lookin*/
3421 pidlNext = pidl;
3422 while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3424 tmpFolder->m_iIndent++;
3427 tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3429 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3430 liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3432 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3433 SHGetFileInfoW((LPCWSTR)pidl,
3435 &sfi,
3436 sizeof(sfi),
3437 SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX
3438 | SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3440 TRACE("-- Add %s attr=%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3442 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3444 int iItemID;
3446 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3448 /* Add the item at the end of the list */
3449 if(iInsertId < 0)
3451 iItemID = CBAddString(hwnd,sfi.szDisplayName);
3453 /* Insert the item at the iInsertId position*/
3454 else
3456 iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3459 CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3460 return iItemID;
3463 COMDLG32_SHFree( tmpFolder->pidlItem );
3464 MemFree( tmpFolder );
3465 return -1;
3469 /***********************************************************************
3470 * FILEDLG95_LOOKIN_InsertItemAfterParent
3472 * Insert an item below its parent
3474 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3477 LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3478 int iParentPos;
3480 TRACE("\n");
3482 if (pidl == pidlParent)
3483 return -1;
3485 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3487 if(iParentPos < 0)
3489 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3492 /* Free pidlParent memory */
3493 COMDLG32_SHFree(pidlParent);
3495 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3498 /***********************************************************************
3499 * FILEDLG95_LOOKIN_SelectItem
3501 * Adds an absolute pidl item to the lookin combo box
3502 * returns the index of the inserted item
3504 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3506 int iItemPos;
3507 LookInInfos *liInfos;
3509 TRACE("\n");
3511 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3513 liInfos = GetPropA(hwnd,LookInInfosStr);
3515 if(iItemPos < 0)
3517 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3518 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3521 else
3523 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3524 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3526 int iRemovedItem;
3528 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3529 break;
3530 if(iRemovedItem < iItemPos)
3531 iItemPos--;
3535 CBSetCurSel(hwnd,iItemPos);
3536 liInfos->uSelectedItem = iItemPos;
3538 return 0;
3542 /***********************************************************************
3543 * FILEDLG95_LOOKIN_RemoveMostExpandedItem
3545 * Remove the item with an expansion level over iExpansionLevel
3547 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3549 int iItemPos;
3550 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3552 TRACE("\n");
3554 if(liInfos->iMaxIndentation <= 2)
3555 return -1;
3557 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3559 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3560 COMDLG32_SHFree(tmpFolder->pidlItem);
3561 MemFree(tmpFolder);
3562 CBDeleteString(hwnd,iItemPos);
3563 liInfos->iMaxIndentation--;
3565 return iItemPos;
3568 return -1;
3571 /***********************************************************************
3572 * FILEDLG95_LOOKIN_SearchItem
3574 * Search for pidl in the lookin combo box
3575 * returns the index of the found item
3577 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3579 int i = 0;
3580 int iCount = CBGetCount(hwnd);
3582 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3584 if (iCount != CB_ERR)
3586 for(;i<iCount;i++)
3588 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3590 if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3591 return i;
3592 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3593 return i;
3597 return -1;
3600 /***********************************************************************
3601 * FILEDLG95_LOOKIN_Clean
3603 * Clean the memory used by the lookin combo box
3605 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3607 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3608 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3609 int iPos;
3610 int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3612 TRACE("\n");
3614 /* Delete each string of the combo and their associated data */
3615 if (iCount != CB_ERR)
3617 for(iPos = iCount-1;iPos>=0;iPos--)
3619 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3620 COMDLG32_SHFree(tmpFolder->pidlItem);
3621 MemFree(tmpFolder);
3622 CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3626 /* LookInInfos structure */
3627 MemFree(liInfos);
3628 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3631 /***********************************************************************
3632 * get_def_format
3634 * Fill the FORMATETC used in the shell id list
3636 static FORMATETC get_def_format(void)
3638 static CLIPFORMAT cfFormat;
3639 FORMATETC formatetc;
3641 if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA);
3642 formatetc.cfFormat = cfFormat;
3643 formatetc.ptd = 0;
3644 formatetc.dwAspect = DVASPECT_CONTENT;
3645 formatetc.lindex = -1;
3646 formatetc.tymed = TYMED_HGLOBAL;
3647 return formatetc;
3650 /***********************************************************************
3651 * FILEDLG95_FILENAME_FillFromSelection
3653 * fills the edit box from the cached DataObject
3655 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3657 FileOpenDlgInfos *fodInfos;
3658 LPITEMIDLIST pidl;
3659 LPWSTR lpstrAllFiles, lpstrTmp;
3660 UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength;
3661 STGMEDIUM medium;
3662 LPIDA cida;
3663 FORMATETC formatetc = get_def_format();
3665 TRACE("\n");
3666 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3668 if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium)))
3669 return;
3671 cida = GlobalLock(medium.u.hGlobal);
3672 nFileSelected = cida->cidl;
3674 /* Allocate a buffer */
3675 nAllFilesMaxLength = MAX_PATH + 3;
3676 lpstrAllFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nAllFilesMaxLength * sizeof(WCHAR));
3677 if (!lpstrAllFiles)
3678 goto ret;
3680 /* Loop through the selection, handle only files (not folders) */
3681 for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++)
3683 pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]);
3684 if (pidl)
3686 if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl))
3688 if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength)
3690 nAllFilesMaxLength *= 2;
3691 lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR));
3692 if (!lpstrTmp)
3693 goto ret;
3694 lpstrAllFiles = lpstrTmp;
3696 nFiles += 1;
3697 lpstrAllFiles[nAllFilesLength++] = '"';
3698 GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength);
3699 nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength);
3700 nAllFilesLength += nThisFileLength;
3701 lpstrAllFiles[nAllFilesLength++] = '"';
3702 lpstrAllFiles[nAllFilesLength++] = ' ';
3707 if (nFiles != 0)
3709 /* If there's only one file, use the name as-is without quotes */
3710 lpstrTmp = lpstrAllFiles;
3711 if (nFiles == 1)
3713 lpstrTmp += 1;
3714 lpstrTmp[nThisFileLength] = 0;
3716 SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp);
3717 /* Select the file name like Windows does */
3718 if (filename_is_edit(fodInfos))
3719 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3722 ret:
3723 HeapFree(GetProcessHeap(), 0, lpstrAllFiles);
3724 COMCTL32_ReleaseStgMedium(medium);
3728 /* copied from shell32 to avoid linking to it
3729 * Although shell32 is already linked the behaviour of exported StrRetToStrN
3730 * is dependent on whether emulated OS is unicode or not.
3732 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3734 switch (src->uType)
3736 case STRRET_WSTR:
3737 lstrcpynW(dest, src->u.pOleStr, len);
3738 COMDLG32_SHFree(src->u.pOleStr);
3739 break;
3741 case STRRET_CSTR:
3742 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3743 dest[len-1] = 0;
3744 break;
3746 case STRRET_OFFSET:
3747 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3748 dest[len-1] = 0;
3749 break;
3751 default:
3752 FIXME("unknown type %x!\n", src->uType);
3753 if (len) *dest = '\0';
3754 return E_FAIL;
3756 return S_OK;
3759 /***********************************************************************
3760 * FILEDLG95_FILENAME_GetFileNames
3762 * Copies the filenames to a delimited string list.
3764 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3766 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3767 UINT nFileCount = 0; /* number of files */
3768 UINT nStrLen = 0; /* length of string in edit control */
3769 LPWSTR lpstrEdit; /* buffer for string from edit control */
3771 TRACE("\n");
3773 /* get the filenames from the filename control */
3774 nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
3775 lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3776 GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
3778 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3780 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3781 MemFree(lpstrEdit);
3782 return nFileCount;
3786 * DATAOBJECT Helper functions
3789 /***********************************************************************
3790 * COMCTL32_ReleaseStgMedium
3792 * like ReleaseStgMedium from ole32
3794 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3796 if(medium.pUnkForRelease)
3798 IUnknown_Release(medium.pUnkForRelease);
3800 else
3802 GlobalUnlock(medium.u.hGlobal);
3803 GlobalFree(medium.u.hGlobal);
3807 /***********************************************************************
3808 * GetPidlFromDataObject
3810 * Return pidl(s) by number from the cached DataObject
3812 * nPidlIndex=0 gets the fully qualified root path
3814 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3817 STGMEDIUM medium;
3818 FORMATETC formatetc = get_def_format();
3819 LPITEMIDLIST pidl = NULL;
3821 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3823 if (!doSelected)
3824 return NULL;
3826 /* Get the pidls from IDataObject */
3827 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3829 LPIDA cida = GlobalLock(medium.u.hGlobal);
3830 if(nPidlIndex <= cida->cidl)
3832 pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3834 COMCTL32_ReleaseStgMedium(medium);
3836 return pidl;
3839 /***********************************************************************
3840 * GetNumSelected
3842 * Return the number of selected items in the DataObject.
3845 static UINT GetNumSelected( IDataObject *doSelected )
3847 UINT retVal = 0;
3848 STGMEDIUM medium;
3849 FORMATETC formatetc = get_def_format();
3851 TRACE("sv=%p\n", doSelected);
3853 if (!doSelected) return 0;
3855 /* Get the pidls from IDataObject */
3856 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3858 LPIDA cida = GlobalLock(medium.u.hGlobal);
3859 retVal = cida->cidl;
3860 COMCTL32_ReleaseStgMedium(medium);
3861 return retVal;
3863 return 0;
3867 * TOOLS
3870 /***********************************************************************
3871 * GetName
3873 * Get the pidl's display name (relative to folder) and
3874 * put it in lpstrFileName.
3876 * Return NOERROR on success,
3877 * E_FAIL otherwise
3880 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3882 STRRET str;
3883 HRESULT hRes;
3885 TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3887 if(!lpsf)
3889 SHGetDesktopFolder(&lpsf);
3890 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3891 IShellFolder_Release(lpsf);
3892 return hRes;
3895 /* Get the display name of the pidl relative to the folder */
3896 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3898 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3900 return E_FAIL;
3903 /***********************************************************************
3904 * GetShellFolderFromPidl
3906 * pidlRel is the item pidl relative
3907 * Return the IShellFolder of the absolute pidl
3909 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3911 IShellFolder *psf = NULL,*psfParent;
3913 TRACE("%p\n", pidlAbs);
3915 if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3917 psf = psfParent;
3918 if(pidlAbs && pidlAbs->mkid.cb)
3920 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3922 IShellFolder_Release(psfParent);
3923 return psf;
3926 /* return the desktop */
3927 return psfParent;
3929 return NULL;
3932 /***********************************************************************
3933 * GetParentPidl
3935 * Return the LPITEMIDLIST to the parent of the pidl in the list
3937 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3939 LPITEMIDLIST pidlParent;
3941 TRACE("%p\n", pidl);
3943 pidlParent = COMDLG32_PIDL_ILClone(pidl);
3944 COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3946 return pidlParent;
3949 /***********************************************************************
3950 * GetPidlFromName
3952 * returns the pidl of the file name relative to folder
3953 * NULL if an error occurred
3955 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3957 LPITEMIDLIST pidl = NULL;
3958 ULONG ulEaten;
3960 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3962 if(!lpcstrFileName) return NULL;
3963 if(!*lpcstrFileName) return NULL;
3965 if(!lpsf)
3967 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3968 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3969 IShellFolder_Release(lpsf);
3972 else
3974 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3976 return pidl;
3981 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3983 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3984 HRESULT ret;
3986 TRACE("%p, %p\n", psf, pidl);
3988 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3990 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
3991 /* see documentation shell 4.1*/
3992 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3995 /***********************************************************************
3996 * BrowseSelectedFolder
3998 static BOOL BrowseSelectedFolder(HWND hwnd)
4000 BOOL bBrowseSelFolder = FALSE;
4001 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
4003 TRACE("\n");
4005 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
4007 LPITEMIDLIST pidlSelection;
4009 /* get the file selected */
4010 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
4011 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
4013 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
4014 pidlSelection, SBSP_RELATIVE ) ) )
4016 static const WCHAR notexist[] = {'P','a','t','h',' ','d','o','e','s',
4017 ' ','n','o','t',' ','e','x','i','s','t',0};
4018 MessageBoxW( hwnd, notexist, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
4020 bBrowseSelFolder = TRUE;
4021 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
4022 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
4024 COMDLG32_SHFree( pidlSelection );
4027 return bBrowseSelFolder;
4031 * Memory allocation methods */
4032 static void *MemAlloc(UINT size)
4034 return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
4037 static void MemFree(void *mem)
4039 HeapFree(GetProcessHeap(),0,mem);
4042 static inline BOOL valid_struct_size( DWORD size )
4044 return (size == OPENFILENAME_SIZE_VERSION_400W) ||
4045 (size == sizeof( OPENFILENAMEW ));
4048 static inline BOOL is_win16_looks(DWORD flags)
4050 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
4051 !(flags & OFN_EXPLORER));
4054 /* ------------------ APIs ---------------------- */
4056 /***********************************************************************
4057 * GetOpenFileNameA (COMDLG32.@)
4059 * Creates a dialog box for the user to select a file to open.
4061 * RETURNS
4062 * TRUE on success: user enters a valid file
4063 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4066 BOOL WINAPI GetOpenFileNameA(
4067 LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
4069 TRACE("flags %08x\n", ofn->Flags);
4071 if (!valid_struct_size( ofn->lStructSize ))
4073 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4074 return FALSE;
4077 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4078 if (ofn->Flags & OFN_FILEMUSTEXIST)
4079 ofn->Flags |= OFN_PATHMUSTEXIST;
4081 if (is_win16_looks(ofn->Flags))
4082 return GetFileName31A(ofn, OPEN_DIALOG);
4083 else
4084 return GetFileDialog95A(ofn, OPEN_DIALOG);
4087 /***********************************************************************
4088 * GetOpenFileNameW (COMDLG32.@)
4090 * Creates a dialog box for the user to select a file to open.
4092 * RETURNS
4093 * TRUE on success: user enters a valid file
4094 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4097 BOOL WINAPI GetOpenFileNameW(
4098 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4100 TRACE("flags %08x\n", ofn->Flags);
4102 if (!valid_struct_size( ofn->lStructSize ))
4104 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4105 return FALSE;
4108 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4109 if (ofn->Flags & OFN_FILEMUSTEXIST)
4110 ofn->Flags |= OFN_PATHMUSTEXIST;
4112 if (is_win16_looks(ofn->Flags))
4113 return GetFileName31W(ofn, OPEN_DIALOG);
4114 else
4115 return GetFileDialog95W(ofn, OPEN_DIALOG);
4119 /***********************************************************************
4120 * GetSaveFileNameA (COMDLG32.@)
4122 * Creates a dialog box for the user to select a file to save.
4124 * RETURNS
4125 * TRUE on success: user enters a valid file
4126 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4129 BOOL WINAPI GetSaveFileNameA(
4130 LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
4132 if (!valid_struct_size( ofn->lStructSize ))
4134 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4135 return FALSE;
4138 if (is_win16_looks(ofn->Flags))
4139 return GetFileName31A(ofn, SAVE_DIALOG);
4140 else
4141 return GetFileDialog95A(ofn, SAVE_DIALOG);
4144 /***********************************************************************
4145 * GetSaveFileNameW (COMDLG32.@)
4147 * Creates a dialog box for the user to select a file to save.
4149 * RETURNS
4150 * TRUE on success: user enters a valid file
4151 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4154 BOOL WINAPI GetSaveFileNameW(
4155 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4157 if (!valid_struct_size( ofn->lStructSize ))
4159 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4160 return FALSE;
4163 if (is_win16_looks(ofn->Flags))
4164 return GetFileName31W(ofn, SAVE_DIALOG);
4165 else
4166 return GetFileDialog95W(ofn, SAVE_DIALOG);
4169 /***********************************************************************
4170 * GetFileTitleA (COMDLG32.@)
4172 * See GetFileTitleW.
4174 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4176 int ret;
4177 UNICODE_STRING strWFile;
4178 LPWSTR lpWTitle;
4180 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4181 lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4182 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4183 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4184 RtlFreeUnicodeString( &strWFile );
4185 RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4186 return ret;
4190 /***********************************************************************
4191 * GetFileTitleW (COMDLG32.@)
4193 * Get the name of a file.
4195 * PARAMS
4196 * lpFile [I] name and location of file
4197 * lpTitle [O] returned file name
4198 * cbBuf [I] buffer size of lpTitle
4200 * RETURNS
4201 * Success: zero
4202 * Failure: negative number.
4204 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4206 int i, len;
4207 static const WCHAR brkpoint[] = {'*','[',']',0};
4208 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4210 if(lpFile == NULL || lpTitle == NULL)
4211 return -1;
4213 len = lstrlenW(lpFile);
4215 if (len == 0)
4216 return -1;
4218 if(strpbrkW(lpFile, brkpoint))
4219 return -1;
4221 len--;
4223 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4224 return -1;
4226 for(i = len; i >= 0; i--)
4228 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':')
4230 i++;
4231 break;
4235 if(i == -1)
4236 i++;
4238 TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4240 len = lstrlenW(lpFile+i)+1;
4241 if(cbBuf < len)
4242 return len;
4244 lstrcpyW(lpTitle, &lpFile[i]);
4245 return 0;