devenum: Implement IMoniker::GetClassID().
[wine.git] / dlls / comdlg32 / filedlg.c
blob305ac62d72ee7407ca9ab2abf0ec1f704d694ef6
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("\n");
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 = 0;
858 TRACE("%p 0x%04x\n",hwndParentDlg, uCode);
860 if(!fodInfos) return 0;
862 if(fodInfos->DlgInfos.hwndCustomDlg)
864 TRACE("CALL NOTIFY for %x\n", uCode);
865 if(fodInfos->unicode)
867 OFNOTIFYW ofnNotify;
868 ofnNotify.hdr.hwndFrom=hwndParentDlg;
869 ofnNotify.hdr.idFrom=0;
870 ofnNotify.hdr.code = uCode;
871 ofnNotify.lpOFN = fodInfos->ofnInfos;
872 ofnNotify.pszFile = NULL;
873 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
875 else
877 OFNOTIFYA ofnNotify;
878 ofnNotify.hdr.hwndFrom=hwndParentDlg;
879 ofnNotify.hdr.idFrom=0;
880 ofnNotify.hdr.code = uCode;
881 ofnNotify.lpOFN = (LPOPENFILENAMEA)fodInfos->ofnInfos;
882 ofnNotify.pszFile = NULL;
883 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
885 TRACE("RET NOTIFY\n");
887 TRACE("Retval: 0x%08lx\n", hook_result);
888 return hook_result;
891 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
893 UINT len, total;
894 WCHAR *p, *buffer;
895 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
897 TRACE("CDM_GETFILEPATH:\n");
899 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
900 return -1;
902 /* get path and filenames */
903 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
904 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
905 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
906 if (len)
908 p = buffer + strlenW(buffer);
909 *p++ = '\\';
910 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
912 if (fodInfos->unicode)
914 total = strlenW( buffer) + 1;
915 if (result) lstrcpynW( result, buffer, size );
916 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
918 else
920 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
921 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
922 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
924 HeapFree( GetProcessHeap(), 0, buffer );
925 return total;
928 /***********************************************************************
929 * FILEDLG95_HandleCustomDialogMessages
931 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
933 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
935 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
936 WCHAR lpstrPath[MAX_PATH];
937 INT_PTR retval;
939 if(!fodInfos) return FALSE;
941 switch(uMsg)
943 case CDM_GETFILEPATH:
944 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
945 break;
947 case CDM_GETFOLDERPATH:
948 TRACE("CDM_GETFOLDERPATH:\n");
949 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
950 if (lParam)
952 if (fodInfos->unicode)
953 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
954 else
955 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
956 (LPSTR)lParam, (int)wParam, NULL, NULL);
958 retval = lstrlenW(lpstrPath) + 1;
959 break;
961 case CDM_GETFOLDERIDLIST:
962 retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
963 if (retval <= wParam)
964 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
965 break;
967 case CDM_GETSPEC:
968 TRACE("CDM_GETSPEC:\n");
969 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
970 if (lParam)
972 if (fodInfos->unicode)
973 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
974 else
975 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
977 break;
979 case CDM_SETCONTROLTEXT:
980 TRACE("CDM_SETCONTROLTEXT:\n");
981 if ( lParam )
983 if( fodInfos->unicode )
984 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
985 else
986 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
988 retval = TRUE;
989 break;
991 case CDM_HIDECONTROL:
992 /* MSDN states that it should fail for not OFN_EXPLORER case */
993 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
995 HWND control = GetDlgItem( hwnd, wParam );
996 if (control) ShowWindow( control, SW_HIDE );
997 retval = TRUE;
999 else retval = FALSE;
1000 break;
1002 default:
1003 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1004 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
1005 return FALSE;
1007 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
1008 return TRUE;
1011 /***********************************************************************
1012 * FILEDLG95_OnWMGetMMI
1014 * WM_GETMINMAXINFO message handler for resizable dialogs
1016 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
1018 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1019 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1020 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1022 mmiptr->ptMinTrackSize = fodInfos->initial_size;
1024 return TRUE;
1027 /***********************************************************************
1028 * FILEDLG95_OnWMSize
1030 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1032 * FIXME: this could be made more elaborate. Now use a simple scheme
1033 * where the file view is enlarged and the controls are either moved
1034 * vertically or horizontally to get out of the way. Only the "grip"
1035 * is moved in both directions to stay in the corner.
1037 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1039 RECT rc, rcview;
1040 int chgx, chgy;
1041 HWND ctrl;
1042 HDWP hdwp;
1043 FileOpenDlgInfos *fodInfos;
1045 if( wParam != SIZE_RESTORED) return FALSE;
1046 fodInfos = get_filedlg_infoptr(hwnd);
1047 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1048 /* get the new dialog rectangle */
1049 GetWindowRect( hwnd, &rc);
1050 TRACE("Size from %d,%d to %d,%d\n", fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1051 rc.right -rc.left, rc.bottom -rc.top);
1052 /* not initialized yet */
1053 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1054 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1055 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1056 return FALSE;
1057 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1058 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1059 fodInfos->sizedlg.cx = rc.right - rc.left;
1060 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1061 /* change the size of the view window */
1062 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1063 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1064 hdwp = BeginDeferWindowPos( 10);
1065 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1066 rcview.right - rcview.left + chgx,
1067 rcview.bottom - rcview.top + chgy,
1068 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1069 /* change position and sizes of the controls */
1070 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1072 int ctrlid = GetDlgCtrlID( ctrl);
1073 GetWindowRect( ctrl, &rc);
1074 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1075 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1077 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1078 0, 0,
1079 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1081 else if( rc.top > rcview.bottom)
1083 /* if it was below the shell view
1084 * move to bottom */
1085 switch( ctrlid)
1087 /* file name (edit or comboboxex) and file types combo change also width */
1088 case edt1:
1089 case cmb13:
1090 case cmb1:
1091 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1092 rc.right - rc.left + chgx, rc.bottom - rc.top,
1093 SWP_NOACTIVATE | SWP_NOZORDER);
1094 break;
1095 /* then these buttons must move out of the way */
1096 case IDOK:
1097 case IDCANCEL:
1098 case pshHelp:
1099 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1100 0, 0,
1101 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1102 break;
1103 default:
1104 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1105 0, 0,
1106 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1109 else if( rc.left > rcview.right)
1111 /* if it was to the right of the shell view
1112 * move to right */
1113 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1114 0, 0,
1115 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1117 else
1118 /* special cases */
1120 switch( ctrlid)
1122 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1123 case IDC_LOOKIN:
1124 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1125 rc.right - rc.left + chgx, rc.bottom - rc.top,
1126 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1127 break;
1128 case IDC_TOOLBARSTATIC:
1129 case IDC_TOOLBAR:
1130 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1131 0, 0,
1132 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1133 break;
1134 #endif
1135 /* not resized in windows. Since wine uses this invisible control
1136 * to size the browser view it needs to be resized */
1137 case IDC_SHELLSTATIC:
1138 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1139 rc.right - rc.left + chgx,
1140 rc.bottom - rc.top + chgy,
1141 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1142 break;
1146 if(fodInfos->DlgInfos.hwndCustomDlg &&
1147 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1149 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1150 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1152 GetWindowRect( ctrl, &rc);
1153 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1154 if( rc.top > rcview.bottom)
1156 /* if it was below the shell view
1157 * move to bottom */
1158 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1159 rc.right - rc.left, rc.bottom - rc.top,
1160 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1162 else if( rc.left > rcview.right)
1164 /* if it was to the right of the shell view
1165 * move to right */
1166 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1167 rc.right - rc.left, rc.bottom - rc.top,
1168 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1171 /* size the custom dialog at the end: some applications do some
1172 * control re-arranging at this point */
1173 GetClientRect(hwnd, &rc);
1174 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1175 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1177 EndDeferWindowPos( hdwp);
1178 /* should not be needed */
1179 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1180 return TRUE;
1183 /***********************************************************************
1184 * FileOpenDlgProc95
1186 * File open dialog procedure
1188 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1190 #if 0
1191 TRACE("%p 0x%04x\n", hwnd, uMsg);
1192 #endif
1194 switch(uMsg)
1196 case WM_INITDIALOG:
1198 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1199 RECT rc, rcstc;
1200 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1201 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1203 /* Some shell namespace extensions depend on COM being initialized. */
1204 if (SUCCEEDED(OleInitialize(NULL)))
1205 fodInfos->ole_initialized = TRUE;
1207 SetPropW(hwnd, filedlg_info_propnameW, fodInfos);
1209 FILEDLG95_InitControls(hwnd);
1211 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1213 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1214 DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1215 RECT client, client_adjusted;
1217 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1219 style |= WS_SIZEBOX;
1220 ex_style |= WS_EX_WINDOWEDGE;
1222 else
1223 style &= ~WS_SIZEBOX;
1224 SetWindowLongW(hwnd, GWL_STYLE, style);
1225 SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1227 GetClientRect( hwnd, &client );
1228 GetClientRect( hwnd, &client_adjusted );
1229 AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1231 GetWindowRect( hwnd, &rc );
1232 rc.right += client_adjusted.right - client.right;
1233 rc.bottom += client_adjusted.bottom - client.bottom;
1234 SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1235 SWP_NOZORDER | SWP_NOMOVE);
1237 GetWindowRect( hwnd, &rc );
1238 fodInfos->DlgInfos.hwndGrip =
1239 CreateWindowExA( 0, "SCROLLBAR", NULL,
1240 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1241 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1242 rc.right - gripx, rc.bottom - gripy,
1243 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1246 fodInfos->DlgInfos.hwndCustomDlg =
1247 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1249 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1250 FILEDLG95_FillControls(hwnd, wParam, lParam);
1252 if( fodInfos->DlgInfos.hwndCustomDlg)
1253 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1255 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1256 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1257 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1260 /* if the app has changed the position of the invisible listbox,
1261 * change that of the listview (browser) as well */
1262 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1263 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1264 if( !EqualRect( &rc, &rcstc))
1266 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1267 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1268 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1269 SWP_NOACTIVATE | SWP_NOZORDER);
1272 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1274 GetWindowRect( hwnd, &rc);
1275 fodInfos->sizedlg.cx = rc.right - rc.left;
1276 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1277 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1278 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1279 GetClientRect( hwnd, &rc);
1280 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1281 rc.right - gripx, rc.bottom - gripy,
1282 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1283 /* resize the dialog to the previous invocation */
1284 if( MemDialogSize.cx && MemDialogSize.cy)
1285 SetWindowPos( hwnd, NULL,
1286 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1287 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1290 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1291 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1293 return 0;
1295 case WM_SIZE:
1296 return FILEDLG95_OnWMSize(hwnd, wParam);
1297 case WM_GETMINMAXINFO:
1298 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1299 case WM_COMMAND:
1300 return FILEDLG95_OnWMCommand(hwnd, wParam);
1301 case WM_DRAWITEM:
1303 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1305 case IDC_LOOKIN:
1306 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1307 return TRUE;
1310 return FALSE;
1312 case WM_GETISHELLBROWSER:
1313 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1315 case WM_DESTROY:
1317 FileOpenDlgInfos * fodInfos = get_filedlg_infoptr(hwnd);
1318 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1319 MemDialogSize = fodInfos->sizedlg;
1320 RemovePropW(hwnd, filedlg_info_propnameW);
1321 return FALSE;
1323 case WM_NOTIFY:
1325 LPNMHDR lpnmh = (LPNMHDR)lParam;
1326 UINT stringId = -1;
1328 /* set up the button tooltips strings */
1329 if(TTN_GETDISPINFOA == lpnmh->code )
1331 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1332 switch(lpnmh->idFrom )
1334 /* Up folder button */
1335 case FCIDM_TB_UPFOLDER:
1336 stringId = IDS_UPFOLDER;
1337 break;
1338 /* New folder button */
1339 case FCIDM_TB_NEWFOLDER:
1340 stringId = IDS_NEWFOLDER;
1341 break;
1342 /* List option button */
1343 case FCIDM_TB_SMALLICON:
1344 stringId = IDS_LISTVIEW;
1345 break;
1346 /* Details option button */
1347 case FCIDM_TB_REPORTVIEW:
1348 stringId = IDS_REPORTVIEW;
1349 break;
1350 /* Desktop button */
1351 case FCIDM_TB_DESKTOP:
1352 stringId = IDS_TODESKTOP;
1353 break;
1354 default:
1355 stringId = 0;
1357 lpdi->hinst = COMDLG32_hInstance;
1358 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1360 return FALSE;
1362 default :
1363 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1364 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1365 return FALSE;
1369 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1371 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1372 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1375 /***********************************************************************
1376 * FILEDLG95_InitControls
1378 * WM_INITDIALOG message handler (before hook notification)
1380 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1382 BOOL win2000plus = FALSE;
1383 BOOL win98plus = FALSE;
1384 BOOL handledPath = FALSE;
1385 OSVERSIONINFOW osVi;
1386 static const WCHAR szwSlash[] = { '\\', 0 };
1387 static const WCHAR szwStar[] = { '*',0 };
1389 static const TBBUTTON tbb[] =
1391 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1392 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1393 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1394 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1395 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1396 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1397 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1398 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1399 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1401 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1403 RECT rectTB;
1404 RECT rectlook;
1406 HIMAGELIST toolbarImageList;
1407 SHFILEINFOA shFileInfo;
1408 ITEMIDLIST *desktopPidl;
1410 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1412 TRACE("%p\n", fodInfos);
1414 /* Get windows version emulating */
1415 osVi.dwOSVersionInfoSize = sizeof(osVi);
1416 GetVersionExW(&osVi);
1417 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1418 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1419 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1420 win2000plus = (osVi.dwMajorVersion > 4);
1421 if (win2000plus) win98plus = TRUE;
1423 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1426 /* Use either the edit or the comboboxex for the filename control */
1427 if (filename_is_edit( fodInfos ))
1429 DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1430 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1432 else
1434 DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1435 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1438 /* Get the hwnd of the controls */
1439 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1440 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1442 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1443 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1445 /* construct the toolbar */
1446 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1447 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1449 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1450 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1451 rectTB.left = rectlook.right;
1452 rectTB.top = rectlook.top-1;
1454 if (fodInfos->unicode)
1455 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1456 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1457 rectTB.left, rectTB.top,
1458 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1459 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1460 else
1461 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1462 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1463 rectTB.left, rectTB.top,
1464 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1465 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1467 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1469 /* FIXME: use TB_LOADIMAGES when implemented */
1470 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1471 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1472 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1474 /* Retrieve and add desktop icon to the toolbar */
1475 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1476 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1477 SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1478 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1479 ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1481 DestroyIcon(shFileInfo.hIcon);
1482 CoTaskMemFree(desktopPidl);
1484 /* Finish Toolbar Construction */
1485 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1486 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1488 /* Set the window text with the text specified in the OPENFILENAME structure */
1489 if(fodInfos->title)
1491 SetWindowTextW(hwnd,fodInfos->title);
1493 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1495 WCHAR buf[64];
1496 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1497 SetWindowTextW(hwnd, buf);
1500 /* Initialise the file name edit control */
1501 handledPath = FALSE;
1502 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1504 if(fodInfos->filename)
1506 /* 1. If win2000 or higher and filename contains a path, use it
1507 in preference over the lpstrInitialDir */
1508 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1509 WCHAR tmpBuf[MAX_PATH];
1510 WCHAR *nameBit;
1511 DWORD result;
1513 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1514 if (result) {
1516 /* nameBit is always shorter than the original filename. It may be NULL
1517 * when the filename contains only a drive name instead of file name */
1518 if (nameBit)
1520 lstrcpyW(fodInfos->filename,nameBit);
1521 *nameBit = 0x00;
1523 else
1524 *fodInfos->filename = '\0';
1526 MemFree(fodInfos->initdir);
1527 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1528 lstrcpyW(fodInfos->initdir, tmpBuf);
1529 handledPath = TRUE;
1530 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1531 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1533 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1535 } else {
1536 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1540 /* 2. (All platforms) If initdir is not null, then use it */
1541 if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1543 /* Work out the proper path as supplied one might be relative */
1544 /* (Here because supplying '.' as dir browses to My Computer) */
1545 WCHAR tmpBuf[MAX_PATH];
1546 WCHAR tmpBuf2[MAX_PATH];
1547 WCHAR *nameBit;
1548 DWORD result;
1550 lstrcpyW(tmpBuf, fodInfos->initdir);
1551 if (PathFileExistsW(tmpBuf)) {
1552 /* initdir does not have to be a directory. If a file is
1553 * specified, the dir part is taken */
1554 if (PathIsDirectoryW(tmpBuf)) {
1555 PathAddBackslashW(tmpBuf);
1556 lstrcatW(tmpBuf, szwStar);
1558 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1559 if (result) {
1560 *nameBit = 0x00;
1561 MemFree(fodInfos->initdir);
1562 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1563 lstrcpyW(fodInfos->initdir, tmpBuf2);
1564 handledPath = TRUE;
1565 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1568 else if (fodInfos->initdir)
1570 MemFree(fodInfos->initdir);
1571 fodInfos->initdir = NULL;
1572 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1576 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1578 /* 3. All except w2k+: if filename contains a path use it */
1579 if (!win2000plus && fodInfos->filename &&
1580 *fodInfos->filename &&
1581 strpbrkW(fodInfos->filename, szwSlash)) {
1582 WCHAR tmpBuf[MAX_PATH];
1583 WCHAR *nameBit;
1584 DWORD result;
1586 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1587 tmpBuf, &nameBit);
1588 if (result) {
1589 int len;
1591 /* nameBit is always shorter than the original filename */
1592 lstrcpyW(fodInfos->filename, nameBit);
1593 *nameBit = 0x00;
1595 len = lstrlenW(tmpBuf);
1596 MemFree(fodInfos->initdir);
1597 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1598 lstrcpyW(fodInfos->initdir, tmpBuf);
1600 handledPath = TRUE;
1601 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1602 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1604 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1607 /* 4. Win2000+: Recently used */
1608 if (!handledPath && win2000plus) {
1609 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1610 fodInfos->initdir[0] = '\0';
1612 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1614 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1615 handledPath = TRUE;
1616 }else{
1617 MemFree(fodInfos->initdir);
1618 fodInfos->initdir = NULL;
1622 /* 5. win98+ and win2000+ if any files of specified filter types in
1623 current directory, use it */
1624 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1626 LPCWSTR lpstrPos = fodInfos->filter;
1627 WIN32_FIND_DATAW FindFileData;
1628 HANDLE hFind;
1630 while (1)
1632 /* filter is a list... title\0ext\0......\0\0 */
1634 /* Skip the title */
1635 if(! *lpstrPos) break; /* end */
1636 lpstrPos += lstrlenW(lpstrPos) + 1;
1638 /* See if any files exist in the current dir with this extension */
1639 if(! *lpstrPos) break; /* end */
1641 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1643 if (hFind == INVALID_HANDLE_VALUE) {
1644 /* None found - continue search */
1645 lpstrPos += lstrlenW(lpstrPos) + 1;
1647 } else {
1649 MemFree(fodInfos->initdir);
1650 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1651 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1653 handledPath = TRUE;
1654 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1655 debugstr_w(lpstrPos));
1656 FindClose(hFind);
1657 break;
1662 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1663 if (!handledPath && (win2000plus || win98plus)) {
1664 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1666 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1668 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1670 /* last fallback */
1671 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1672 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1673 } else {
1674 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1676 } else {
1677 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1679 handledPath = TRUE;
1680 } else if (!handledPath) {
1681 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1682 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1683 handledPath = TRUE;
1684 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1687 SetFocus( fodInfos->DlgInfos.hwndFileName );
1688 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1690 /* Must the open as read only check box be checked ?*/
1691 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1693 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1696 /* Must the open as read only check box be hidden? */
1697 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1699 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1700 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1703 /* Must the help button be hidden? */
1704 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1706 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1707 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1710 /* change Open to Save */
1711 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1713 WCHAR buf[16];
1714 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1715 SetDlgItemTextW(hwnd, IDOK, buf);
1716 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1717 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1720 /* Initialize the filter combo box */
1721 FILEDLG95_FILETYPE_Init(hwnd);
1723 return 0;
1726 /***********************************************************************
1727 * FILEDLG95_ResizeControls
1729 * WM_INITDIALOG message handler (after hook notification)
1731 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1733 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1735 if (fodInfos->DlgInfos.hwndCustomDlg)
1737 RECT rc;
1738 UINT flags = SWP_NOACTIVATE;
1740 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1741 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1743 /* resize the custom dialog to the parent size */
1744 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1745 GetClientRect(hwnd, &rc);
1746 else
1748 /* our own fake template is zero sized and doesn't have children, so
1749 * there is no need to resize it. Picasa depends on it.
1751 flags |= SWP_NOSIZE;
1752 SetRectEmpty(&rc);
1754 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1755 0, 0, rc.right, rc.bottom, flags);
1757 else
1759 /* Resize the height; if opened as read-only, checkbox and help button are
1760 * hidden and we are not using a custom template nor a customDialog
1762 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1763 (!(fodInfos->ofnInfos->Flags &
1764 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1766 RECT rectDlg, rectHelp, rectCancel;
1767 GetWindowRect(hwnd, &rectDlg);
1768 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1769 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1770 /* subtract the height of the help button plus the space between the help
1771 * button and the cancel button to the height of the dialog
1773 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1774 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1775 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1778 return TRUE;
1781 /***********************************************************************
1782 * FILEDLG95_FillControls
1784 * WM_INITDIALOG message handler (after hook notification)
1786 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1788 LPITEMIDLIST pidlItemId = NULL;
1790 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1792 TRACE("dir=%s file=%s\n",
1793 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1795 /* Get the initial directory pidl */
1797 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1799 WCHAR path[MAX_PATH];
1801 GetCurrentDirectoryW(MAX_PATH,path);
1802 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1805 /* Initialise shell objects */
1806 FILEDLG95_SHELL_Init(hwnd);
1808 /* Initialize the Look In combo box */
1809 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1811 /* Browse to the initial directory */
1812 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1814 /* Free pidlItem memory */
1815 COMDLG32_SHFree(pidlItemId);
1817 return TRUE;
1819 /***********************************************************************
1820 * FILEDLG95_Clean
1822 * Regroups all the cleaning functions of the filedlg
1824 void FILEDLG95_Clean(HWND hwnd)
1826 FILEDLG95_FILETYPE_Clean(hwnd);
1827 FILEDLG95_LOOKIN_Clean(hwnd);
1828 FILEDLG95_SHELL_Clean(hwnd);
1830 /***********************************************************************
1831 * FILEDLG95_OnWMCommand
1833 * WM_COMMAND message handler
1835 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1837 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1838 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1839 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1841 switch(wID)
1843 /* OK button */
1844 case IDOK:
1845 FILEDLG95_OnOpen(hwnd);
1846 break;
1847 /* Cancel button */
1848 case IDCANCEL:
1849 FILEDLG95_Clean(hwnd);
1850 EndDialog(hwnd, FALSE);
1851 break;
1852 /* Filetype combo box */
1853 case IDC_FILETYPE:
1854 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1855 break;
1856 /* LookIn combo box */
1857 case IDC_LOOKIN:
1858 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1859 break;
1861 /* --- toolbar --- */
1862 /* Up folder button */
1863 case FCIDM_TB_UPFOLDER:
1864 FILEDLG95_SHELL_UpFolder(hwnd);
1865 break;
1866 /* New folder button */
1867 case FCIDM_TB_NEWFOLDER:
1868 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1869 break;
1870 /* List option button */
1871 case FCIDM_TB_SMALLICON:
1872 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1873 break;
1874 /* Details option button */
1875 case FCIDM_TB_REPORTVIEW:
1876 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1877 break;
1878 /* Details option button */
1879 case FCIDM_TB_DESKTOP:
1880 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1881 break;
1883 case edt1:
1884 case cmb13:
1885 break;
1888 /* Do not use the listview selection anymore */
1889 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1890 return 0;
1893 /***********************************************************************
1894 * FILEDLG95_OnWMGetIShellBrowser
1896 * WM_GETISHELLBROWSER message handler
1898 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1900 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1902 TRACE("\n");
1904 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1906 return TRUE;
1910 /***********************************************************************
1911 * FILEDLG95_SendFileOK
1913 * Sends the CDN_FILEOK notification if required
1915 * RETURNS
1916 * TRUE if the dialog should close
1917 * FALSE if the dialog should not be closed
1919 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1921 /* ask the hook if we can close */
1922 if(IsHooked(fodInfos))
1924 LRESULT retval = 0;
1926 TRACE("---\n");
1927 /* First send CDN_FILEOK as MSDN doc says */
1928 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1929 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1930 if( retval)
1932 TRACE("canceled\n");
1933 return FALSE;
1936 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1937 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1938 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1939 if( retval)
1941 TRACE("canceled\n");
1942 return FALSE;
1945 return TRUE;
1948 /***********************************************************************
1949 * FILEDLG95_OnOpenMultipleFiles
1951 * Handles the opening of multiple files.
1953 * FIXME
1954 * check destination buffer size
1956 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
1958 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1959 WCHAR lpstrPathSpec[MAX_PATH] = {0};
1960 UINT nCount, nSizePath;
1962 TRACE("\n");
1964 if(fodInfos->unicode)
1966 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1967 ofn->lpstrFile[0] = '\0';
1969 else
1971 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
1972 ofn->lpstrFile[0] = '\0';
1975 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
1977 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
1978 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
1979 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
1981 LPWSTR lpstrTemp = lpstrFileList;
1983 for ( nCount = 0; nCount < nFileCount; nCount++ )
1985 LPITEMIDLIST pidl;
1987 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
1988 if (!pidl)
1990 WCHAR lpstrNotFound[100];
1991 WCHAR lpstrMsg[100];
1992 WCHAR tmp[400];
1993 static const WCHAR nl[] = {'\n',0};
1995 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
1996 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
1998 lstrcpyW(tmp, lpstrTemp);
1999 lstrcatW(tmp, nl);
2000 lstrcatW(tmp, lpstrNotFound);
2001 lstrcatW(tmp, nl);
2002 lstrcatW(tmp, lpstrMsg);
2004 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
2005 return FALSE;
2008 /* move to the next file in the list of files */
2009 lpstrTemp += lstrlenW(lpstrTemp) + 1;
2010 COMDLG32_SHFree(pidl);
2014 nSizePath = lstrlenW(lpstrPathSpec) + 1;
2015 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2017 /* For "oldstyle" dialog the components have to
2018 be separated by blanks (not '\0'!) and short
2019 filenames have to be used! */
2020 FIXME("Components have to be separated by blanks\n");
2022 if(fodInfos->unicode)
2024 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2025 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2026 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2028 else
2030 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2032 if (ofn->lpstrFile != NULL)
2034 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2035 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2036 if (ofn->nMaxFile > nSizePath)
2038 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2039 ofn->lpstrFile + nSizePath,
2040 ofn->nMaxFile - nSizePath, NULL, NULL);
2045 fodInfos->ofnInfos->nFileOffset = nSizePath;
2046 fodInfos->ofnInfos->nFileExtension = 0;
2048 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2049 return FALSE;
2051 /* clean and exit */
2052 FILEDLG95_Clean(hwnd);
2053 return EndDialog(hwnd,TRUE);
2056 /* Returns the 'slot name' of the given module_name in the registry's
2057 * most-recently-used list. This will be an ASCII value in the
2058 * range ['a','z'). Returns zero on error.
2060 * The slot's value in the registry has the form:
2061 * module_name\0mru_path\0
2063 * If stored_path is given, then stored_path will contain the path name
2064 * stored in the registry's MRU list for the given module_name.
2066 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2067 * MRU list key for the given module_name.
2069 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2071 WCHAR mru_list[32], *cur_mru_slot;
2072 BOOL taken[25] = {0};
2073 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2074 HKEY hkey_tmp, *hkey;
2075 LONG ret;
2077 if(hkey_ret)
2078 hkey = hkey_ret;
2079 else
2080 hkey = &hkey_tmp;
2082 if(stored_path)
2083 *stored_path = '\0';
2085 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2086 if(ret){
2087 WARN("Unable to create MRU key: %d\n", ret);
2088 return 0;
2091 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2092 (LPBYTE)mru_list, &mru_list_size);
2093 if(ret || key_type != REG_SZ){
2094 if(ret == ERROR_FILE_NOT_FOUND)
2095 return 'a';
2097 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2098 RegCloseKey(*hkey);
2099 return 0;
2102 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2103 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2104 DWORD value_data_size = sizeof(value_data);
2106 *value_name = *cur_mru_slot;
2108 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2109 &key_type, (LPBYTE)value_data, &value_data_size);
2110 if(ret || key_type != REG_BINARY){
2111 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2112 continue;
2115 if(!strcmpiW(module_name, value_data)){
2116 if(!hkey_ret)
2117 RegCloseKey(*hkey);
2118 if(stored_path)
2119 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2120 return *value_name;
2124 if(!hkey_ret)
2125 RegCloseKey(*hkey);
2127 /* the module name isn't in the registry, so find the next open slot */
2128 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2129 taken[*cur_mru_slot - 'a'] = TRUE;
2130 for(i = 0; i < 25; ++i){
2131 if(!taken[i])
2132 return i + 'a';
2135 /* all slots are taken, so return the last one in MRUList */
2136 --cur_mru_slot;
2137 return *cur_mru_slot;
2140 /* save the given filename as most-recently-used path for this module */
2141 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2143 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2144 LONG ret;
2145 HKEY hkey;
2147 /* get the current executable's name */
2148 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2149 WARN("GotModuleFileName failed: %d\n", GetLastError());
2150 return;
2152 module_name = strrchrW(module_path, '\\');
2153 if(!module_name)
2154 module_name = module_path;
2155 else
2156 module_name += 1;
2158 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2159 if(!slot)
2160 return;
2161 *slot_name = slot;
2163 { /* update the slot's info */
2164 WCHAR *path_ends, *final;
2165 DWORD path_len, final_len;
2167 /* use only the path segment of `filename' */
2168 path_ends = strrchrW(filename, '\\');
2169 path_len = path_ends - filename;
2171 final_len = path_len + lstrlenW(module_name) + 2;
2173 final = MemAlloc(final_len * sizeof(WCHAR));
2174 if(!final)
2175 return;
2176 lstrcpyW(final, module_name);
2177 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2178 final[final_len-1] = '\0';
2180 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2181 final_len * sizeof(WCHAR));
2182 if(ret){
2183 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2184 MemFree(final);
2185 RegCloseKey(hkey);
2186 return;
2189 MemFree(final);
2192 { /* update MRUList value */
2193 WCHAR old_mru_list[32], new_mru_list[32];
2194 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2195 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2197 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2198 (LPBYTE)old_mru_list, &mru_list_size);
2199 if(ret || key_type != REG_SZ){
2200 if(ret == ERROR_FILE_NOT_FOUND){
2201 new_mru_list[0] = slot;
2202 new_mru_list[1] = '\0';
2203 }else{
2204 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2205 RegCloseKey(hkey);
2206 return;
2208 }else{
2209 /* copy old list data over so that the new slot is at the start
2210 * of the list */
2211 *new_mru_slot++ = slot;
2212 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2213 if(*old_mru_slot != slot)
2214 *new_mru_slot++ = *old_mru_slot;
2216 *new_mru_slot = '\0';
2219 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2220 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2221 if(ret){
2222 WARN("Error saving MRUList data: %d\n", ret);
2223 RegCloseKey(hkey);
2224 return;
2229 /* load the most-recently-used path for this module */
2230 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2232 WCHAR module_path[MAX_PATH], *module_name;
2234 /* get the current executable's name */
2235 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2236 WARN("GotModuleFileName failed: %d\n", GetLastError());
2237 return;
2239 module_name = strrchrW(module_path, '\\');
2240 if(!module_name)
2241 module_name = module_path;
2242 else
2243 module_name += 1;
2245 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2246 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2249 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2251 WCHAR strMsgTitle[MAX_PATH];
2252 WCHAR strMsgText [MAX_PATH];
2253 if (idCaption)
2254 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2255 else
2256 strMsgTitle[0] = '\0';
2257 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2258 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2261 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2262 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2264 int nOpenAction = defAction;
2265 LPWSTR lpszTemp, lpszTemp1;
2266 LPITEMIDLIST pidl = NULL;
2267 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2269 /* check for invalid chars */
2270 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2272 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2273 return FALSE;
2276 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2278 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2279 while (lpszTemp1)
2281 LPSHELLFOLDER lpsfChild;
2282 WCHAR lpwstrTemp[MAX_PATH];
2283 DWORD dwEaten, dwAttributes;
2284 LPWSTR p;
2286 lstrcpyW(lpwstrTemp, lpszTemp);
2287 p = PathFindNextComponentW(lpwstrTemp);
2289 if (!p) break; /* end of path */
2291 *p = 0;
2292 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2294 /* There are no wildcards when OFN_NOVALIDATE is set */
2295 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2297 static const WCHAR wszWild[] = { '*', '?', 0 };
2298 /* if the last element is a wildcard do a search */
2299 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2301 nOpenAction = ONOPEN_SEARCH;
2302 break;
2305 lpszTemp1 = lpszTemp;
2307 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2309 /* append a backslash to drive letters */
2310 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2311 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2312 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2314 PathAddBackslashW(lpwstrTemp);
2317 dwAttributes = SFGAO_FOLDER;
2318 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2320 /* the path component is valid, we have a pidl of the next path component */
2321 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2322 if(dwAttributes & SFGAO_FOLDER)
2324 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2326 ERR("bind to failed\n"); /* should not fail */
2327 break;
2329 IShellFolder_Release(*ppsf);
2330 *ppsf = lpsfChild;
2331 lpsfChild = NULL;
2333 else
2335 TRACE("value\n");
2337 /* end dialog, return value */
2338 nOpenAction = ONOPEN_OPEN;
2339 break;
2341 COMDLG32_SHFree(pidl);
2342 pidl = NULL;
2344 else if (!(flags & OFN_NOVALIDATE))
2346 if(*lpszTemp || /* points to trailing null for last path element */
2347 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2349 if(flags & OFN_PATHMUSTEXIST)
2351 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2352 break;
2355 else
2357 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2359 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2360 break;
2363 /* change to the current folder */
2364 nOpenAction = ONOPEN_OPEN;
2365 break;
2367 else
2369 nOpenAction = ONOPEN_OPEN;
2370 break;
2373 if(pidl) COMDLG32_SHFree(pidl);
2375 return nOpenAction;
2378 /***********************************************************************
2379 * FILEDLG95_OnOpen
2381 * Ok button WM_COMMAND message handler
2383 * If the function succeeds, the return value is nonzero.
2385 BOOL FILEDLG95_OnOpen(HWND hwnd)
2387 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2388 LPWSTR lpstrFileList;
2389 UINT nFileCount = 0;
2390 UINT sizeUsed = 0;
2391 BOOL ret = TRUE;
2392 WCHAR lpstrPathAndFile[MAX_PATH];
2393 LPSHELLFOLDER lpsf = NULL;
2394 int nOpenAction;
2396 TRACE("hwnd=%p\n", hwnd);
2398 /* try to browse the selected item */
2399 if(BrowseSelectedFolder(hwnd))
2400 return FALSE;
2402 /* get the files from the edit control */
2403 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2405 if(nFileCount == 0)
2406 return FALSE;
2408 if(nFileCount > 1)
2410 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2411 goto ret;
2414 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2417 Step 1: Build a complete path name from the current folder and
2418 the filename or path in the edit box.
2419 Special cases:
2420 - the path in the edit box is a root path
2421 (with or without drive letter)
2422 - the edit box contains ".." (or a path with ".." in it)
2425 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2426 MemFree(lpstrFileList);
2429 Step 2: here we have a cleaned up path
2431 We have to parse the path step by step to see if we have to browse
2432 to a folder if the path points to a directory or the last
2433 valid element is a directory.
2435 valid variables:
2436 lpstrPathAndFile: cleaned up path
2439 if (nFileCount &&
2440 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2441 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2442 nOpenAction = ONOPEN_OPEN;
2443 else
2444 nOpenAction = ONOPEN_BROWSE;
2446 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2447 fodInfos->ofnInfos->Flags,
2448 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2449 nOpenAction);
2450 if(!nOpenAction)
2451 goto ret;
2454 Step 3: here we have a cleaned up and validated path
2456 valid variables:
2457 lpsf: ShellFolder bound to the rightmost valid path component
2458 lpstrPathAndFile: cleaned up path
2459 nOpenAction: action to do
2461 TRACE("end validate sf=%p\n", lpsf);
2463 switch(nOpenAction)
2465 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2466 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2468 int iPos;
2469 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2470 DWORD len;
2472 /* replace the current filter */
2473 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2474 len = lstrlenW(lpszTemp)+1;
2475 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2476 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2478 /* set the filter cb to the extension when possible */
2479 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2480 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2482 /* fall through */
2483 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2484 TRACE("ONOPEN_BROWSE\n");
2486 IPersistFolder2 * ppf2;
2487 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2489 LPITEMIDLIST pidlCurrent;
2490 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2491 IPersistFolder2_Release(ppf2);
2492 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2494 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2495 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2497 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2498 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2501 else if( nOpenAction == ONOPEN_SEARCH )
2503 if (fodInfos->Shell.FOIShellView)
2504 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2506 COMDLG32_SHFree(pidlCurrent);
2507 if (filename_is_edit( fodInfos ))
2508 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2509 else
2511 HWND hwnd;
2513 hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2514 SendMessageW(hwnd, EM_SETSEL, 0, -1);
2518 ret = FALSE;
2519 break;
2520 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2521 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2523 WCHAR *ext = NULL;
2525 /* update READONLY check box flag */
2526 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2527 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2528 else
2529 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2531 /* Attach the file extension with file name*/
2532 ext = PathFindExtensionW(lpstrPathAndFile);
2533 if (! *ext && fodInfos->defext)
2535 /* if no extension is specified with file name, then */
2536 /* attach the extension from file filter or default one */
2538 WCHAR *filterExt = NULL;
2539 LPWSTR lpstrFilter = NULL;
2540 static const WCHAR szwDot[] = {'.',0};
2541 int PathLength = lstrlenW(lpstrPathAndFile);
2543 /*Get the file extension from file type filter*/
2544 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2545 fodInfos->ofnInfos->nFilterIndex-1);
2547 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2549 WCHAR* filterSearchIndex;
2550 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2551 strcpyW(filterExt, lpstrFilter);
2553 /* if a semicolon-separated list of file extensions was given, do not include the
2554 semicolon or anything after it in the extension.
2555 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2556 filterSearchIndex = strchrW(filterExt, ';');
2557 if (filterSearchIndex)
2559 filterSearchIndex[0] = '\0';
2562 /* find the file extension by searching for the first dot in filterExt */
2563 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2564 /* if the extension is invalid or contains a glob, ignore it */
2565 filterSearchIndex = strchrW(filterExt, '.');
2566 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2568 strcpyW(filterExt, filterSearchIndex);
2570 else
2572 HeapFree(GetProcessHeap(), 0, filterExt);
2573 filterExt = NULL;
2577 if (!filterExt)
2579 /* use the default file extension */
2580 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2581 strcpyW(filterExt, fodInfos->defext);
2584 if (*filterExt) /* ignore filterExt="" */
2586 /* Attach the dot*/
2587 lstrcatW(lpstrPathAndFile, szwDot);
2588 /* Attach the extension */
2589 lstrcatW(lpstrPathAndFile, filterExt);
2592 HeapFree(GetProcessHeap(), 0, filterExt);
2594 /* In Open dialog: if file does not exist try without extension */
2595 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2596 lpstrPathAndFile[PathLength] = '\0';
2598 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2599 if (*ext)
2600 ext++;
2601 if (!lstrcmpiW(fodInfos->defext, ext))
2602 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2603 else
2604 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2607 /* In Save dialog: check if the file already exists */
2608 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2609 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2610 && PathFileExistsW(lpstrPathAndFile))
2612 WCHAR lpstrOverwrite[100];
2613 int answer;
2615 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2616 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2617 MB_YESNO | MB_ICONEXCLAMATION);
2618 if (answer == IDNO || answer == IDCANCEL)
2620 ret = FALSE;
2621 goto ret;
2625 /* In Open dialog: check if it should be created if it doesn't exist */
2626 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2627 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2628 && !PathFileExistsW(lpstrPathAndFile))
2630 WCHAR lpstrCreate[100];
2631 int answer;
2633 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2634 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2635 MB_YESNO | MB_ICONEXCLAMATION);
2636 if (answer == IDNO || answer == IDCANCEL)
2638 ret = FALSE;
2639 goto ret;
2643 /* Check that the size of the file does not exceed buffer size.
2644 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2645 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2646 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2649 /* fill destination buffer */
2650 if (fodInfos->ofnInfos->lpstrFile)
2652 if(fodInfos->unicode)
2654 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2656 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2657 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2658 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2660 else
2662 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2664 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2665 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2666 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2667 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2671 if(fodInfos->unicode)
2673 LPWSTR lpszTemp;
2675 /* set filename offset */
2676 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2677 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2679 /* set extension offset */
2680 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2681 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2683 else
2685 LPSTR lpszTemp;
2686 CHAR tempFileA[MAX_PATH];
2688 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2689 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2690 tempFileA, sizeof(tempFileA), NULL, NULL);
2692 /* set filename offset */
2693 lpszTemp = PathFindFileNameA(tempFileA);
2694 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2696 /* set extension offset */
2697 lpszTemp = PathFindExtensionA(tempFileA);
2698 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2701 /* set the lpstrFileTitle */
2702 if(fodInfos->ofnInfos->lpstrFileTitle)
2704 LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2705 if(fodInfos->unicode)
2707 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2708 lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2710 else
2712 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2713 WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2714 ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2718 /* copy currently selected filter to lpstrCustomFilter */
2719 if (fodInfos->ofnInfos->lpstrCustomFilter)
2721 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2722 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2723 NULL, 0, NULL, NULL);
2724 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2726 LPSTR s = ofn->lpstrCustomFilter;
2727 s += strlen(ofn->lpstrCustomFilter)+1;
2728 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2729 s, len, NULL, NULL);
2734 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2735 goto ret;
2737 FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2739 TRACE("close\n");
2740 FILEDLG95_Clean(hwnd);
2741 ret = EndDialog(hwnd, TRUE);
2743 else
2745 WORD size;
2747 size = lstrlenW(lpstrPathAndFile) + 1;
2748 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2749 size += 1;
2750 /* return needed size in first two bytes of lpstrFile */
2751 if(fodInfos->ofnInfos->lpstrFile)
2752 *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2753 FILEDLG95_Clean(hwnd);
2754 ret = EndDialog(hwnd, FALSE);
2755 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2758 break;
2761 ret:
2762 if(lpsf) IShellFolder_Release(lpsf);
2763 return ret;
2766 /***********************************************************************
2767 * FILEDLG95_SHELL_Init
2769 * Initialisation of the shell objects
2771 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2773 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2775 TRACE("\n");
2778 * Initialisation of the FileOpenDialogInfos structure
2781 /* Shell */
2783 /*ShellInfos */
2784 fodInfos->ShellInfos.hwndOwner = hwnd;
2786 /* Disable multi-select if flag not set */
2787 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2789 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2791 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2792 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2794 /* Construct the IShellBrowser interface */
2795 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2797 return NOERROR;
2800 /***********************************************************************
2801 * FILEDLG95_SHELL_ExecuteCommand
2803 * Change the folder option and refresh the view
2804 * If the function succeeds, the return value is nonzero.
2806 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2808 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2809 IContextMenu * pcm;
2811 TRACE("(%p,%p)\n", hwnd, lpVerb);
2813 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2814 SVGIO_BACKGROUND,
2815 &IID_IContextMenu,
2816 (LPVOID*)&pcm)))
2818 CMINVOKECOMMANDINFO ci;
2819 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2820 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2821 ci.lpVerb = lpVerb;
2822 ci.hwnd = hwnd;
2824 IContextMenu_InvokeCommand(pcm, &ci);
2825 IContextMenu_Release(pcm);
2828 return FALSE;
2831 /***********************************************************************
2832 * FILEDLG95_SHELL_UpFolder
2834 * Browse to the specified object
2835 * If the function succeeds, the return value is nonzero.
2837 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2839 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2841 TRACE("\n");
2843 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2844 NULL,
2845 SBSP_PARENT)))
2847 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2848 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2849 return TRUE;
2851 return FALSE;
2854 /***********************************************************************
2855 * FILEDLG95_SHELL_BrowseToDesktop
2857 * Browse to the Desktop
2858 * If the function succeeds, the return value is nonzero.
2860 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2862 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2863 LPITEMIDLIST pidl;
2864 HRESULT hres;
2866 TRACE("\n");
2868 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2869 hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2870 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2871 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2872 COMDLG32_SHFree(pidl);
2873 return SUCCEEDED(hres);
2875 /***********************************************************************
2876 * FILEDLG95_SHELL_Clean
2878 * Cleans the memory used by shell objects
2880 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2882 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2884 TRACE("\n");
2886 COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2888 /* clean Shell interfaces */
2889 if (fodInfos->Shell.FOIShellView)
2891 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2892 IShellView_Release(fodInfos->Shell.FOIShellView);
2894 if (fodInfos->Shell.FOIShellFolder)
2895 IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2896 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2897 if (fodInfos->Shell.FOIDataObject)
2898 IDataObject_Release(fodInfos->Shell.FOIDataObject);
2901 /***********************************************************************
2902 * FILEDLG95_FILETYPE_Init
2904 * Initialisation of the file type combo box
2906 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2908 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2909 int nFilters = 0; /* number of filters */
2910 int nFilterIndexCB;
2912 TRACE("\n");
2914 if(fodInfos->customfilter)
2916 /* customfilter has one entry... title\0ext\0
2917 * Set first entry of combo box item with customfilter
2919 LPWSTR lpstrExt;
2920 LPCWSTR lpstrPos = fodInfos->customfilter;
2922 /* Get the title */
2923 lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2925 /* Copy the extensions */
2926 if (! *lpstrPos) return E_FAIL; /* malformed filter */
2927 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2928 lstrcpyW(lpstrExt,lpstrPos);
2930 /* Add the item at the end of the combo */
2931 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2932 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2933 nFilters++;
2935 if(fodInfos->filter)
2937 LPCWSTR lpstrPos = fodInfos->filter;
2939 for(;;)
2941 /* filter is a list... title\0ext\0......\0\0
2942 * Set the combo item text to the title and the item data
2943 * to the ext
2945 LPCWSTR lpstrDisplay;
2946 LPWSTR lpstrExt;
2948 /* Get the title */
2949 if(! *lpstrPos) break; /* end */
2950 lpstrDisplay = lpstrPos;
2951 lpstrPos += lstrlenW(lpstrPos) + 1;
2953 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2955 nFilters++;
2957 /* Copy the extensions */
2958 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2959 lstrcpyW(lpstrExt,lpstrPos);
2960 lpstrPos += lstrlenW(lpstrPos) + 1;
2962 /* Add the item at the end of the combo */
2963 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
2965 /* malformed filters are added anyway... */
2966 if (!*lpstrExt) break;
2971 * Set the current filter to the one specified
2972 * in the initialisation structure
2974 if (fodInfos->filter || fodInfos->customfilter)
2976 LPWSTR lpstrFilter;
2978 /* Check to make sure our index isn't out of bounds. */
2979 if ( fodInfos->ofnInfos->nFilterIndex >
2980 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
2981 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
2983 /* set default filter index */
2984 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
2985 fodInfos->ofnInfos->nFilterIndex = 1;
2987 /* calculate index of Combo Box item */
2988 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
2989 if (fodInfos->customfilter == NULL)
2990 nFilterIndexCB--;
2992 /* Set the current index selection. */
2993 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
2995 /* Get the corresponding text string from the combo box. */
2996 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2997 nFilterIndexCB);
2999 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
3000 lpstrFilter = NULL;
3002 if(lpstrFilter)
3004 DWORD len;
3005 CharLowerW(lpstrFilter); /* lowercase */
3006 len = lstrlenW(lpstrFilter)+1;
3007 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3008 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3010 } else
3011 fodInfos->ofnInfos->nFilterIndex = 0;
3012 return S_OK;
3015 /***********************************************************************
3016 * FILEDLG95_FILETYPE_OnCommand
3018 * WM_COMMAND of the file type combo box
3019 * If the function succeeds, the return value is nonzero.
3021 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
3023 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3025 switch(wNotifyCode)
3027 case CBN_SELENDOK:
3029 LPWSTR lpstrFilter;
3031 /* Get the current item of the filetype combo box */
3032 int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
3034 /* set the current filter index */
3035 fodInfos->ofnInfos->nFilterIndex = iItem +
3036 (fodInfos->customfilter == NULL ? 1 : 0);
3038 /* Set the current filter with the current selection */
3039 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3041 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3042 iItem);
3043 if((INT_PTR)lpstrFilter != CB_ERR)
3045 DWORD len;
3046 CharLowerW(lpstrFilter); /* lowercase */
3047 len = lstrlenW(lpstrFilter)+1;
3048 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3049 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3050 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3051 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3054 /* Refresh the actual view to display the included items*/
3055 if (fodInfos->Shell.FOIShellView)
3056 IShellView_Refresh(fodInfos->Shell.FOIShellView);
3059 return FALSE;
3061 /***********************************************************************
3062 * FILEDLG95_FILETYPE_SearchExt
3064 * searches for an extension in the filetype box
3066 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3068 int i, iCount = CBGetCount(hwnd);
3070 TRACE("%s\n", debugstr_w(lpstrExt));
3072 if(iCount != CB_ERR)
3074 for(i=0;i<iCount;i++)
3076 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3077 return i;
3080 return -1;
3083 /***********************************************************************
3084 * FILEDLG95_FILETYPE_Clean
3086 * Clean the memory used by the filetype combo box
3088 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3090 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3091 int iPos;
3092 int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3094 TRACE("\n");
3096 /* Delete each string of the combo and their associated data */
3097 if(iCount != CB_ERR)
3099 for(iPos = iCount-1;iPos>=0;iPos--)
3101 MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3102 CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3105 /* Current filter */
3106 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3110 /***********************************************************************
3111 * FILEDLG95_LOOKIN_Init
3113 * Initialisation of the look in combo box
3116 /* Small helper function, to determine if the unixfs shell extension is rooted
3117 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3119 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3120 HKEY hKey;
3121 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3122 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3123 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3124 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3125 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3126 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3127 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3129 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3130 return FALSE;
3132 RegCloseKey(hKey);
3133 return TRUE;
3136 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3138 IShellFolder *psfRoot, *psfDrives;
3139 IEnumIDList *lpeRoot, *lpeDrives;
3140 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3141 HDC hdc;
3142 TEXTMETRICW tm;
3143 LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3145 TRACE("\n");
3147 liInfos->iMaxIndentation = 0;
3149 SetPropA(hwndCombo, LookInInfosStr, liInfos);
3151 hdc = GetDC( hwndCombo );
3152 SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3153 GetTextMetricsW( hdc, &tm );
3154 ReleaseDC( hwndCombo, hdc );
3156 /* set item height for both text field and listbox */
3157 CBSetItemHeight( hwndCombo, -1, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3158 CBSetItemHeight( hwndCombo, 0, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3160 /* Turn on the extended UI for the combo box like Windows does */
3161 CBSetExtendedUI(hwndCombo, TRUE);
3163 /* Initialise data of Desktop folder */
3164 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3165 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3166 COMDLG32_SHFree(pidlTmp);
3168 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3170 SHGetDesktopFolder(&psfRoot);
3172 if (psfRoot)
3174 /* enumerate the contents of the desktop */
3175 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3177 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3179 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3181 /* If the unixfs extension is rooted, we don't expand the drives by default */
3182 if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3184 /* special handling for CSIDL_DRIVES */
3185 if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3187 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3189 /* enumerate the drives */
3190 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3192 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3194 pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3195 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3196 COMDLG32_SHFree(pidlAbsTmp);
3197 COMDLG32_SHFree(pidlTmp1);
3199 IEnumIDList_Release(lpeDrives);
3201 IShellFolder_Release(psfDrives);
3206 COMDLG32_SHFree(pidlTmp);
3208 IEnumIDList_Release(lpeRoot);
3210 IShellFolder_Release(psfRoot);
3213 COMDLG32_SHFree(pidlDrives);
3216 /***********************************************************************
3217 * FILEDLG95_LOOKIN_DrawItem
3219 * WM_DRAWITEM message handler
3221 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3223 COLORREF crWin = GetSysColor(COLOR_WINDOW);
3224 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3225 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3226 RECT rectText;
3227 RECT rectIcon;
3228 SHFILEINFOW sfi;
3229 HIMAGELIST ilItemImage;
3230 int iIndentation;
3231 TEXTMETRICW tm;
3232 LPSFOLDER tmpFolder;
3233 UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3234 UINT icon_width, icon_height;
3236 TRACE("\n");
3238 if(pDIStruct->itemID == -1)
3239 return 0;
3241 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3242 pDIStruct->itemID)))
3243 return 0;
3246 icon_width = GetSystemMetrics(SM_CXICON);
3247 icon_height = GetSystemMetrics(SM_CYICON);
3248 if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3250 icon_width = GetSystemMetrics(SM_CXSMICON);
3251 icon_height = GetSystemMetrics(SM_CYSMICON);
3252 shgfi_flags |= SHGFI_SMALLICON;
3255 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3256 0, &sfi, sizeof (sfi), shgfi_flags );
3258 /* Is this item selected ? */
3259 if(pDIStruct->itemState & ODS_SELECTED)
3261 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3262 SetBkColor(pDIStruct->hDC,crHighLight);
3263 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3265 else
3267 SetTextColor(pDIStruct->hDC,crText);
3268 SetBkColor(pDIStruct->hDC,crWin);
3269 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3272 /* Do not indent item if drawing in the edit of the combo */
3273 if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3274 iIndentation = 0;
3275 else
3276 iIndentation = tmpFolder->m_iIndent;
3278 /* Draw text and icon */
3280 /* Initialise the icon display area */
3281 rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3282 rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3283 rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3284 rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3286 /* Initialise the text display area */
3287 GetTextMetricsW(pDIStruct->hDC, &tm);
3288 rectText.left = rectIcon.right;
3289 rectText.top =
3290 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3291 rectText.right = pDIStruct->rcItem.right;
3292 rectText.bottom =
3293 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3295 /* Draw the icon from the image list */
3296 ImageList_Draw(ilItemImage,
3297 sfi.iIcon,
3298 pDIStruct->hDC,
3299 rectIcon.left,
3300 rectIcon.top,
3301 ILD_TRANSPARENT );
3303 /* Draw the associated text */
3304 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3305 return NOERROR;
3308 /***********************************************************************
3309 * FILEDLG95_LOOKIN_OnCommand
3311 * LookIn combo box WM_COMMAND message handler
3312 * If the function succeeds, the return value is nonzero.
3314 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3316 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3318 TRACE("%p\n", fodInfos);
3320 switch(wNotifyCode)
3322 case CBN_SELENDOK:
3324 LPSFOLDER tmpFolder;
3325 int iItem;
3327 iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3329 if( iItem == CB_ERR) return FALSE;
3331 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3332 iItem)))
3333 return FALSE;
3336 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3337 tmpFolder->pidlItem,
3338 SBSP_ABSOLUTE)))
3340 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3341 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3342 return TRUE;
3344 break;
3348 return FALSE;
3351 /***********************************************************************
3352 * FILEDLG95_LOOKIN_AddItem
3354 * Adds an absolute pidl item to the lookin combo box
3355 * returns the index of the inserted item
3357 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3359 LPITEMIDLIST pidlNext;
3360 SHFILEINFOW sfi;
3361 SFOLDER *tmpFolder;
3362 LookInInfos *liInfos;
3364 TRACE("%08x\n", iInsertId);
3366 if(!pidl)
3367 return -1;
3369 if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3370 return -1;
3372 tmpFolder = MemAlloc(sizeof(SFOLDER));
3373 tmpFolder->m_iIndent = 0;
3375 /* Calculate the indentation of the item in the lookin*/
3376 pidlNext = pidl;
3377 while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3379 tmpFolder->m_iIndent++;
3382 tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3384 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3385 liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3387 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3388 SHGetFileInfoW((LPCWSTR)pidl,
3390 &sfi,
3391 sizeof(sfi),
3392 SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX
3393 | SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3395 TRACE("-- Add %s attr=%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3397 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3399 int iItemID;
3401 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3403 /* Add the item at the end of the list */
3404 if(iInsertId < 0)
3406 iItemID = CBAddString(hwnd,sfi.szDisplayName);
3408 /* Insert the item at the iInsertId position*/
3409 else
3411 iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3414 CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3415 return iItemID;
3418 COMDLG32_SHFree( tmpFolder->pidlItem );
3419 MemFree( tmpFolder );
3420 return -1;
3424 /***********************************************************************
3425 * FILEDLG95_LOOKIN_InsertItemAfterParent
3427 * Insert an item below its parent
3429 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3432 LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3433 int iParentPos;
3435 TRACE("\n");
3437 if (pidl == pidlParent)
3438 return -1;
3440 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3442 if(iParentPos < 0)
3444 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3447 /* Free pidlParent memory */
3448 COMDLG32_SHFree(pidlParent);
3450 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3453 /***********************************************************************
3454 * FILEDLG95_LOOKIN_SelectItem
3456 * Adds an absolute pidl item to the lookin combo box
3457 * returns the index of the inserted item
3459 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3461 int iItemPos;
3462 LookInInfos *liInfos;
3464 TRACE("\n");
3466 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3468 liInfos = GetPropA(hwnd,LookInInfosStr);
3470 if(iItemPos < 0)
3472 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3473 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3476 else
3478 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3479 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3481 int iRemovedItem;
3483 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3484 break;
3485 if(iRemovedItem < iItemPos)
3486 iItemPos--;
3490 CBSetCurSel(hwnd,iItemPos);
3491 liInfos->uSelectedItem = iItemPos;
3493 return 0;
3497 /***********************************************************************
3498 * FILEDLG95_LOOKIN_RemoveMostExpandedItem
3500 * Remove the item with an expansion level over iExpansionLevel
3502 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3504 int iItemPos;
3505 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3507 TRACE("\n");
3509 if(liInfos->iMaxIndentation <= 2)
3510 return -1;
3512 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3514 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3515 COMDLG32_SHFree(tmpFolder->pidlItem);
3516 MemFree(tmpFolder);
3517 CBDeleteString(hwnd,iItemPos);
3518 liInfos->iMaxIndentation--;
3520 return iItemPos;
3523 return -1;
3526 /***********************************************************************
3527 * FILEDLG95_LOOKIN_SearchItem
3529 * Search for pidl in the lookin combo box
3530 * returns the index of the found item
3532 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3534 int i = 0;
3535 int iCount = CBGetCount(hwnd);
3537 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3539 if (iCount != CB_ERR)
3541 for(;i<iCount;i++)
3543 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3545 if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3546 return i;
3547 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3548 return i;
3552 return -1;
3555 /***********************************************************************
3556 * FILEDLG95_LOOKIN_Clean
3558 * Clean the memory used by the lookin combo box
3560 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3562 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3563 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3564 int iPos;
3565 int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3567 TRACE("\n");
3569 /* Delete each string of the combo and their associated data */
3570 if (iCount != CB_ERR)
3572 for(iPos = iCount-1;iPos>=0;iPos--)
3574 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3575 COMDLG32_SHFree(tmpFolder->pidlItem);
3576 MemFree(tmpFolder);
3577 CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3581 /* LookInInfos structure */
3582 MemFree(liInfos);
3583 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3586 /***********************************************************************
3587 * get_def_format
3589 * Fill the FORMATETC used in the shell id list
3591 static FORMATETC get_def_format(void)
3593 static CLIPFORMAT cfFormat;
3594 FORMATETC formatetc;
3596 if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA);
3597 formatetc.cfFormat = cfFormat;
3598 formatetc.ptd = 0;
3599 formatetc.dwAspect = DVASPECT_CONTENT;
3600 formatetc.lindex = -1;
3601 formatetc.tymed = TYMED_HGLOBAL;
3602 return formatetc;
3605 /***********************************************************************
3606 * FILEDLG95_FILENAME_FillFromSelection
3608 * fills the edit box from the cached DataObject
3610 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3612 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3613 LPITEMIDLIST pidl;
3614 LPWSTR lpstrAllFiles, lpstrTmp;
3615 UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength;
3616 STGMEDIUM medium;
3617 LPIDA cida;
3618 FORMATETC formatetc = get_def_format();
3620 TRACE("\n");
3622 if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium)))
3623 return;
3625 cida = GlobalLock(medium.u.hGlobal);
3626 nFileSelected = cida->cidl;
3628 /* Allocate a buffer */
3629 nAllFilesMaxLength = MAX_PATH + 3;
3630 lpstrAllFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nAllFilesMaxLength * sizeof(WCHAR));
3631 if (!lpstrAllFiles)
3632 goto ret;
3634 /* Loop through the selection, handle only files (not folders) */
3635 for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++)
3637 pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]);
3638 if (pidl)
3640 if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl))
3642 if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength)
3644 nAllFilesMaxLength *= 2;
3645 lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR));
3646 if (!lpstrTmp)
3647 goto ret;
3648 lpstrAllFiles = lpstrTmp;
3650 nFiles += 1;
3651 lpstrAllFiles[nAllFilesLength++] = '"';
3652 GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength);
3653 nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength);
3654 nAllFilesLength += nThisFileLength;
3655 lpstrAllFiles[nAllFilesLength++] = '"';
3656 lpstrAllFiles[nAllFilesLength++] = ' ';
3661 if (nFiles != 0)
3663 /* If there's only one file, use the name as-is without quotes */
3664 lpstrTmp = lpstrAllFiles;
3665 if (nFiles == 1)
3667 lpstrTmp += 1;
3668 lpstrTmp[nThisFileLength] = 0;
3670 SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp);
3671 /* Select the file name like Windows does */
3672 if (filename_is_edit(fodInfos))
3673 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3676 ret:
3677 HeapFree(GetProcessHeap(), 0, lpstrAllFiles);
3678 COMCTL32_ReleaseStgMedium(medium);
3682 /* copied from shell32 to avoid linking to it
3683 * Although shell32 is already linked the behaviour of exported StrRetToStrN
3684 * is dependent on whether emulated OS is unicode or not.
3686 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3688 switch (src->uType)
3690 case STRRET_WSTR:
3691 lstrcpynW(dest, src->u.pOleStr, len);
3692 COMDLG32_SHFree(src->u.pOleStr);
3693 break;
3695 case STRRET_CSTR:
3696 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3697 dest[len-1] = 0;
3698 break;
3700 case STRRET_OFFSET:
3701 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3702 dest[len-1] = 0;
3703 break;
3705 default:
3706 FIXME("unknown type %x!\n", src->uType);
3707 if (len) *dest = '\0';
3708 return E_FAIL;
3710 return S_OK;
3713 /***********************************************************************
3714 * FILEDLG95_FILENAME_GetFileNames
3716 * Copies the filenames to a delimited string list.
3718 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3720 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3721 UINT nFileCount = 0; /* number of files */
3722 UINT nStrLen = 0; /* length of string in edit control */
3723 LPWSTR lpstrEdit; /* buffer for string from edit control */
3725 TRACE("\n");
3727 /* get the filenames from the filename control */
3728 nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
3729 lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3730 GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
3732 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3734 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3735 MemFree(lpstrEdit);
3736 return nFileCount;
3740 * DATAOBJECT Helper functions
3743 /***********************************************************************
3744 * COMCTL32_ReleaseStgMedium
3746 * like ReleaseStgMedium from ole32
3748 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3750 if(medium.pUnkForRelease)
3752 IUnknown_Release(medium.pUnkForRelease);
3754 else
3756 GlobalUnlock(medium.u.hGlobal);
3757 GlobalFree(medium.u.hGlobal);
3761 /***********************************************************************
3762 * GetPidlFromDataObject
3764 * Return pidl(s) by number from the cached DataObject
3766 * nPidlIndex=0 gets the fully qualified root path
3768 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3771 STGMEDIUM medium;
3772 FORMATETC formatetc = get_def_format();
3773 LPITEMIDLIST pidl = NULL;
3775 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3777 if (!doSelected)
3778 return NULL;
3780 /* Get the pidls from IDataObject */
3781 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3783 LPIDA cida = GlobalLock(medium.u.hGlobal);
3784 if(nPidlIndex <= cida->cidl)
3786 pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3788 COMCTL32_ReleaseStgMedium(medium);
3790 return pidl;
3793 /***********************************************************************
3794 * GetNumSelected
3796 * Return the number of selected items in the DataObject.
3799 static UINT GetNumSelected( IDataObject *doSelected )
3801 UINT retVal = 0;
3802 STGMEDIUM medium;
3803 FORMATETC formatetc = get_def_format();
3805 TRACE("sv=%p\n", doSelected);
3807 if (!doSelected) return 0;
3809 /* Get the pidls from IDataObject */
3810 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3812 LPIDA cida = GlobalLock(medium.u.hGlobal);
3813 retVal = cida->cidl;
3814 COMCTL32_ReleaseStgMedium(medium);
3815 return retVal;
3817 return 0;
3821 * TOOLS
3824 /***********************************************************************
3825 * GetName
3827 * Get the pidl's display name (relative to folder) and
3828 * put it in lpstrFileName.
3830 * Return NOERROR on success,
3831 * E_FAIL otherwise
3834 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3836 STRRET str;
3837 HRESULT hRes;
3839 TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3841 if(!lpsf)
3843 SHGetDesktopFolder(&lpsf);
3844 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3845 IShellFolder_Release(lpsf);
3846 return hRes;
3849 /* Get the display name of the pidl relative to the folder */
3850 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3852 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3854 return E_FAIL;
3857 /***********************************************************************
3858 * GetShellFolderFromPidl
3860 * pidlRel is the item pidl relative
3861 * Return the IShellFolder of the absolute pidl
3863 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3865 IShellFolder *psf = NULL,*psfParent;
3867 TRACE("%p\n", pidlAbs);
3869 if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3871 psf = psfParent;
3872 if(pidlAbs && pidlAbs->mkid.cb)
3874 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3876 IShellFolder_Release(psfParent);
3877 return psf;
3880 /* return the desktop */
3881 return psfParent;
3883 return NULL;
3886 /***********************************************************************
3887 * GetParentPidl
3889 * Return the LPITEMIDLIST to the parent of the pidl in the list
3891 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3893 LPITEMIDLIST pidlParent;
3895 TRACE("%p\n", pidl);
3897 pidlParent = COMDLG32_PIDL_ILClone(pidl);
3898 COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3900 return pidlParent;
3903 /***********************************************************************
3904 * GetPidlFromName
3906 * returns the pidl of the file name relative to folder
3907 * NULL if an error occurred
3909 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3911 LPITEMIDLIST pidl = NULL;
3912 ULONG ulEaten;
3914 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3916 if(!lpcstrFileName) return NULL;
3917 if(!*lpcstrFileName) return NULL;
3919 if(!lpsf)
3921 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3922 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3923 IShellFolder_Release(lpsf);
3926 else
3928 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3930 return pidl;
3935 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3937 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3938 HRESULT ret;
3940 TRACE("%p, %p\n", psf, pidl);
3942 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3944 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
3945 /* see documentation shell 4.1*/
3946 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3949 /***********************************************************************
3950 * BrowseSelectedFolder
3952 static BOOL BrowseSelectedFolder(HWND hwnd)
3954 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3955 BOOL bBrowseSelFolder = FALSE;
3957 TRACE("\n");
3959 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
3961 LPITEMIDLIST pidlSelection;
3963 /* get the file selected */
3964 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
3965 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
3967 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
3968 pidlSelection, SBSP_RELATIVE ) ) )
3970 WCHAR buf[64];
3971 LoadStringW( COMDLG32_hInstance, IDS_PATHNOTEXISTING, buf, sizeof(buf)/sizeof(WCHAR) );
3972 MessageBoxW( hwnd, buf, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
3974 bBrowseSelFolder = TRUE;
3975 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3976 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
3978 COMDLG32_SHFree( pidlSelection );
3981 return bBrowseSelFolder;
3985 * Memory allocation methods */
3986 static void *MemAlloc(UINT size)
3988 return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
3991 static void MemFree(void *mem)
3993 HeapFree(GetProcessHeap(),0,mem);
3996 static inline BOOL valid_struct_size( DWORD size )
3998 return (size == OPENFILENAME_SIZE_VERSION_400W) ||
3999 (size == sizeof( OPENFILENAMEW ));
4002 static inline BOOL is_win16_looks(DWORD flags)
4004 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
4005 !(flags & OFN_EXPLORER));
4008 /* ------------------ APIs ---------------------- */
4010 /***********************************************************************
4011 * GetOpenFileNameA (COMDLG32.@)
4013 * Creates a dialog box for the user to select a file to open.
4015 * RETURNS
4016 * TRUE on success: user enters a valid file
4017 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4020 BOOL WINAPI GetOpenFileNameA(OPENFILENAMEA *ofn)
4022 TRACE("flags %08x\n", ofn->Flags);
4024 if (!valid_struct_size( ofn->lStructSize ))
4026 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4027 return FALSE;
4030 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4031 if (ofn->Flags & OFN_FILEMUSTEXIST)
4032 ofn->Flags |= OFN_PATHMUSTEXIST;
4034 if (is_win16_looks(ofn->Flags))
4035 return GetFileName31A(ofn, OPEN_DIALOG);
4036 else
4038 FileOpenDlgInfos info;
4040 init_filedlg_infoA(ofn, &info);
4041 return GetFileDialog95(&info, OPEN_DIALOG);
4045 /***********************************************************************
4046 * GetOpenFileNameW (COMDLG32.@)
4048 * Creates a dialog box for the user to select a file to open.
4050 * RETURNS
4051 * TRUE on success: user enters a valid file
4052 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4055 BOOL WINAPI GetOpenFileNameW(OPENFILENAMEW *ofn)
4057 TRACE("flags %08x\n", ofn->Flags);
4059 if (!valid_struct_size( ofn->lStructSize ))
4061 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4062 return FALSE;
4065 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4066 if (ofn->Flags & OFN_FILEMUSTEXIST)
4067 ofn->Flags |= OFN_PATHMUSTEXIST;
4069 if (is_win16_looks(ofn->Flags))
4070 return GetFileName31W(ofn, OPEN_DIALOG);
4071 else
4073 FileOpenDlgInfos info;
4075 init_filedlg_infoW(ofn, &info);
4076 return GetFileDialog95(&info, OPEN_DIALOG);
4081 /***********************************************************************
4082 * GetSaveFileNameA (COMDLG32.@)
4084 * Creates a dialog box for the user to select a file to save.
4086 * RETURNS
4087 * TRUE on success: user enters a valid file
4088 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4091 BOOL WINAPI GetSaveFileNameA(OPENFILENAMEA *ofn)
4093 if (!valid_struct_size( ofn->lStructSize ))
4095 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4096 return FALSE;
4099 if (is_win16_looks(ofn->Flags))
4100 return GetFileName31A(ofn, SAVE_DIALOG);
4101 else
4103 FileOpenDlgInfos info;
4105 init_filedlg_infoA(ofn, &info);
4106 return GetFileDialog95(&info, SAVE_DIALOG);
4110 /***********************************************************************
4111 * GetSaveFileNameW (COMDLG32.@)
4113 * Creates a dialog box for the user to select a file to save.
4115 * RETURNS
4116 * TRUE on success: user enters a valid file
4117 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4120 BOOL WINAPI GetSaveFileNameW(
4121 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4123 if (!valid_struct_size( ofn->lStructSize ))
4125 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4126 return FALSE;
4129 if (is_win16_looks(ofn->Flags))
4130 return GetFileName31W(ofn, SAVE_DIALOG);
4131 else
4133 FileOpenDlgInfos info;
4135 init_filedlg_infoW(ofn, &info);
4136 return GetFileDialog95(&info, SAVE_DIALOG);
4140 /***********************************************************************
4141 * GetFileTitleA (COMDLG32.@)
4143 * See GetFileTitleW.
4145 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4147 int ret;
4148 UNICODE_STRING strWFile;
4149 LPWSTR lpWTitle;
4151 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4152 lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4153 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4154 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4155 RtlFreeUnicodeString( &strWFile );
4156 RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4157 return ret;
4161 /***********************************************************************
4162 * GetFileTitleW (COMDLG32.@)
4164 * Get the name of a file.
4166 * PARAMS
4167 * lpFile [I] name and location of file
4168 * lpTitle [O] returned file name
4169 * cbBuf [I] buffer size of lpTitle
4171 * RETURNS
4172 * Success: zero
4173 * Failure: negative number.
4175 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4177 int i, len;
4178 static const WCHAR brkpoint[] = {'*','[',']',0};
4179 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4181 if(lpFile == NULL || lpTitle == NULL)
4182 return -1;
4184 len = lstrlenW(lpFile);
4186 if (len == 0)
4187 return -1;
4189 if(strpbrkW(lpFile, brkpoint))
4190 return -1;
4192 len--;
4194 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4195 return -1;
4197 for(i = len; i >= 0; i--)
4199 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':')
4201 i++;
4202 break;
4206 if(i == -1)
4207 i++;
4209 TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4211 len = lstrlenW(lpFile+i)+1;
4212 if(cbBuf < len)
4213 return len;
4215 lstrcpyW(lpTitle, &lpFile[i]);
4216 return 0;