oleaut32/tests: Test more return values.
[wine.git] / dlls / comdlg32 / filedlg.c
blob2ad7efa80e802aaecfa1669984878578aa067f27
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 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
166 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
168 static const WCHAR LastVisitedMRUW[] =
169 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
170 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
171 'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
172 'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
173 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
175 static const WCHAR filedlg_info_propnameW[] = {'F','i','l','e','O','p','e','n','D','l','g','I','n','f','o','s',0};
177 FileOpenDlgInfos *get_filedlg_infoptr(HWND hwnd)
179 return GetPropW(hwnd, filedlg_info_propnameW);
182 /***********************************************************************
183 * Prototypes
186 /* Internal functions used by the dialog */
187 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
188 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
189 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
190 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
191 static BOOL FILEDLG95_OnOpen(HWND hwnd);
192 static LRESULT FILEDLG95_InitControls(HWND hwnd);
193 static void FILEDLG95_Clean(HWND hwnd);
195 /* Functions used by the shell navigation */
196 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
197 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd);
198 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
199 static void FILEDLG95_SHELL_Clean(HWND hwnd);
200 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd);
202 /* Functions used by the EDIT box */
203 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
205 /* Functions used by the filetype combo box */
206 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
207 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
208 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
209 static void FILEDLG95_FILETYPE_Clean(HWND hwnd);
211 /* Functions used by the Look In combo box */
212 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo);
213 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
214 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
215 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
216 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
217 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
218 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
219 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
220 static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
222 /* Functions for dealing with the most-recently-used registry keys */
223 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
224 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
225 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
227 /* Miscellaneous tool functions */
228 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
229 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
230 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl);
231 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
232 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
233 static UINT GetNumSelected( IDataObject *doSelected );
234 static void COMCTL32_ReleaseStgMedium(STGMEDIUM medium);
236 /* Shell memory allocation */
237 static void *MemAlloc(UINT size);
238 static void MemFree(void *mem);
240 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
241 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
242 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
243 static BOOL BrowseSelectedFolder(HWND hwnd);
245 /***********************************************************************
246 * GetFileName95
248 * Creates an Open common dialog box that lets the user select
249 * the drive, directory, and the name of a file or set of files to open.
251 * IN : The FileOpenDlgInfos structure associated with the dialog
252 * OUT : TRUE on success
253 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
255 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
257 LRESULT lRes;
258 void *template;
259 HRSRC hRes;
260 HANDLE hDlgTmpl = 0;
262 /* test for missing functionality */
263 if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
265 FIXME("Flags 0x%08x not yet implemented\n",
266 fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
269 /* Create the dialog from a template */
271 if(!(hRes = FindResourceW(COMDLG32_hInstance,MAKEINTRESOURCEW(NEWFILEOPENORD),(LPCWSTR)RT_DIALOG)))
273 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
274 return FALSE;
276 if (!(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes )) ||
277 !(template = LockResource( hDlgTmpl )))
279 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
280 return FALSE;
283 /* msdn: explorer style dialogs permit sizing by default.
284 * The OFN_ENABLESIZING flag is only needed when a hook or
285 * custom template is provided */
286 if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
287 !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
288 fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
290 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
292 fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
293 fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
296 /* old style hook messages */
297 if (IsHooked(fodInfos))
299 fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
300 fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
301 fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
302 fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
305 if (fodInfos->unicode)
306 lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
307 template,
308 fodInfos->ofnInfos->hwndOwner,
309 FileOpenDlgProc95,
310 (LPARAM) fodInfos);
311 else
312 lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
313 template,
314 fodInfos->ofnInfos->hwndOwner,
315 FileOpenDlgProc95,
316 (LPARAM) fodInfos);
317 if (fodInfos->ole_initialized)
318 OleUninitialize();
320 /* Unable to create the dialog */
321 if( lRes == -1)
322 return FALSE;
324 return lRes;
327 static WCHAR *heap_strdupAtoW(const char *str)
329 WCHAR *ret;
330 INT len;
332 if (!str)
333 return NULL;
335 len = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
336 ret = MemAlloc(len * sizeof(WCHAR));
337 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
339 return ret;
342 static void init_filedlg_infoW(OPENFILENAMEW *ofn, FileOpenDlgInfos *info)
344 INITCOMMONCONTROLSEX icc;
346 /* Initialize ComboBoxEx32 */
347 icc.dwSize = sizeof(icc);
348 icc.dwICC = ICC_USEREX_CLASSES;
349 InitCommonControlsEx(&icc);
351 /* Initialize CommDlgExtendedError() */
352 COMDLG32_SetCommDlgExtendedError(0);
354 memset(info, 0, sizeof(*info));
356 /* Pass in the original ofn */
357 info->ofnInfos = ofn;
359 info->title = ofn->lpstrTitle;
360 info->defext = ofn->lpstrDefExt;
361 info->filter = ofn->lpstrFilter;
362 info->customfilter = ofn->lpstrCustomFilter;
364 if (ofn->lpstrFile)
366 info->filename = MemAlloc(ofn->nMaxFile * sizeof(WCHAR));
367 lstrcpynW(info->filename, ofn->lpstrFile, ofn->nMaxFile);
370 if (ofn->lpstrInitialDir)
372 DWORD len = ExpandEnvironmentStringsW(ofn->lpstrInitialDir, NULL, 0);
373 if (len)
375 info->initdir = MemAlloc(len * sizeof(WCHAR));
376 ExpandEnvironmentStringsW(ofn->lpstrInitialDir, info->initdir, len);
380 info->unicode = TRUE;
383 static void init_filedlg_infoA(OPENFILENAMEA *ofn, FileOpenDlgInfos *info)
385 OPENFILENAMEW ofnW;
386 int len;
388 ofnW = *(OPENFILENAMEW *)ofn;
390 ofnW.lpstrInitialDir = heap_strdupAtoW(ofn->lpstrInitialDir);
391 ofnW.lpstrDefExt = heap_strdupAtoW(ofn->lpstrDefExt);
392 ofnW.lpstrTitle = heap_strdupAtoW(ofn->lpstrTitle);
394 if (ofn->lpstrFile)
396 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, NULL, 0);
397 ofnW.lpstrFile = MemAlloc(len * sizeof(WCHAR));
398 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, ofnW.lpstrFile, len);
399 ofnW.nMaxFile = len;
402 if (ofn->lpstrFilter)
404 LPCSTR s;
405 int n;
407 /* filter is a list... title\0ext\0......\0\0 */
408 s = ofn->lpstrFilter;
409 while (*s) s = s+strlen(s)+1;
410 s++;
411 n = s - ofn->lpstrFilter;
412 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0);
413 ofnW.lpstrFilter = MemAlloc(len * sizeof(WCHAR));
414 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, (WCHAR *)ofnW.lpstrFilter, len);
417 /* convert lpstrCustomFilter */
418 if (ofn->lpstrCustomFilter)
420 int n, len;
421 LPCSTR s;
423 /* customfilter contains a pair of strings... title\0ext\0 */
424 s = ofn->lpstrCustomFilter;
425 if (*s) s = s+strlen(s)+1;
426 if (*s) s = s+strlen(s)+1;
427 n = s - ofn->lpstrCustomFilter;
428 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0);
429 ofnW.lpstrCustomFilter = MemAlloc(len * sizeof(WCHAR));
430 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, ofnW.lpstrCustomFilter, len);
433 init_filedlg_infoW(&ofnW, info);
435 /* fixup A-specific fields */
436 info->ofnInfos = (OPENFILENAMEW *)ofn;
437 info->unicode = FALSE;
439 /* free what was duplicated */
440 MemFree((WCHAR *)ofnW.lpstrInitialDir);
441 MemFree((WCHAR *)ofnW.lpstrFile);
444 /***********************************************************************
445 * GetFileDialog95
447 * Call GetFileName95 with this structure and clean the memory.
449 static BOOL GetFileDialog95(FileOpenDlgInfos *info, UINT dlg_type)
451 WCHAR *current_dir = NULL;
452 BOOL ret;
454 /* save current directory */
455 if (info->ofnInfos->Flags & OFN_NOCHANGEDIR)
457 current_dir = MemAlloc(MAX_PATH * sizeof(WCHAR));
458 GetCurrentDirectoryW(MAX_PATH, current_dir);
461 switch (dlg_type)
463 case OPEN_DIALOG:
464 ret = GetFileName95(info);
465 break;
466 case SAVE_DIALOG:
467 info->DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
468 ret = GetFileName95(info);
469 break;
470 default:
471 ret = FALSE;
474 if (current_dir)
476 SetCurrentDirectoryW(current_dir);
477 MemFree(current_dir);
480 if (!info->unicode)
482 MemFree((WCHAR *)info->defext);
483 MemFree((WCHAR *)info->title);
484 MemFree((WCHAR *)info->filter);
485 MemFree((WCHAR *)info->customfilter);
488 MemFree(info->filename);
489 MemFree(info->initdir);
490 return ret;
493 /******************************************************************************
494 * COMDLG32_GetDisplayNameOf [internal]
496 * Helper function to get the display name for a pidl.
498 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
499 LPSHELLFOLDER psfDesktop;
500 STRRET strret;
502 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
503 return FALSE;
505 if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
506 IShellFolder_Release(psfDesktop);
507 return FALSE;
510 IShellFolder_Release(psfDesktop);
511 return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
514 /******************************************************************************
515 * COMDLG32_GetCanonicalPath [internal]
517 * Helper function to get the canonical path.
519 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
520 LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
522 WCHAR lpstrTemp[MAX_PATH];
524 /* Get the current directory name */
525 if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
527 /* last fallback */
528 GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
530 PathAddBackslashW(lpstrPathAndFile);
532 TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile));
534 /* if the user specified a fully qualified path use it */
535 if(PathIsRelativeW(lpstrFile))
537 lstrcatW(lpstrPathAndFile, lpstrFile);
539 else
541 /* does the path have a drive letter? */
542 if (PathGetDriveNumberW(lpstrFile) == -1)
543 lstrcpyW(lpstrPathAndFile+2, lpstrFile);
544 else
545 lstrcpyW(lpstrPathAndFile, lpstrFile);
548 /* resolve "." and ".." */
549 PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
550 lstrcpyW(lpstrPathAndFile, lpstrTemp);
551 TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
554 /***********************************************************************
555 * COMDLG32_SplitFileNames [internal]
557 * Creates a delimited list of filenames.
559 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
561 UINT nStrCharCount = 0; /* index in src buffer */
562 UINT nFileIndex = 0; /* index in dest buffer */
563 UINT nFileCount = 0; /* number of files */
565 /* we might get single filename without any '"',
566 * so we need nStrLen + terminating \0 + end-of-list \0 */
567 *lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
568 *sizeUsed = 0;
570 /* build delimited file list from filenames */
571 while ( nStrCharCount <= nStrLen )
573 if ( lpstrEdit[nStrCharCount]=='"' )
575 nStrCharCount++;
576 while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
578 (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
579 nStrCharCount++;
581 (*lpstrFileList)[nFileIndex++] = 0;
582 nFileCount++;
584 nStrCharCount++;
587 /* single, unquoted string */
588 if ((nStrLen > 0) && (nFileIndex == 0) )
590 lstrcpyW(*lpstrFileList, lpstrEdit);
591 nFileIndex = lstrlenW(lpstrEdit) + 1;
592 nFileCount = 1;
595 /* trailing \0 */
596 (*lpstrFileList)[nFileIndex++] = '\0';
598 *sizeUsed = nFileIndex;
599 return nFileCount;
602 /***********************************************************************
603 * ArrangeCtrlPositions [internal]
605 * NOTE: Make sure to add testcases for any changes made here.
607 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
609 HWND hwndChild, hwndStc32;
610 RECT rectParent, rectChild, rectStc32;
611 INT help_fixup = 0;
612 int chgx, chgy;
614 /* Take into account if open as read only checkbox and help button
615 * are hidden
617 if (hide_help)
619 RECT rectHelp, rectCancel;
620 GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
621 GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
622 /* subtract the height of the help button plus the space between
623 * the help button and the cancel button to the height of the dialog
625 help_fixup = rectHelp.bottom - rectCancel.bottom;
629 There are two possibilities to add components to the default file dialog box.
631 By default, all the new components are added below the standard dialog box (the else case).
633 However, if there is a static text component with the stc32 id, a special case happens.
634 The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
635 in the window and the cx and cy indicate how to size the window.
636 Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
637 of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
641 GetClientRect(hwndParentDlg, &rectParent);
643 /* when arranging controls we have to use fixed parent size */
644 rectParent.bottom -= help_fixup;
646 hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
647 if (hwndStc32)
649 GetWindowRect(hwndStc32, &rectStc32);
650 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
652 /* set the size of the stc32 control according to the size of
653 * client area of the parent dialog
655 SetWindowPos(hwndStc32, 0,
656 0, 0,
657 rectParent.right, rectParent.bottom,
658 SWP_NOMOVE | SWP_NOZORDER);
660 else
661 SetRectEmpty(&rectStc32);
663 /* this part moves controls of the child dialog */
664 hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
665 while (hwndChild)
667 if (hwndChild != hwndStc32)
669 GetWindowRect(hwndChild, &rectChild);
670 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
672 /* move only if stc32 exist */
673 if (hwndStc32 && rectChild.left > rectStc32.right)
675 /* move to the right of visible controls of the parent dialog */
676 rectChild.left += rectParent.right;
677 rectChild.left -= rectStc32.right;
679 /* move even if stc32 doesn't exist */
680 if (rectChild.top >= rectStc32.bottom)
682 /* move below visible controls of the parent dialog */
683 rectChild.top += rectParent.bottom;
684 rectChild.top -= rectStc32.bottom - rectStc32.top;
687 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
688 0, 0, SWP_NOSIZE | SWP_NOZORDER);
690 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
693 /* this part moves controls of the parent dialog */
694 hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
695 while (hwndChild)
697 if (hwndChild != hwndChildDlg)
699 GetWindowRect(hwndChild, &rectChild);
700 MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
702 /* left,top of stc32 marks the position of controls
703 * from the parent dialog
705 rectChild.left += rectStc32.left;
706 rectChild.top += rectStc32.top;
708 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
709 0, 0, SWP_NOSIZE | SWP_NOZORDER);
711 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
714 /* calculate the size of the resulting dialog */
716 /* here we have to use original parent size */
717 GetClientRect(hwndParentDlg, &rectParent);
718 GetClientRect(hwndChildDlg, &rectChild);
719 TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
720 wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
722 if (hwndStc32)
724 /* width */
725 if (rectParent.right > rectStc32.right - rectStc32.left)
726 chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
727 else
728 chgx = rectChild.right - rectParent.right;
729 /* height */
730 if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
731 chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
732 else
733 /* Unconditionally set new dialog
734 * height to that of the child
736 chgy = rectChild.bottom - rectParent.bottom;
738 else
740 chgx = 0;
741 chgy = rectChild.bottom - help_fixup;
743 /* set the size of the parent dialog */
744 GetWindowRect(hwndParentDlg, &rectParent);
745 SetWindowPos(hwndParentDlg, 0,
746 0, 0,
747 rectParent.right - rectParent.left + chgx,
748 rectParent.bottom - rectParent.top + chgy,
749 SWP_NOMOVE | SWP_NOZORDER);
752 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
754 switch(uMsg) {
755 case WM_INITDIALOG:
756 return TRUE;
758 return FALSE;
761 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
763 LPCVOID template;
764 HRSRC hRes;
765 HANDLE hDlgTmpl = 0;
766 HWND hChildDlg = 0;
768 TRACE("%p, %p\n", fodInfos, hwnd);
771 * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
772 * structure's hInstance parameter is not a HINSTANCE, but
773 * instead a pointer to a template resource to use.
775 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
777 HINSTANCE hinst;
778 if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
780 hinst = COMDLG32_hInstance;
781 if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
783 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
784 return NULL;
787 else
789 hinst = fodInfos->ofnInfos->hInstance;
790 if(fodInfos->unicode)
792 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
793 hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
795 else
797 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
798 hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
800 if (!hRes)
802 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
803 return NULL;
805 if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
806 !(template = LockResource( hDlgTmpl )))
808 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
809 return NULL;
812 if (fodInfos->unicode)
813 hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
814 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
815 (LPARAM)fodInfos->ofnInfos);
816 else
817 hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
818 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
819 (LPARAM)fodInfos->ofnInfos);
820 return hChildDlg;
822 else if( IsHooked(fodInfos))
824 RECT rectHwnd;
825 struct {
826 DLGTEMPLATE tmplate;
827 WORD menu,class,title;
828 } temp;
829 GetClientRect(hwnd,&rectHwnd);
830 temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
831 temp.tmplate.dwExtendedStyle = 0;
832 temp.tmplate.cdit = 0;
833 temp.tmplate.x = 0;
834 temp.tmplate.y = 0;
835 temp.tmplate.cx = 0;
836 temp.tmplate.cy = 0;
837 temp.menu = temp.class = temp.title = 0;
839 hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
840 hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
842 return hChildDlg;
844 return NULL;
847 /***********************************************************************
848 * SendCustomDlgNotificationMessage
850 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
853 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
855 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwndParentDlg);
856 LRESULT hook_result;
857 OFNOTIFYW ofnNotify;
859 TRACE("%p %d\n", hwndParentDlg, uCode);
861 if (!fodInfos || !fodInfos->DlgInfos.hwndCustomDlg)
862 return 0;
864 TRACE("CALL NOTIFY for %d\n", uCode);
866 ofnNotify.hdr.hwndFrom = hwndParentDlg;
867 ofnNotify.hdr.idFrom = 0;
868 ofnNotify.hdr.code = uCode;
869 ofnNotify.lpOFN = fodInfos->ofnInfos;
870 ofnNotify.pszFile = NULL;
872 if (fodInfos->unicode)
873 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
874 else
875 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
877 TRACE("RET NOTIFY retval %#lx\n", hook_result);
879 return hook_result;
882 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
884 UINT len, total;
885 WCHAR *p, *buffer;
886 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
888 TRACE("CDM_GETFILEPATH:\n");
890 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
891 return -1;
893 /* get path and filenames */
894 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
895 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
896 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
897 if (len)
899 p = buffer + strlenW(buffer);
900 *p++ = '\\';
901 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
903 if (fodInfos->unicode)
905 total = strlenW( buffer) + 1;
906 if (result) lstrcpynW( result, buffer, size );
907 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
909 else
911 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
912 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
913 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
915 HeapFree( GetProcessHeap(), 0, buffer );
916 return total;
919 /***********************************************************************
920 * FILEDLG95_HandleCustomDialogMessages
922 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
924 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
926 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
927 WCHAR lpstrPath[MAX_PATH];
928 INT_PTR retval;
930 if(!fodInfos) return FALSE;
932 switch(uMsg)
934 case CDM_GETFILEPATH:
935 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
936 break;
938 case CDM_GETFOLDERPATH:
939 TRACE("CDM_GETFOLDERPATH:\n");
940 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
941 if (lParam)
943 if (fodInfos->unicode)
944 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
945 else
946 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
947 (LPSTR)lParam, (int)wParam, NULL, NULL);
949 retval = lstrlenW(lpstrPath) + 1;
950 break;
952 case CDM_GETFOLDERIDLIST:
953 retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
954 if (retval <= wParam)
955 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
956 break;
958 case CDM_GETSPEC:
959 TRACE("CDM_GETSPEC:\n");
960 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
961 if (lParam)
963 if (fodInfos->unicode)
964 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
965 else
966 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
968 break;
970 case CDM_SETCONTROLTEXT:
971 TRACE("CDM_SETCONTROLTEXT:\n");
972 if ( lParam )
974 if( fodInfos->unicode )
975 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
976 else
977 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
979 retval = TRUE;
980 break;
982 case CDM_HIDECONTROL:
983 /* MSDN states that it should fail for not OFN_EXPLORER case */
984 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
986 HWND control = GetDlgItem( hwnd, wParam );
987 if (control) ShowWindow( control, SW_HIDE );
988 retval = TRUE;
990 else retval = FALSE;
991 break;
993 default:
994 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
995 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
996 return FALSE;
998 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
999 return TRUE;
1002 /***********************************************************************
1003 * FILEDLG95_OnWMGetMMI
1005 * WM_GETMINMAXINFO message handler for resizable dialogs
1007 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
1009 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1010 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1011 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1013 mmiptr->ptMinTrackSize = fodInfos->initial_size;
1015 return TRUE;
1018 /***********************************************************************
1019 * FILEDLG95_OnWMSize
1021 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1023 * FIXME: this could be made more elaborate. Now use a simple scheme
1024 * where the file view is enlarged and the controls are either moved
1025 * vertically or horizontally to get out of the way. Only the "grip"
1026 * is moved in both directions to stay in the corner.
1028 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1030 RECT rc, rcview;
1031 int chgx, chgy;
1032 HWND ctrl;
1033 HDWP hdwp;
1034 FileOpenDlgInfos *fodInfos;
1036 if( wParam != SIZE_RESTORED) return FALSE;
1037 fodInfos = get_filedlg_infoptr(hwnd);
1038 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1039 /* get the new dialog rectangle */
1040 GetWindowRect( hwnd, &rc);
1041 TRACE("%p, size from %d,%d to %d,%d\n", hwnd, fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1042 rc.right -rc.left, rc.bottom -rc.top);
1043 /* not initialized yet */
1044 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1045 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1046 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1047 return FALSE;
1048 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1049 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1050 fodInfos->sizedlg.cx = rc.right - rc.left;
1051 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1052 /* change the size of the view window */
1053 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1054 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1055 hdwp = BeginDeferWindowPos( 10);
1056 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1057 rcview.right - rcview.left + chgx,
1058 rcview.bottom - rcview.top + chgy,
1059 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1060 /* change position and sizes of the controls */
1061 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1063 int ctrlid = GetDlgCtrlID( ctrl);
1064 GetWindowRect( ctrl, &rc);
1065 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1066 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1068 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1069 0, 0,
1070 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1072 else if( rc.top > rcview.bottom)
1074 /* if it was below the shell view
1075 * move to bottom */
1076 switch( ctrlid)
1078 /* file name (edit or comboboxex) and file types combo change also width */
1079 case edt1:
1080 case cmb13:
1081 case cmb1:
1082 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1083 rc.right - rc.left + chgx, rc.bottom - rc.top,
1084 SWP_NOACTIVATE | SWP_NOZORDER);
1085 break;
1086 /* then these buttons must move out of the way */
1087 case IDOK:
1088 case IDCANCEL:
1089 case pshHelp:
1090 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1091 0, 0,
1092 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1093 break;
1094 default:
1095 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1096 0, 0,
1097 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1100 else if( rc.left > rcview.right)
1102 /* if it was to the right of the shell view
1103 * move to right */
1104 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1105 0, 0,
1106 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1108 else
1109 /* special cases */
1111 switch( ctrlid)
1113 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1114 case IDC_LOOKIN:
1115 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1116 rc.right - rc.left + chgx, rc.bottom - rc.top,
1117 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1118 break;
1119 case IDC_TOOLBARSTATIC:
1120 case IDC_TOOLBAR:
1121 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1122 0, 0,
1123 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1124 break;
1125 #endif
1126 /* not resized in windows. Since wine uses this invisible control
1127 * to size the browser view it needs to be resized */
1128 case IDC_SHELLSTATIC:
1129 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1130 rc.right - rc.left + chgx,
1131 rc.bottom - rc.top + chgy,
1132 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1133 break;
1137 if(fodInfos->DlgInfos.hwndCustomDlg &&
1138 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1140 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1141 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1143 GetWindowRect( ctrl, &rc);
1144 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1145 if( rc.top > rcview.bottom)
1147 /* if it was below the shell view
1148 * move to bottom */
1149 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1150 rc.right - rc.left, rc.bottom - rc.top,
1151 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1153 else if( rc.left > rcview.right)
1155 /* if it was to the right of the shell view
1156 * move to right */
1157 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1158 rc.right - rc.left, rc.bottom - rc.top,
1159 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1162 /* size the custom dialog at the end: some applications do some
1163 * control re-arranging at this point */
1164 GetClientRect(hwnd, &rc);
1165 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1166 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1168 EndDeferWindowPos( hdwp);
1169 /* should not be needed */
1170 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1171 return TRUE;
1174 /***********************************************************************
1175 * FileOpenDlgProc95
1177 * File open dialog procedure
1179 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1181 #if 0
1182 TRACE("%p 0x%04x\n", hwnd, uMsg);
1183 #endif
1185 switch(uMsg)
1187 case WM_INITDIALOG:
1189 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1190 RECT rc, rcstc;
1191 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1192 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1194 /* Some shell namespace extensions depend on COM being initialized. */
1195 if (SUCCEEDED(OleInitialize(NULL)))
1196 fodInfos->ole_initialized = TRUE;
1198 SetPropW(hwnd, filedlg_info_propnameW, fodInfos);
1200 FILEDLG95_InitControls(hwnd);
1202 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1204 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1205 DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1206 RECT client, client_adjusted;
1208 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1210 style |= WS_SIZEBOX;
1211 ex_style |= WS_EX_WINDOWEDGE;
1213 else
1214 style &= ~WS_SIZEBOX;
1215 SetWindowLongW(hwnd, GWL_STYLE, style);
1216 SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1218 GetClientRect( hwnd, &client );
1219 GetClientRect( hwnd, &client_adjusted );
1220 AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1222 GetWindowRect( hwnd, &rc );
1223 rc.right += client_adjusted.right - client.right;
1224 rc.bottom += client_adjusted.bottom - client.bottom;
1225 SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1226 SWP_NOZORDER | SWP_NOMOVE);
1228 GetWindowRect( hwnd, &rc );
1229 fodInfos->DlgInfos.hwndGrip =
1230 CreateWindowExA( 0, "SCROLLBAR", NULL,
1231 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1232 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1233 rc.right - gripx, rc.bottom - gripy,
1234 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1237 fodInfos->DlgInfos.hwndCustomDlg =
1238 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1240 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1241 FILEDLG95_FillControls(hwnd, wParam, lParam);
1243 if( fodInfos->DlgInfos.hwndCustomDlg)
1244 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1246 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1247 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1248 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1251 /* if the app has changed the position of the invisible listbox,
1252 * change that of the listview (browser) as well */
1253 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1254 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1255 if( !EqualRect( &rc, &rcstc))
1257 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1258 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1259 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1260 SWP_NOACTIVATE | SWP_NOZORDER);
1263 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1265 GetWindowRect( hwnd, &rc);
1266 fodInfos->sizedlg.cx = rc.right - rc.left;
1267 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1268 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1269 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1270 GetClientRect( hwnd, &rc);
1271 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1272 rc.right - gripx, rc.bottom - gripy,
1273 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1274 /* resize the dialog to the previous invocation */
1275 if( MemDialogSize.cx && MemDialogSize.cy)
1276 SetWindowPos( hwnd, NULL,
1277 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1278 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1281 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1282 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1284 return 0;
1286 case WM_SIZE:
1287 return FILEDLG95_OnWMSize(hwnd, wParam);
1288 case WM_GETMINMAXINFO:
1289 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1290 case WM_COMMAND:
1291 return FILEDLG95_OnWMCommand(hwnd, wParam);
1292 case WM_DRAWITEM:
1294 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1296 case IDC_LOOKIN:
1297 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1298 return TRUE;
1301 return FALSE;
1303 case WM_GETISHELLBROWSER:
1304 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1306 case WM_DESTROY:
1308 FileOpenDlgInfos * fodInfos = get_filedlg_infoptr(hwnd);
1309 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1310 MemDialogSize = fodInfos->sizedlg;
1311 RemovePropW(hwnd, filedlg_info_propnameW);
1312 return FALSE;
1314 case WM_NOTIFY:
1316 LPNMHDR lpnmh = (LPNMHDR)lParam;
1317 UINT stringId = -1;
1319 /* set up the button tooltips strings */
1320 if(TTN_GETDISPINFOA == lpnmh->code )
1322 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1323 switch(lpnmh->idFrom )
1325 /* Up folder button */
1326 case FCIDM_TB_UPFOLDER:
1327 stringId = IDS_UPFOLDER;
1328 break;
1329 /* New folder button */
1330 case FCIDM_TB_NEWFOLDER:
1331 stringId = IDS_NEWFOLDER;
1332 break;
1333 /* List option button */
1334 case FCIDM_TB_SMALLICON:
1335 stringId = IDS_LISTVIEW;
1336 break;
1337 /* Details option button */
1338 case FCIDM_TB_REPORTVIEW:
1339 stringId = IDS_REPORTVIEW;
1340 break;
1341 /* Desktop button */
1342 case FCIDM_TB_DESKTOP:
1343 stringId = IDS_TODESKTOP;
1344 break;
1345 default:
1346 stringId = 0;
1348 lpdi->hinst = COMDLG32_hInstance;
1349 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1351 return FALSE;
1353 default :
1354 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1355 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1356 return FALSE;
1360 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1362 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1363 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1366 /***********************************************************************
1367 * FILEDLG95_InitControls
1369 * WM_INITDIALOG message handler (before hook notification)
1371 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1373 BOOL win2000plus = FALSE;
1374 BOOL win98plus = FALSE;
1375 BOOL handledPath = FALSE;
1376 OSVERSIONINFOW osVi;
1377 static const WCHAR szwSlash[] = { '\\', 0 };
1378 static const WCHAR szwStar[] = { '*',0 };
1380 static const TBBUTTON tbb[] =
1382 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1383 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1384 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1385 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1386 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1387 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1388 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1389 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1390 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1392 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1394 RECT rectTB;
1395 RECT rectlook;
1397 HIMAGELIST toolbarImageList;
1398 SHFILEINFOA shFileInfo;
1399 ITEMIDLIST *desktopPidl;
1401 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1403 TRACE("%p\n", fodInfos);
1405 /* Get windows version emulating */
1406 osVi.dwOSVersionInfoSize = sizeof(osVi);
1407 GetVersionExW(&osVi);
1408 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1409 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1410 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1411 win2000plus = (osVi.dwMajorVersion > 4);
1412 if (win2000plus) win98plus = TRUE;
1414 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1417 /* Use either the edit or the comboboxex for the filename control */
1418 if (filename_is_edit( fodInfos ))
1420 DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1421 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1423 else
1425 DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1426 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1429 /* Get the hwnd of the controls */
1430 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1431 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1433 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1434 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1436 /* construct the toolbar */
1437 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1438 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1440 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1441 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1442 rectTB.left = rectlook.right;
1443 rectTB.top = rectlook.top-1;
1445 if (fodInfos->unicode)
1446 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1447 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1448 rectTB.left, rectTB.top,
1449 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1450 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1451 else
1452 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1453 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1454 rectTB.left, rectTB.top,
1455 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1456 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1458 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1460 /* FIXME: use TB_LOADIMAGES when implemented */
1461 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1462 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1463 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1465 /* Retrieve and add desktop icon to the toolbar */
1466 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1467 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1468 SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1469 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1470 ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1472 DestroyIcon(shFileInfo.hIcon);
1473 CoTaskMemFree(desktopPidl);
1475 /* Finish Toolbar Construction */
1476 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1477 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1479 /* Set the window text with the text specified in the OPENFILENAME structure */
1480 if(fodInfos->title)
1482 SetWindowTextW(hwnd,fodInfos->title);
1484 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1486 WCHAR buf[64];
1487 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1488 SetWindowTextW(hwnd, buf);
1491 /* Initialise the file name edit control */
1492 handledPath = FALSE;
1493 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1495 if(fodInfos->filename)
1497 /* 1. If win2000 or higher and filename contains a path, use it
1498 in preference over the lpstrInitialDir */
1499 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1500 WCHAR tmpBuf[MAX_PATH];
1501 WCHAR *nameBit;
1502 DWORD result;
1504 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1505 if (result) {
1507 /* nameBit is always shorter than the original filename. It may be NULL
1508 * when the filename contains only a drive name instead of file name */
1509 if (nameBit)
1511 lstrcpyW(fodInfos->filename,nameBit);
1512 *nameBit = 0x00;
1514 else
1515 *fodInfos->filename = '\0';
1517 MemFree(fodInfos->initdir);
1518 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1519 lstrcpyW(fodInfos->initdir, tmpBuf);
1520 handledPath = TRUE;
1521 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1522 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1524 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1526 } else {
1527 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1531 /* 2. (All platforms) If initdir is not null, then use it */
1532 if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1534 /* Work out the proper path as supplied one might be relative */
1535 /* (Here because supplying '.' as dir browses to My Computer) */
1536 WCHAR tmpBuf[MAX_PATH];
1537 WCHAR tmpBuf2[MAX_PATH];
1538 WCHAR *nameBit;
1539 DWORD result;
1541 lstrcpyW(tmpBuf, fodInfos->initdir);
1542 if (PathFileExistsW(tmpBuf)) {
1543 /* initdir does not have to be a directory. If a file is
1544 * specified, the dir part is taken */
1545 if (PathIsDirectoryW(tmpBuf)) {
1546 PathAddBackslashW(tmpBuf);
1547 lstrcatW(tmpBuf, szwStar);
1549 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1550 if (result) {
1551 *nameBit = 0x00;
1552 MemFree(fodInfos->initdir);
1553 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1554 lstrcpyW(fodInfos->initdir, tmpBuf2);
1555 handledPath = TRUE;
1556 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1559 else if (fodInfos->initdir)
1561 MemFree(fodInfos->initdir);
1562 fodInfos->initdir = NULL;
1563 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1567 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1569 /* 3. All except w2k+: if filename contains a path use it */
1570 if (!win2000plus && fodInfos->filename &&
1571 *fodInfos->filename &&
1572 strpbrkW(fodInfos->filename, szwSlash)) {
1573 WCHAR tmpBuf[MAX_PATH];
1574 WCHAR *nameBit;
1575 DWORD result;
1577 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1578 tmpBuf, &nameBit);
1579 if (result) {
1580 int len;
1582 /* nameBit is always shorter than the original filename */
1583 lstrcpyW(fodInfos->filename, nameBit);
1584 *nameBit = 0x00;
1586 len = lstrlenW(tmpBuf);
1587 MemFree(fodInfos->initdir);
1588 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1589 lstrcpyW(fodInfos->initdir, tmpBuf);
1591 handledPath = TRUE;
1592 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1593 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1595 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1598 /* 4. Win2000+: Recently used */
1599 if (!handledPath && win2000plus) {
1600 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1601 fodInfos->initdir[0] = '\0';
1603 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1605 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1606 handledPath = TRUE;
1607 }else{
1608 MemFree(fodInfos->initdir);
1609 fodInfos->initdir = NULL;
1613 /* 5. win98+ and win2000+ if any files of specified filter types in
1614 current directory, use it */
1615 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1617 LPCWSTR lpstrPos = fodInfos->filter;
1618 WIN32_FIND_DATAW FindFileData;
1619 HANDLE hFind;
1621 while (1)
1623 /* filter is a list... title\0ext\0......\0\0 */
1625 /* Skip the title */
1626 if(! *lpstrPos) break; /* end */
1627 lpstrPos += lstrlenW(lpstrPos) + 1;
1629 /* See if any files exist in the current dir with this extension */
1630 if(! *lpstrPos) break; /* end */
1632 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1634 if (hFind == INVALID_HANDLE_VALUE) {
1635 /* None found - continue search */
1636 lpstrPos += lstrlenW(lpstrPos) + 1;
1638 } else {
1640 MemFree(fodInfos->initdir);
1641 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1642 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1644 handledPath = TRUE;
1645 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1646 debugstr_w(lpstrPos));
1647 FindClose(hFind);
1648 break;
1653 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1654 if (!handledPath && (win2000plus || win98plus)) {
1655 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1657 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1659 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1661 /* last fallback */
1662 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1663 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1664 } else {
1665 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1667 } else {
1668 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1670 handledPath = TRUE;
1671 } else if (!handledPath) {
1672 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1673 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1674 handledPath = TRUE;
1675 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1678 SetFocus( fodInfos->DlgInfos.hwndFileName );
1679 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1681 /* Must the open as read only check box be checked ?*/
1682 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1684 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1687 /* Must the open as read only check box be hidden? */
1688 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1690 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1691 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1694 /* Must the help button be hidden? */
1695 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1697 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1698 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1701 /* change Open to Save */
1702 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1704 WCHAR buf[16];
1705 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1706 SetDlgItemTextW(hwnd, IDOK, buf);
1707 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1708 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1711 /* Initialize the filter combo box */
1712 FILEDLG95_FILETYPE_Init(hwnd);
1714 return 0;
1717 /***********************************************************************
1718 * FILEDLG95_ResizeControls
1720 * WM_INITDIALOG message handler (after hook notification)
1722 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1724 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1726 if (fodInfos->DlgInfos.hwndCustomDlg)
1728 RECT rc;
1729 UINT flags = SWP_NOACTIVATE;
1731 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1732 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1734 /* resize the custom dialog to the parent size */
1735 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1736 GetClientRect(hwnd, &rc);
1737 else
1739 /* our own fake template is zero sized and doesn't have children, so
1740 * there is no need to resize it. Picasa depends on it.
1742 flags |= SWP_NOSIZE;
1743 SetRectEmpty(&rc);
1745 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1746 0, 0, rc.right, rc.bottom, flags);
1748 else
1750 /* Resize the height; if opened as read-only, checkbox and help button are
1751 * hidden and we are not using a custom template nor a customDialog
1753 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1754 (!(fodInfos->ofnInfos->Flags &
1755 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1757 RECT rectDlg, rectHelp, rectCancel;
1758 GetWindowRect(hwnd, &rectDlg);
1759 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1760 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1761 /* subtract the height of the help button plus the space between the help
1762 * button and the cancel button to the height of the dialog
1764 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1765 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1766 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1769 return TRUE;
1772 /***********************************************************************
1773 * FILEDLG95_FillControls
1775 * WM_INITDIALOG message handler (after hook notification)
1777 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1779 LPITEMIDLIST pidlItemId = NULL;
1781 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1783 TRACE("dir=%s file=%s\n",
1784 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1786 /* Get the initial directory pidl */
1788 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1790 WCHAR path[MAX_PATH];
1792 GetCurrentDirectoryW(MAX_PATH,path);
1793 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1796 /* Initialise shell objects */
1797 FILEDLG95_SHELL_Init(hwnd);
1799 /* Initialize the Look In combo box */
1800 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1802 /* Browse to the initial directory */
1803 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1805 /* Free pidlItem memory */
1806 COMDLG32_SHFree(pidlItemId);
1808 return TRUE;
1810 /***********************************************************************
1811 * FILEDLG95_Clean
1813 * Regroups all the cleaning functions of the filedlg
1815 void FILEDLG95_Clean(HWND hwnd)
1817 FILEDLG95_FILETYPE_Clean(hwnd);
1818 FILEDLG95_LOOKIN_Clean(hwnd);
1819 FILEDLG95_SHELL_Clean(hwnd);
1821 /***********************************************************************
1822 * FILEDLG95_OnWMCommand
1824 * WM_COMMAND message handler
1826 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1828 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1829 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1830 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1832 switch(wID)
1834 /* OK button */
1835 case IDOK:
1836 FILEDLG95_OnOpen(hwnd);
1837 break;
1838 /* Cancel button */
1839 case IDCANCEL:
1840 FILEDLG95_Clean(hwnd);
1841 EndDialog(hwnd, FALSE);
1842 break;
1843 /* Filetype combo box */
1844 case IDC_FILETYPE:
1845 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1846 break;
1847 /* LookIn combo box */
1848 case IDC_LOOKIN:
1849 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1850 break;
1852 /* --- toolbar --- */
1853 /* Up folder button */
1854 case FCIDM_TB_UPFOLDER:
1855 FILEDLG95_SHELL_UpFolder(hwnd);
1856 break;
1857 /* New folder button */
1858 case FCIDM_TB_NEWFOLDER:
1859 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1860 break;
1861 /* List option button */
1862 case FCIDM_TB_SMALLICON:
1863 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1864 break;
1865 /* Details option button */
1866 case FCIDM_TB_REPORTVIEW:
1867 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1868 break;
1869 /* Details option button */
1870 case FCIDM_TB_DESKTOP:
1871 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1872 break;
1874 case edt1:
1875 case cmb13:
1876 break;
1879 /* Do not use the listview selection anymore */
1880 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1881 return 0;
1884 /***********************************************************************
1885 * FILEDLG95_OnWMGetIShellBrowser
1887 * WM_GETISHELLBROWSER message handler
1889 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1891 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1893 TRACE("\n");
1895 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1897 return TRUE;
1901 /***********************************************************************
1902 * FILEDLG95_SendFileOK
1904 * Sends the CDN_FILEOK notification if required
1906 * RETURNS
1907 * TRUE if the dialog should close
1908 * FALSE if the dialog should not be closed
1910 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1912 /* ask the hook if we can close */
1913 if(IsHooked(fodInfos))
1915 LRESULT retval = 0;
1917 TRACE("---\n");
1918 /* First send CDN_FILEOK as MSDN doc says */
1919 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1920 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1921 if( retval)
1923 TRACE("canceled\n");
1924 return FALSE;
1927 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1928 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1929 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1930 if( retval)
1932 TRACE("canceled\n");
1933 return FALSE;
1936 return TRUE;
1939 /***********************************************************************
1940 * FILEDLG95_OnOpenMultipleFiles
1942 * Handles the opening of multiple files.
1944 * FIXME
1945 * check destination buffer size
1947 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
1949 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1950 WCHAR lpstrPathSpec[MAX_PATH] = {0};
1951 UINT nCount, nSizePath;
1953 TRACE("\n");
1955 if(fodInfos->unicode)
1957 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1958 ofn->lpstrFile[0] = '\0';
1960 else
1962 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
1963 ofn->lpstrFile[0] = '\0';
1966 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
1968 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
1969 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
1970 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
1972 LPWSTR lpstrTemp = lpstrFileList;
1974 for ( nCount = 0; nCount < nFileCount; nCount++ )
1976 LPITEMIDLIST pidl;
1978 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
1979 if (!pidl)
1981 WCHAR lpstrNotFound[100];
1982 WCHAR lpstrMsg[100];
1983 WCHAR tmp[400];
1984 static const WCHAR nl[] = {'\n',0};
1986 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
1987 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
1989 lstrcpyW(tmp, lpstrTemp);
1990 lstrcatW(tmp, nl);
1991 lstrcatW(tmp, lpstrNotFound);
1992 lstrcatW(tmp, nl);
1993 lstrcatW(tmp, lpstrMsg);
1995 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
1996 return FALSE;
1999 /* move to the next file in the list of files */
2000 lpstrTemp += lstrlenW(lpstrTemp) + 1;
2001 COMDLG32_SHFree(pidl);
2005 nSizePath = lstrlenW(lpstrPathSpec) + 1;
2006 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2008 /* For "oldstyle" dialog the components have to
2009 be separated by blanks (not '\0'!) and short
2010 filenames have to be used! */
2011 FIXME("Components have to be separated by blanks\n");
2013 if(fodInfos->unicode)
2015 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2016 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2017 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2019 else
2021 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2023 if (ofn->lpstrFile != NULL)
2025 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2026 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2027 if (ofn->nMaxFile > nSizePath)
2029 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2030 ofn->lpstrFile + nSizePath,
2031 ofn->nMaxFile - nSizePath, NULL, NULL);
2036 fodInfos->ofnInfos->nFileOffset = nSizePath;
2037 fodInfos->ofnInfos->nFileExtension = 0;
2039 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2040 return FALSE;
2042 /* clean and exit */
2043 FILEDLG95_Clean(hwnd);
2044 return EndDialog(hwnd,TRUE);
2047 /* Returns the 'slot name' of the given module_name in the registry's
2048 * most-recently-used list. This will be an ASCII value in the
2049 * range ['a','z'). Returns zero on error.
2051 * The slot's value in the registry has the form:
2052 * module_name\0mru_path\0
2054 * If stored_path is given, then stored_path will contain the path name
2055 * stored in the registry's MRU list for the given module_name.
2057 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2058 * MRU list key for the given module_name.
2060 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2062 WCHAR mru_list[32], *cur_mru_slot;
2063 BOOL taken[25] = {0};
2064 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2065 HKEY hkey_tmp, *hkey;
2066 LONG ret;
2068 if(hkey_ret)
2069 hkey = hkey_ret;
2070 else
2071 hkey = &hkey_tmp;
2073 if(stored_path)
2074 *stored_path = '\0';
2076 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2077 if(ret){
2078 WARN("Unable to create MRU key: %d\n", ret);
2079 return 0;
2082 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2083 (LPBYTE)mru_list, &mru_list_size);
2084 if(ret || key_type != REG_SZ){
2085 if(ret == ERROR_FILE_NOT_FOUND)
2086 return 'a';
2088 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2089 RegCloseKey(*hkey);
2090 return 0;
2093 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2094 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2095 DWORD value_data_size = sizeof(value_data);
2097 *value_name = *cur_mru_slot;
2099 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2100 &key_type, (LPBYTE)value_data, &value_data_size);
2101 if(ret || key_type != REG_BINARY){
2102 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2103 continue;
2106 if(!strcmpiW(module_name, value_data)){
2107 if(!hkey_ret)
2108 RegCloseKey(*hkey);
2109 if(stored_path)
2110 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2111 return *value_name;
2115 if(!hkey_ret)
2116 RegCloseKey(*hkey);
2118 /* the module name isn't in the registry, so find the next open slot */
2119 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2120 taken[*cur_mru_slot - 'a'] = TRUE;
2121 for(i = 0; i < 25; ++i){
2122 if(!taken[i])
2123 return i + 'a';
2126 /* all slots are taken, so return the last one in MRUList */
2127 --cur_mru_slot;
2128 return *cur_mru_slot;
2131 /* save the given filename as most-recently-used path for this module */
2132 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2134 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2135 LONG ret;
2136 HKEY hkey;
2138 /* get the current executable's name */
2139 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2140 WARN("GotModuleFileName failed: %d\n", GetLastError());
2141 return;
2143 module_name = strrchrW(module_path, '\\');
2144 if(!module_name)
2145 module_name = module_path;
2146 else
2147 module_name += 1;
2149 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2150 if(!slot)
2151 return;
2152 *slot_name = slot;
2154 { /* update the slot's info */
2155 WCHAR *path_ends, *final;
2156 DWORD path_len, final_len;
2158 /* use only the path segment of `filename' */
2159 path_ends = strrchrW(filename, '\\');
2160 path_len = path_ends - filename;
2162 final_len = path_len + lstrlenW(module_name) + 2;
2164 final = MemAlloc(final_len * sizeof(WCHAR));
2165 if(!final)
2166 return;
2167 lstrcpyW(final, module_name);
2168 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2169 final[final_len-1] = '\0';
2171 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2172 final_len * sizeof(WCHAR));
2173 if(ret){
2174 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2175 MemFree(final);
2176 RegCloseKey(hkey);
2177 return;
2180 MemFree(final);
2183 { /* update MRUList value */
2184 WCHAR old_mru_list[32], new_mru_list[32];
2185 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2186 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2188 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2189 (LPBYTE)old_mru_list, &mru_list_size);
2190 if(ret || key_type != REG_SZ){
2191 if(ret == ERROR_FILE_NOT_FOUND){
2192 new_mru_list[0] = slot;
2193 new_mru_list[1] = '\0';
2194 }else{
2195 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2196 RegCloseKey(hkey);
2197 return;
2199 }else{
2200 /* copy old list data over so that the new slot is at the start
2201 * of the list */
2202 *new_mru_slot++ = slot;
2203 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2204 if(*old_mru_slot != slot)
2205 *new_mru_slot++ = *old_mru_slot;
2207 *new_mru_slot = '\0';
2210 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2211 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2212 if(ret){
2213 WARN("Error saving MRUList data: %d\n", ret);
2214 RegCloseKey(hkey);
2215 return;
2220 /* load the most-recently-used path for this module */
2221 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2223 WCHAR module_path[MAX_PATH], *module_name;
2225 /* get the current executable's name */
2226 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2227 WARN("GotModuleFileName failed: %d\n", GetLastError());
2228 return;
2230 module_name = strrchrW(module_path, '\\');
2231 if(!module_name)
2232 module_name = module_path;
2233 else
2234 module_name += 1;
2236 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2237 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2240 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2242 WCHAR strMsgTitle[MAX_PATH];
2243 WCHAR strMsgText [MAX_PATH];
2244 if (idCaption)
2245 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2246 else
2247 strMsgTitle[0] = '\0';
2248 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2249 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2252 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2253 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2255 int nOpenAction = defAction;
2256 LPWSTR lpszTemp, lpszTemp1;
2257 LPITEMIDLIST pidl = NULL;
2258 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2260 /* check for invalid chars */
2261 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2263 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2264 return FALSE;
2267 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2269 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2270 while (lpszTemp1)
2272 LPSHELLFOLDER lpsfChild;
2273 WCHAR lpwstrTemp[MAX_PATH];
2274 DWORD dwEaten, dwAttributes;
2275 LPWSTR p;
2277 lstrcpyW(lpwstrTemp, lpszTemp);
2278 p = PathFindNextComponentW(lpwstrTemp);
2280 if (!p) break; /* end of path */
2282 *p = 0;
2283 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2285 /* There are no wildcards when OFN_NOVALIDATE is set */
2286 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2288 static const WCHAR wszWild[] = { '*', '?', 0 };
2289 /* if the last element is a wildcard do a search */
2290 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2292 nOpenAction = ONOPEN_SEARCH;
2293 break;
2296 lpszTemp1 = lpszTemp;
2298 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2300 /* append a backslash to drive letters */
2301 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2302 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2303 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2305 PathAddBackslashW(lpwstrTemp);
2308 dwAttributes = SFGAO_FOLDER;
2309 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2311 /* the path component is valid, we have a pidl of the next path component */
2312 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2313 if(dwAttributes & SFGAO_FOLDER)
2315 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2317 ERR("bind to failed\n"); /* should not fail */
2318 break;
2320 IShellFolder_Release(*ppsf);
2321 *ppsf = lpsfChild;
2322 lpsfChild = NULL;
2324 else
2326 TRACE("value\n");
2328 /* end dialog, return value */
2329 nOpenAction = ONOPEN_OPEN;
2330 break;
2332 COMDLG32_SHFree(pidl);
2333 pidl = NULL;
2335 else if (!(flags & OFN_NOVALIDATE))
2337 if(*lpszTemp || /* points to trailing null for last path element */
2338 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2340 if(flags & OFN_PATHMUSTEXIST)
2342 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2343 break;
2346 else
2348 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2350 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2351 break;
2354 /* change to the current folder */
2355 nOpenAction = ONOPEN_OPEN;
2356 break;
2358 else
2360 nOpenAction = ONOPEN_OPEN;
2361 break;
2364 if(pidl) COMDLG32_SHFree(pidl);
2366 return nOpenAction;
2369 /***********************************************************************
2370 * FILEDLG95_OnOpen
2372 * Ok button WM_COMMAND message handler
2374 * If the function succeeds, the return value is nonzero.
2376 BOOL FILEDLG95_OnOpen(HWND hwnd)
2378 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2379 LPWSTR lpstrFileList;
2380 UINT nFileCount = 0;
2381 UINT sizeUsed = 0;
2382 BOOL ret = TRUE;
2383 WCHAR lpstrPathAndFile[MAX_PATH];
2384 LPSHELLFOLDER lpsf = NULL;
2385 int nOpenAction;
2387 TRACE("hwnd=%p\n", hwnd);
2389 /* try to browse the selected item */
2390 if(BrowseSelectedFolder(hwnd))
2391 return FALSE;
2393 /* get the files from the edit control */
2394 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2396 if(nFileCount == 0)
2397 return FALSE;
2399 if(nFileCount > 1)
2401 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2402 goto ret;
2405 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2408 Step 1: Build a complete path name from the current folder and
2409 the filename or path in the edit box.
2410 Special cases:
2411 - the path in the edit box is a root path
2412 (with or without drive letter)
2413 - the edit box contains ".." (or a path with ".." in it)
2416 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2417 MemFree(lpstrFileList);
2420 Step 2: here we have a cleaned up path
2422 We have to parse the path step by step to see if we have to browse
2423 to a folder if the path points to a directory or the last
2424 valid element is a directory.
2426 valid variables:
2427 lpstrPathAndFile: cleaned up path
2430 if (nFileCount &&
2431 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2432 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2433 nOpenAction = ONOPEN_OPEN;
2434 else
2435 nOpenAction = ONOPEN_BROWSE;
2437 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2438 fodInfos->ofnInfos->Flags,
2439 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2440 nOpenAction);
2441 if(!nOpenAction)
2442 goto ret;
2445 Step 3: here we have a cleaned up and validated path
2447 valid variables:
2448 lpsf: ShellFolder bound to the rightmost valid path component
2449 lpstrPathAndFile: cleaned up path
2450 nOpenAction: action to do
2452 TRACE("end validate sf=%p\n", lpsf);
2454 switch(nOpenAction)
2456 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2457 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2459 int iPos;
2460 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2461 DWORD len;
2463 /* replace the current filter */
2464 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2465 len = lstrlenW(lpszTemp)+1;
2466 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2467 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2469 /* set the filter cb to the extension when possible */
2470 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2471 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2473 /* fall through */
2474 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2475 TRACE("ONOPEN_BROWSE\n");
2477 IPersistFolder2 * ppf2;
2478 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2480 LPITEMIDLIST pidlCurrent;
2481 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2482 IPersistFolder2_Release(ppf2);
2483 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2485 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2486 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2488 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2489 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2492 else if( nOpenAction == ONOPEN_SEARCH )
2494 if (fodInfos->Shell.FOIShellView)
2495 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2497 COMDLG32_SHFree(pidlCurrent);
2498 if (filename_is_edit( fodInfos ))
2499 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2500 else
2502 HWND hwnd;
2504 hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2505 SendMessageW(hwnd, EM_SETSEL, 0, -1);
2509 ret = FALSE;
2510 break;
2511 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2512 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2514 WCHAR *ext = NULL;
2516 /* update READONLY check box flag */
2517 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2518 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2519 else
2520 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2522 /* Attach the file extension with file name*/
2523 ext = PathFindExtensionW(lpstrPathAndFile);
2524 if (! *ext && fodInfos->defext)
2526 /* if no extension is specified with file name, then */
2527 /* attach the extension from file filter or default one */
2529 WCHAR *filterExt = NULL;
2530 LPWSTR lpstrFilter = NULL;
2531 static const WCHAR szwDot[] = {'.',0};
2532 int PathLength = lstrlenW(lpstrPathAndFile);
2534 /*Get the file extension from file type filter*/
2535 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2536 fodInfos->ofnInfos->nFilterIndex-1);
2538 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2540 WCHAR* filterSearchIndex;
2541 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2542 strcpyW(filterExt, lpstrFilter);
2544 /* if a semicolon-separated list of file extensions was given, do not include the
2545 semicolon or anything after it in the extension.
2546 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2547 filterSearchIndex = strchrW(filterExt, ';');
2548 if (filterSearchIndex)
2550 filterSearchIndex[0] = '\0';
2553 /* find the file extension by searching for the first dot in filterExt */
2554 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2555 /* if the extension is invalid or contains a glob, ignore it */
2556 filterSearchIndex = strchrW(filterExt, '.');
2557 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2559 strcpyW(filterExt, filterSearchIndex);
2561 else
2563 HeapFree(GetProcessHeap(), 0, filterExt);
2564 filterExt = NULL;
2568 if (!filterExt)
2570 /* use the default file extension */
2571 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2572 strcpyW(filterExt, fodInfos->defext);
2575 if (*filterExt) /* ignore filterExt="" */
2577 /* Attach the dot*/
2578 lstrcatW(lpstrPathAndFile, szwDot);
2579 /* Attach the extension */
2580 lstrcatW(lpstrPathAndFile, filterExt);
2583 HeapFree(GetProcessHeap(), 0, filterExt);
2585 /* In Open dialog: if file does not exist try without extension */
2586 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2587 lpstrPathAndFile[PathLength] = '\0';
2589 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2590 if (*ext)
2591 ext++;
2592 if (!lstrcmpiW(fodInfos->defext, ext))
2593 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2594 else
2595 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2598 /* In Save dialog: check if the file already exists */
2599 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2600 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2601 && PathFileExistsW(lpstrPathAndFile))
2603 WCHAR lpstrOverwrite[100];
2604 int answer;
2606 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2607 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2608 MB_YESNO | MB_ICONEXCLAMATION);
2609 if (answer == IDNO || answer == IDCANCEL)
2611 ret = FALSE;
2612 goto ret;
2616 /* In Open dialog: check if it should be created if it doesn't exist */
2617 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2618 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2619 && !PathFileExistsW(lpstrPathAndFile))
2621 WCHAR lpstrCreate[100];
2622 int answer;
2624 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2625 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2626 MB_YESNO | MB_ICONEXCLAMATION);
2627 if (answer == IDNO || answer == IDCANCEL)
2629 ret = FALSE;
2630 goto ret;
2634 /* Check that the size of the file does not exceed buffer size.
2635 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2636 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2637 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2640 /* fill destination buffer */
2641 if (fodInfos->ofnInfos->lpstrFile)
2643 if(fodInfos->unicode)
2645 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2647 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2648 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2649 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2651 else
2653 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2655 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2656 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2657 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2658 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2662 if(fodInfos->unicode)
2664 LPWSTR lpszTemp;
2666 /* set filename offset */
2667 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2668 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2670 /* set extension offset */
2671 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2672 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2674 else
2676 LPSTR lpszTemp;
2677 CHAR tempFileA[MAX_PATH];
2679 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2680 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2681 tempFileA, sizeof(tempFileA), NULL, NULL);
2683 /* set filename offset */
2684 lpszTemp = PathFindFileNameA(tempFileA);
2685 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2687 /* set extension offset */
2688 lpszTemp = PathFindExtensionA(tempFileA);
2689 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2692 /* set the lpstrFileTitle */
2693 if(fodInfos->ofnInfos->lpstrFileTitle)
2695 LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2696 if(fodInfos->unicode)
2698 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2699 lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2701 else
2703 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2704 WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2705 ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2709 /* copy currently selected filter to lpstrCustomFilter */
2710 if (fodInfos->ofnInfos->lpstrCustomFilter)
2712 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2713 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2714 NULL, 0, NULL, NULL);
2715 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2717 LPSTR s = ofn->lpstrCustomFilter;
2718 s += strlen(ofn->lpstrCustomFilter)+1;
2719 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2720 s, len, NULL, NULL);
2725 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2726 goto ret;
2728 FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2730 TRACE("close\n");
2731 FILEDLG95_Clean(hwnd);
2732 ret = EndDialog(hwnd, TRUE);
2734 else
2736 WORD size;
2738 size = lstrlenW(lpstrPathAndFile) + 1;
2739 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2740 size += 1;
2741 /* return needed size in first two bytes of lpstrFile */
2742 if(fodInfos->ofnInfos->lpstrFile)
2743 *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2744 FILEDLG95_Clean(hwnd);
2745 ret = EndDialog(hwnd, FALSE);
2746 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2749 break;
2752 ret:
2753 if(lpsf) IShellFolder_Release(lpsf);
2754 return ret;
2757 /***********************************************************************
2758 * FILEDLG95_SHELL_Init
2760 * Initialisation of the shell objects
2762 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2764 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2766 TRACE("%p\n", hwnd);
2769 * Initialisation of the FileOpenDialogInfos structure
2772 /* Shell */
2774 /*ShellInfos */
2775 fodInfos->ShellInfos.hwndOwner = hwnd;
2777 /* Disable multi-select if flag not set */
2778 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2780 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2782 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2783 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2785 /* Construct the IShellBrowser interface */
2786 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2788 return NOERROR;
2791 /***********************************************************************
2792 * FILEDLG95_SHELL_ExecuteCommand
2794 * Change the folder option and refresh the view
2795 * If the function succeeds, the return value is nonzero.
2797 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2799 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2800 IContextMenu * pcm;
2802 TRACE("(%p,%p)\n", hwnd, lpVerb);
2804 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2805 SVGIO_BACKGROUND,
2806 &IID_IContextMenu,
2807 (LPVOID*)&pcm)))
2809 CMINVOKECOMMANDINFO ci;
2810 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2811 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2812 ci.lpVerb = lpVerb;
2813 ci.hwnd = hwnd;
2815 IContextMenu_InvokeCommand(pcm, &ci);
2816 IContextMenu_Release(pcm);
2819 return FALSE;
2822 /***********************************************************************
2823 * FILEDLG95_SHELL_UpFolder
2825 * Browse to the specified object
2826 * If the function succeeds, the return value is nonzero.
2828 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2830 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2832 TRACE("\n");
2834 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2835 NULL,
2836 SBSP_PARENT)))
2838 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2839 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2840 return TRUE;
2842 return FALSE;
2845 /***********************************************************************
2846 * FILEDLG95_SHELL_BrowseToDesktop
2848 * Browse to the Desktop
2849 * If the function succeeds, the return value is nonzero.
2851 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2853 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2854 LPITEMIDLIST pidl;
2855 HRESULT hres;
2857 TRACE("\n");
2859 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2860 hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2861 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2862 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2863 COMDLG32_SHFree(pidl);
2864 return SUCCEEDED(hres);
2866 /***********************************************************************
2867 * FILEDLG95_SHELL_Clean
2869 * Cleans the memory used by shell objects
2871 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2873 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2875 TRACE("\n");
2877 COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2879 /* clean Shell interfaces */
2880 if (fodInfos->Shell.FOIShellView)
2882 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2883 IShellView_Release(fodInfos->Shell.FOIShellView);
2885 if (fodInfos->Shell.FOIShellFolder)
2886 IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2887 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2888 if (fodInfos->Shell.FOIDataObject)
2889 IDataObject_Release(fodInfos->Shell.FOIDataObject);
2892 /***********************************************************************
2893 * FILEDLG95_FILETYPE_Init
2895 * Initialisation of the file type combo box
2897 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2899 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2900 int nFilters = 0; /* number of filters */
2901 int nFilterIndexCB;
2903 TRACE("%p\n", hwnd);
2905 if(fodInfos->customfilter)
2907 /* customfilter has one entry... title\0ext\0
2908 * Set first entry of combo box item with customfilter
2910 LPWSTR lpstrExt;
2911 LPCWSTR lpstrPos = fodInfos->customfilter;
2913 /* Get the title */
2914 lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2916 /* Copy the extensions */
2917 if (! *lpstrPos) return E_FAIL; /* malformed filter */
2918 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2919 lstrcpyW(lpstrExt,lpstrPos);
2921 /* Add the item at the end of the combo */
2922 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2923 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2924 nFilters++;
2926 if(fodInfos->filter)
2928 LPCWSTR lpstrPos = fodInfos->filter;
2930 for(;;)
2932 /* filter is a list... title\0ext\0......\0\0
2933 * Set the combo item text to the title and the item data
2934 * to the ext
2936 LPCWSTR lpstrDisplay;
2937 LPWSTR lpstrExt;
2939 /* Get the title */
2940 if(! *lpstrPos) break; /* end */
2941 lpstrDisplay = lpstrPos;
2942 lpstrPos += lstrlenW(lpstrPos) + 1;
2944 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2946 nFilters++;
2948 /* Copy the extensions */
2949 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2950 lstrcpyW(lpstrExt,lpstrPos);
2951 lpstrPos += lstrlenW(lpstrPos) + 1;
2953 /* Add the item at the end of the combo */
2954 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
2956 /* malformed filters are added anyway... */
2957 if (!*lpstrExt) break;
2962 * Set the current filter to the one specified
2963 * in the initialisation structure
2965 if (fodInfos->filter || fodInfos->customfilter)
2967 LPWSTR lpstrFilter;
2969 /* Check to make sure our index isn't out of bounds. */
2970 if ( fodInfos->ofnInfos->nFilterIndex >
2971 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
2972 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
2974 /* set default filter index */
2975 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
2976 fodInfos->ofnInfos->nFilterIndex = 1;
2978 /* calculate index of Combo Box item */
2979 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
2980 if (fodInfos->customfilter == NULL)
2981 nFilterIndexCB--;
2983 /* Set the current index selection. */
2984 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
2986 /* Get the corresponding text string from the combo box. */
2987 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2988 nFilterIndexCB);
2990 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
2991 lpstrFilter = NULL;
2993 if(lpstrFilter)
2995 DWORD len;
2996 CharLowerW(lpstrFilter); /* lowercase */
2997 len = lstrlenW(lpstrFilter)+1;
2998 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
2999 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3001 } else
3002 fodInfos->ofnInfos->nFilterIndex = 0;
3003 return S_OK;
3006 /***********************************************************************
3007 * FILEDLG95_FILETYPE_OnCommand
3009 * WM_COMMAND of the file type combo box
3010 * If the function succeeds, the return value is nonzero.
3012 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
3014 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3016 switch(wNotifyCode)
3018 case CBN_SELENDOK:
3020 LPWSTR lpstrFilter;
3022 /* Get the current item of the filetype combo box */
3023 int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
3025 /* set the current filter index */
3026 fodInfos->ofnInfos->nFilterIndex = iItem +
3027 (fodInfos->customfilter == NULL ? 1 : 0);
3029 /* Set the current filter with the current selection */
3030 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3032 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3033 iItem);
3034 if((INT_PTR)lpstrFilter != CB_ERR)
3036 DWORD len;
3037 CharLowerW(lpstrFilter); /* lowercase */
3038 len = lstrlenW(lpstrFilter)+1;
3039 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3040 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3041 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3042 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3045 /* Refresh the actual view to display the included items*/
3046 if (fodInfos->Shell.FOIShellView)
3047 IShellView_Refresh(fodInfos->Shell.FOIShellView);
3050 return FALSE;
3052 /***********************************************************************
3053 * FILEDLG95_FILETYPE_SearchExt
3055 * searches for an extension in the filetype box
3057 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3059 int i, iCount = CBGetCount(hwnd);
3061 TRACE("%s\n", debugstr_w(lpstrExt));
3063 if(iCount != CB_ERR)
3065 for(i=0;i<iCount;i++)
3067 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3068 return i;
3071 return -1;
3074 /***********************************************************************
3075 * FILEDLG95_FILETYPE_Clean
3077 * Clean the memory used by the filetype combo box
3079 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3081 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3082 int iPos;
3083 int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3085 TRACE("\n");
3087 /* Delete each string of the combo and their associated data */
3088 if(iCount != CB_ERR)
3090 for(iPos = iCount-1;iPos>=0;iPos--)
3092 MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3093 CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3096 /* Current filter */
3097 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3101 /***********************************************************************
3102 * FILEDLG95_LOOKIN_Init
3104 * Initialisation of the look in combo box
3107 /* Small helper function, to determine if the unixfs shell extension is rooted
3108 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3110 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3111 HKEY hKey;
3112 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3113 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3114 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3115 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3116 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3117 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3118 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3120 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3121 return FALSE;
3123 RegCloseKey(hKey);
3124 return TRUE;
3127 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3129 IShellFolder *psfRoot, *psfDrives;
3130 IEnumIDList *lpeRoot, *lpeDrives;
3131 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3132 HDC hdc;
3133 TEXTMETRICW tm;
3134 LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3136 TRACE("%p\n", hwndCombo);
3138 liInfos->iMaxIndentation = 0;
3140 SetPropA(hwndCombo, LookInInfosStr, liInfos);
3142 hdc = GetDC( hwndCombo );
3143 SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3144 GetTextMetricsW( hdc, &tm );
3145 ReleaseDC( hwndCombo, hdc );
3147 /* set item height for both text field and listbox */
3148 CBSetItemHeight( hwndCombo, -1, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3149 CBSetItemHeight( hwndCombo, 0, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3151 /* Turn on the extended UI for the combo box like Windows does */
3152 CBSetExtendedUI(hwndCombo, TRUE);
3154 /* Initialise data of Desktop folder */
3155 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3156 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3157 COMDLG32_SHFree(pidlTmp);
3159 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3161 SHGetDesktopFolder(&psfRoot);
3163 if (psfRoot)
3165 /* enumerate the contents of the desktop */
3166 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3168 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3170 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3172 /* If the unixfs extension is rooted, we don't expand the drives by default */
3173 if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3175 /* special handling for CSIDL_DRIVES */
3176 if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3178 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3180 /* enumerate the drives */
3181 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3183 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3185 pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3186 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3187 COMDLG32_SHFree(pidlAbsTmp);
3188 COMDLG32_SHFree(pidlTmp1);
3190 IEnumIDList_Release(lpeDrives);
3192 IShellFolder_Release(psfDrives);
3197 COMDLG32_SHFree(pidlTmp);
3199 IEnumIDList_Release(lpeRoot);
3201 IShellFolder_Release(psfRoot);
3204 COMDLG32_SHFree(pidlDrives);
3207 /***********************************************************************
3208 * FILEDLG95_LOOKIN_DrawItem
3210 * WM_DRAWITEM message handler
3212 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3214 COLORREF crWin = GetSysColor(COLOR_WINDOW);
3215 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3216 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3217 RECT rectText;
3218 RECT rectIcon;
3219 SHFILEINFOW sfi;
3220 HIMAGELIST ilItemImage;
3221 int iIndentation;
3222 TEXTMETRICW tm;
3223 LPSFOLDER tmpFolder;
3224 UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3225 UINT icon_width, icon_height;
3227 TRACE("\n");
3229 if(pDIStruct->itemID == -1)
3230 return 0;
3232 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3233 pDIStruct->itemID)))
3234 return 0;
3237 icon_width = GetSystemMetrics(SM_CXICON);
3238 icon_height = GetSystemMetrics(SM_CYICON);
3239 if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3241 icon_width = GetSystemMetrics(SM_CXSMICON);
3242 icon_height = GetSystemMetrics(SM_CYSMICON);
3243 shgfi_flags |= SHGFI_SMALLICON;
3246 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3247 0, &sfi, sizeof (sfi), shgfi_flags );
3249 /* Is this item selected ? */
3250 if(pDIStruct->itemState & ODS_SELECTED)
3252 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3253 SetBkColor(pDIStruct->hDC,crHighLight);
3254 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3256 else
3258 SetTextColor(pDIStruct->hDC,crText);
3259 SetBkColor(pDIStruct->hDC,crWin);
3260 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3263 /* Do not indent item if drawing in the edit of the combo */
3264 if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3265 iIndentation = 0;
3266 else
3267 iIndentation = tmpFolder->m_iIndent;
3269 /* Draw text and icon */
3271 /* Initialise the icon display area */
3272 rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3273 rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3274 rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3275 rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3277 /* Initialise the text display area */
3278 GetTextMetricsW(pDIStruct->hDC, &tm);
3279 rectText.left = rectIcon.right;
3280 rectText.top =
3281 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3282 rectText.right = pDIStruct->rcItem.right;
3283 rectText.bottom =
3284 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3286 /* Draw the icon from the image list */
3287 ImageList_Draw(ilItemImage,
3288 sfi.iIcon,
3289 pDIStruct->hDC,
3290 rectIcon.left,
3291 rectIcon.top,
3292 ILD_TRANSPARENT );
3294 /* Draw the associated text */
3295 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3296 return NOERROR;
3299 /***********************************************************************
3300 * FILEDLG95_LOOKIN_OnCommand
3302 * LookIn combo box WM_COMMAND message handler
3303 * If the function succeeds, the return value is nonzero.
3305 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3307 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3309 TRACE("%p\n", fodInfos);
3311 switch(wNotifyCode)
3313 case CBN_SELENDOK:
3315 LPSFOLDER tmpFolder;
3316 int iItem;
3318 iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3320 if( iItem == CB_ERR) return FALSE;
3322 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3323 iItem)))
3324 return FALSE;
3327 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3328 tmpFolder->pidlItem,
3329 SBSP_ABSOLUTE)))
3331 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3332 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3333 return TRUE;
3335 break;
3339 return FALSE;
3342 /***********************************************************************
3343 * FILEDLG95_LOOKIN_AddItem
3345 * Adds an absolute pidl item to the lookin combo box
3346 * returns the index of the inserted item
3348 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3350 LPITEMIDLIST pidlNext;
3351 SHFILEINFOW sfi;
3352 SFOLDER *tmpFolder;
3353 LookInInfos *liInfos;
3355 TRACE("%p, %p, %d\n", hwnd, pidl, iInsertId);
3357 if(!pidl)
3358 return -1;
3360 if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3361 return -1;
3363 tmpFolder = MemAlloc(sizeof(SFOLDER));
3364 tmpFolder->m_iIndent = 0;
3366 /* Calculate the indentation of the item in the lookin*/
3367 pidlNext = pidl;
3368 while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3370 tmpFolder->m_iIndent++;
3373 tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3375 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3376 liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3378 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3379 SHGetFileInfoW((LPCWSTR)pidl,
3381 &sfi,
3382 sizeof(sfi),
3383 SHGFI_DISPLAYNAME | SHGFI_PIDL | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3385 TRACE("-- Add %s attr=0x%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3387 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3389 int iItemID;
3391 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3393 /* Add the item at the end of the list */
3394 if(iInsertId < 0)
3396 iItemID = CBAddString(hwnd,sfi.szDisplayName);
3398 /* Insert the item at the iInsertId position*/
3399 else
3401 iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3404 CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3405 return iItemID;
3408 COMDLG32_SHFree( tmpFolder->pidlItem );
3409 MemFree( tmpFolder );
3410 return -1;
3414 /***********************************************************************
3415 * FILEDLG95_LOOKIN_InsertItemAfterParent
3417 * Insert an item below its parent
3419 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3422 LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3423 int iParentPos;
3425 TRACE("\n");
3427 if (pidl == pidlParent)
3428 return -1;
3430 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3432 if(iParentPos < 0)
3434 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3437 /* Free pidlParent memory */
3438 COMDLG32_SHFree(pidlParent);
3440 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3443 /***********************************************************************
3444 * FILEDLG95_LOOKIN_SelectItem
3446 * Adds an absolute pidl item to the lookin combo box
3447 * returns the index of the inserted item
3449 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3451 int iItemPos;
3452 LookInInfos *liInfos;
3454 TRACE("%p, %p\n", hwnd, pidl);
3456 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3458 liInfos = GetPropA(hwnd,LookInInfosStr);
3460 if(iItemPos < 0)
3462 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3463 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3466 else
3468 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3469 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3471 int iRemovedItem;
3473 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3474 break;
3475 if(iRemovedItem < iItemPos)
3476 iItemPos--;
3480 CBSetCurSel(hwnd,iItemPos);
3481 liInfos->uSelectedItem = iItemPos;
3483 return 0;
3487 /***********************************************************************
3488 * FILEDLG95_LOOKIN_RemoveMostExpandedItem
3490 * Remove the item with an expansion level over iExpansionLevel
3492 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3494 int iItemPos;
3495 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3497 TRACE("\n");
3499 if(liInfos->iMaxIndentation <= 2)
3500 return -1;
3502 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3504 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3505 COMDLG32_SHFree(tmpFolder->pidlItem);
3506 MemFree(tmpFolder);
3507 CBDeleteString(hwnd,iItemPos);
3508 liInfos->iMaxIndentation--;
3510 return iItemPos;
3513 return -1;
3516 /***********************************************************************
3517 * FILEDLG95_LOOKIN_SearchItem
3519 * Search for pidl in the lookin combo box
3520 * returns the index of the found item
3522 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3524 int i = 0;
3525 int iCount = CBGetCount(hwnd);
3527 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3529 if (iCount != CB_ERR)
3531 for(;i<iCount;i++)
3533 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3535 if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3536 return i;
3537 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3538 return i;
3542 return -1;
3545 /***********************************************************************
3546 * FILEDLG95_LOOKIN_Clean
3548 * Clean the memory used by the lookin combo box
3550 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3552 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3553 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3554 int iPos;
3555 int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3557 TRACE("\n");
3559 /* Delete each string of the combo and their associated data */
3560 if (iCount != CB_ERR)
3562 for(iPos = iCount-1;iPos>=0;iPos--)
3564 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3565 COMDLG32_SHFree(tmpFolder->pidlItem);
3566 MemFree(tmpFolder);
3567 CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3571 /* LookInInfos structure */
3572 MemFree(liInfos);
3573 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3576 /***********************************************************************
3577 * get_def_format
3579 * Fill the FORMATETC used in the shell id list
3581 static FORMATETC get_def_format(void)
3583 static CLIPFORMAT cfFormat;
3584 FORMATETC formatetc;
3586 if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA);
3587 formatetc.cfFormat = cfFormat;
3588 formatetc.ptd = 0;
3589 formatetc.dwAspect = DVASPECT_CONTENT;
3590 formatetc.lindex = -1;
3591 formatetc.tymed = TYMED_HGLOBAL;
3592 return formatetc;
3595 /***********************************************************************
3596 * FILEDLG95_FILENAME_FillFromSelection
3598 * fills the edit box from the cached DataObject
3600 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3602 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3603 LPITEMIDLIST pidl;
3604 LPWSTR lpstrAllFiles, lpstrTmp;
3605 UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength;
3606 STGMEDIUM medium;
3607 LPIDA cida;
3608 FORMATETC formatetc = get_def_format();
3610 TRACE("\n");
3612 if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium)))
3613 return;
3615 cida = GlobalLock(medium.u.hGlobal);
3616 nFileSelected = cida->cidl;
3618 /* Allocate a buffer */
3619 nAllFilesMaxLength = MAX_PATH + 3;
3620 lpstrAllFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nAllFilesMaxLength * sizeof(WCHAR));
3621 if (!lpstrAllFiles)
3622 goto ret;
3624 /* Loop through the selection, handle only files (not folders) */
3625 for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++)
3627 pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]);
3628 if (pidl)
3630 if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl))
3632 if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength)
3634 nAllFilesMaxLength *= 2;
3635 lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR));
3636 if (!lpstrTmp)
3637 goto ret;
3638 lpstrAllFiles = lpstrTmp;
3640 nFiles += 1;
3641 lpstrAllFiles[nAllFilesLength++] = '"';
3642 GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength);
3643 nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength);
3644 nAllFilesLength += nThisFileLength;
3645 lpstrAllFiles[nAllFilesLength++] = '"';
3646 lpstrAllFiles[nAllFilesLength++] = ' ';
3651 if (nFiles != 0)
3653 /* If there's only one file, use the name as-is without quotes */
3654 lpstrTmp = lpstrAllFiles;
3655 if (nFiles == 1)
3657 lpstrTmp += 1;
3658 lpstrTmp[nThisFileLength] = 0;
3660 SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp);
3661 /* Select the file name like Windows does */
3662 if (filename_is_edit(fodInfos))
3663 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3666 ret:
3667 HeapFree(GetProcessHeap(), 0, lpstrAllFiles);
3668 COMCTL32_ReleaseStgMedium(medium);
3672 /* copied from shell32 to avoid linking to it
3673 * Although shell32 is already linked the behaviour of exported StrRetToStrN
3674 * is dependent on whether emulated OS is unicode or not.
3676 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3678 switch (src->uType)
3680 case STRRET_WSTR:
3681 lstrcpynW(dest, src->u.pOleStr, len);
3682 COMDLG32_SHFree(src->u.pOleStr);
3683 break;
3685 case STRRET_CSTR:
3686 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3687 dest[len-1] = 0;
3688 break;
3690 case STRRET_OFFSET:
3691 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3692 dest[len-1] = 0;
3693 break;
3695 default:
3696 FIXME("unknown type %x!\n", src->uType);
3697 if (len) *dest = '\0';
3698 return E_FAIL;
3700 return S_OK;
3703 /***********************************************************************
3704 * FILEDLG95_FILENAME_GetFileNames
3706 * Copies the filenames to a delimited string list.
3708 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3710 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3711 UINT nFileCount = 0; /* number of files */
3712 UINT nStrLen = 0; /* length of string in edit control */
3713 LPWSTR lpstrEdit; /* buffer for string from edit control */
3715 TRACE("\n");
3717 /* get the filenames from the filename control */
3718 nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
3719 lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3720 GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
3722 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3724 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3725 MemFree(lpstrEdit);
3726 return nFileCount;
3730 * DATAOBJECT Helper functions
3733 /***********************************************************************
3734 * COMCTL32_ReleaseStgMedium
3736 * like ReleaseStgMedium from ole32
3738 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3740 if(medium.pUnkForRelease)
3742 IUnknown_Release(medium.pUnkForRelease);
3744 else
3746 GlobalUnlock(medium.u.hGlobal);
3747 GlobalFree(medium.u.hGlobal);
3751 /***********************************************************************
3752 * GetPidlFromDataObject
3754 * Return pidl(s) by number from the cached DataObject
3756 * nPidlIndex=0 gets the fully qualified root path
3758 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3761 STGMEDIUM medium;
3762 FORMATETC formatetc = get_def_format();
3763 LPITEMIDLIST pidl = NULL;
3765 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3767 if (!doSelected)
3768 return NULL;
3770 /* Get the pidls from IDataObject */
3771 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3773 LPIDA cida = GlobalLock(medium.u.hGlobal);
3774 if(nPidlIndex <= cida->cidl)
3776 pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3778 COMCTL32_ReleaseStgMedium(medium);
3780 return pidl;
3783 /***********************************************************************
3784 * GetNumSelected
3786 * Return the number of selected items in the DataObject.
3789 static UINT GetNumSelected( IDataObject *doSelected )
3791 UINT retVal = 0;
3792 STGMEDIUM medium;
3793 FORMATETC formatetc = get_def_format();
3795 TRACE("sv=%p\n", doSelected);
3797 if (!doSelected) return 0;
3799 /* Get the pidls from IDataObject */
3800 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3802 LPIDA cida = GlobalLock(medium.u.hGlobal);
3803 retVal = cida->cidl;
3804 COMCTL32_ReleaseStgMedium(medium);
3805 return retVal;
3807 return 0;
3811 * TOOLS
3814 /***********************************************************************
3815 * GetName
3817 * Get the pidl's display name (relative to folder) and
3818 * put it in lpstrFileName.
3820 * Return NOERROR on success,
3821 * E_FAIL otherwise
3824 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3826 STRRET str;
3827 HRESULT hRes;
3829 TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3831 if(!lpsf)
3833 SHGetDesktopFolder(&lpsf);
3834 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3835 IShellFolder_Release(lpsf);
3836 return hRes;
3839 /* Get the display name of the pidl relative to the folder */
3840 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3842 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3844 return E_FAIL;
3847 /***********************************************************************
3848 * GetShellFolderFromPidl
3850 * pidlRel is the item pidl relative
3851 * Return the IShellFolder of the absolute pidl
3853 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3855 IShellFolder *psf = NULL,*psfParent;
3857 TRACE("%p\n", pidlAbs);
3859 if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3861 psf = psfParent;
3862 if(pidlAbs && pidlAbs->mkid.cb)
3864 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3866 IShellFolder_Release(psfParent);
3867 return psf;
3870 /* return the desktop */
3871 return psfParent;
3873 return NULL;
3876 /***********************************************************************
3877 * GetParentPidl
3879 * Return the LPITEMIDLIST to the parent of the pidl in the list
3881 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3883 LPITEMIDLIST pidlParent;
3885 TRACE("%p\n", pidl);
3887 pidlParent = COMDLG32_PIDL_ILClone(pidl);
3888 COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3890 return pidlParent;
3893 /***********************************************************************
3894 * GetPidlFromName
3896 * returns the pidl of the file name relative to folder
3897 * NULL if an error occurred
3899 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3901 LPITEMIDLIST pidl = NULL;
3902 ULONG ulEaten;
3904 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3906 if(!lpcstrFileName) return NULL;
3907 if(!*lpcstrFileName) return NULL;
3909 if(!lpsf)
3911 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3912 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3913 IShellFolder_Release(lpsf);
3916 else
3918 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3920 return pidl;
3925 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3927 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3928 HRESULT ret;
3930 TRACE("%p, %p\n", psf, pidl);
3932 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3934 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
3935 /* see documentation shell 4.1*/
3936 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3939 /***********************************************************************
3940 * BrowseSelectedFolder
3942 static BOOL BrowseSelectedFolder(HWND hwnd)
3944 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3945 BOOL bBrowseSelFolder = FALSE;
3947 TRACE("\n");
3949 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
3951 LPITEMIDLIST pidlSelection;
3953 /* get the file selected */
3954 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
3955 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
3957 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
3958 pidlSelection, SBSP_RELATIVE ) ) )
3960 WCHAR buf[64];
3961 LoadStringW( COMDLG32_hInstance, IDS_PATHNOTEXISTING, buf, sizeof(buf)/sizeof(WCHAR) );
3962 MessageBoxW( hwnd, buf, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
3964 bBrowseSelFolder = TRUE;
3965 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3966 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
3968 COMDLG32_SHFree( pidlSelection );
3971 return bBrowseSelFolder;
3975 * Memory allocation methods */
3976 static void *MemAlloc(UINT size)
3978 return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
3981 static void MemFree(void *mem)
3983 HeapFree(GetProcessHeap(),0,mem);
3986 static inline BOOL valid_struct_size( DWORD size )
3988 return (size == OPENFILENAME_SIZE_VERSION_400W) ||
3989 (size == sizeof( OPENFILENAMEW ));
3992 static inline BOOL is_win16_looks(DWORD flags)
3994 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
3995 !(flags & OFN_EXPLORER));
3998 /* ------------------ APIs ---------------------- */
4000 /***********************************************************************
4001 * GetOpenFileNameA (COMDLG32.@)
4003 * Creates a dialog box for the user to select a file to open.
4005 * RETURNS
4006 * TRUE on success: user enters a valid file
4007 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4010 BOOL WINAPI GetOpenFileNameA(OPENFILENAMEA *ofn)
4012 TRACE("flags 0x%08x\n", ofn->Flags);
4014 if (!valid_struct_size( ofn->lStructSize ))
4016 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4017 return FALSE;
4020 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4021 if (ofn->Flags & OFN_FILEMUSTEXIST)
4022 ofn->Flags |= OFN_PATHMUSTEXIST;
4024 if (is_win16_looks(ofn->Flags))
4025 return GetFileName31A(ofn, OPEN_DIALOG);
4026 else
4028 FileOpenDlgInfos info;
4030 init_filedlg_infoA(ofn, &info);
4031 return GetFileDialog95(&info, OPEN_DIALOG);
4035 /***********************************************************************
4036 * GetOpenFileNameW (COMDLG32.@)
4038 * Creates a dialog box for the user to select a file to open.
4040 * RETURNS
4041 * TRUE on success: user enters a valid file
4042 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4045 BOOL WINAPI GetOpenFileNameW(OPENFILENAMEW *ofn)
4047 TRACE("flags 0x%08x\n", ofn->Flags);
4049 if (!valid_struct_size( ofn->lStructSize ))
4051 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4052 return FALSE;
4055 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4056 if (ofn->Flags & OFN_FILEMUSTEXIST)
4057 ofn->Flags |= OFN_PATHMUSTEXIST;
4059 if (is_win16_looks(ofn->Flags))
4060 return GetFileName31W(ofn, OPEN_DIALOG);
4061 else
4063 FileOpenDlgInfos info;
4065 init_filedlg_infoW(ofn, &info);
4066 return GetFileDialog95(&info, OPEN_DIALOG);
4071 /***********************************************************************
4072 * GetSaveFileNameA (COMDLG32.@)
4074 * Creates a dialog box for the user to select a file to save.
4076 * RETURNS
4077 * TRUE on success: user enters a valid file
4078 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4081 BOOL WINAPI GetSaveFileNameA(OPENFILENAMEA *ofn)
4083 if (!valid_struct_size( ofn->lStructSize ))
4085 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4086 return FALSE;
4089 if (is_win16_looks(ofn->Flags))
4090 return GetFileName31A(ofn, SAVE_DIALOG);
4091 else
4093 FileOpenDlgInfos info;
4095 init_filedlg_infoA(ofn, &info);
4096 return GetFileDialog95(&info, SAVE_DIALOG);
4100 /***********************************************************************
4101 * GetSaveFileNameW (COMDLG32.@)
4103 * Creates a dialog box for the user to select a file to save.
4105 * RETURNS
4106 * TRUE on success: user enters a valid file
4107 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4110 BOOL WINAPI GetSaveFileNameW(
4111 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4113 if (!valid_struct_size( ofn->lStructSize ))
4115 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4116 return FALSE;
4119 if (is_win16_looks(ofn->Flags))
4120 return GetFileName31W(ofn, SAVE_DIALOG);
4121 else
4123 FileOpenDlgInfos info;
4125 init_filedlg_infoW(ofn, &info);
4126 return GetFileDialog95(&info, SAVE_DIALOG);
4130 /***********************************************************************
4131 * GetFileTitleA (COMDLG32.@)
4133 * See GetFileTitleW.
4135 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4137 int ret;
4138 UNICODE_STRING strWFile;
4139 LPWSTR lpWTitle;
4141 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4142 lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4143 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4144 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4145 RtlFreeUnicodeString( &strWFile );
4146 RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4147 return ret;
4151 /***********************************************************************
4152 * GetFileTitleW (COMDLG32.@)
4154 * Get the name of a file.
4156 * PARAMS
4157 * lpFile [I] name and location of file
4158 * lpTitle [O] returned file name
4159 * cbBuf [I] buffer size of lpTitle
4161 * RETURNS
4162 * Success: zero
4163 * Failure: negative number.
4165 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4167 int i, len;
4168 static const WCHAR brkpoint[] = {'*','[',']',0};
4169 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4171 if(lpFile == NULL || lpTitle == NULL)
4172 return -1;
4174 len = lstrlenW(lpFile);
4176 if (len == 0)
4177 return -1;
4179 if(strpbrkW(lpFile, brkpoint))
4180 return -1;
4182 len--;
4184 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4185 return -1;
4187 for(i = len; i >= 0; i--)
4189 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':')
4191 i++;
4192 break;
4196 if(i == -1)
4197 i++;
4199 TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4201 len = lstrlenW(lpFile+i)+1;
4202 if(cbBuf < len)
4203 return len;
4205 lstrcpyW(lpTitle, &lpFile[i]);
4206 return 0;