comdlg32: Consolidate file dialog initialization to avoid duplication.
[wine.git] / dlls / comdlg32 / filedlg.c
blob4093cf019f2ef40d3323400d869e8e2bd1a0ec77
1 /*
2 * COMMDLG - File Open Dialogs Win95 look and feel
4 * Copyright 1999 Francois Boisvert
5 * Copyright 1999, 2000 Juergen Schmied
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * FIXME: The whole concept of handling unicode is badly broken.
22 * many hook-messages expect a pointer to a
23 * OPENFILENAMEA or W structure. With the current architecture
24 * we would have to convert the beast at every call to a hook.
25 * we have to find a better solution but it would likely cause
26 * a complete rewrite after which we should handle the
27 * OPENFILENAME structure without any converting (jsch).
29 * FIXME: any hook gets a OPENFILENAMEA structure
31 * FIXME: CDN_FILEOK is wrong implemented, other CDN_ messages likely too
33 * FIXME: old style hook messages are not implemented (except FILEOKSTRING)
35 * FIXME: algorithm for selecting the initial directory is too simple
37 * FIXME: add to recent docs
39 * FIXME: flags not implemented: OFN_DONTADDTORECENT,
40 * OFN_NODEREFERENCELINKS, OFN_NOREADONLYRETURN,
41 * OFN_NOTESTFILECREATE, OFN_USEMONIKERS
43 * FIXME: lCustData for lpfnHook (WM_INITDIALOG)
48 #include "config.h"
49 #include "wine/port.h"
51 #include <ctype.h>
52 #include <stdlib.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <string.h>
57 #define COBJMACROS
58 #define NONAMELESSUNION
60 #include "windef.h"
61 #include "winbase.h"
62 #include "winternl.h"
63 #include "winnls.h"
64 #include "wingdi.h"
65 #include "winreg.h"
66 #include "winuser.h"
67 #include "commdlg.h"
68 #include "dlgs.h"
69 #include "cdlg.h"
70 #include "cderr.h"
71 #include "shellapi.h"
72 #include "shlobj.h"
73 #include "filedlgbrowser.h"
74 #include "shlwapi.h"
76 #include "wine/unicode.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
81 #define UNIMPLEMENTED_FLAGS \
82 (OFN_DONTADDTORECENT |\
83 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
84 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
86 #define IsHooked(fodInfos) \
87 ((fodInfos->ofnInfos->Flags & OFN_ENABLEHOOK) && fodInfos->ofnInfos->lpfnHook)
88 /***********************************************************************
89 * Data structure and global variables
91 typedef struct SFolder
93 int m_iImageIndex; /* Index of picture in image list */
94 HIMAGELIST hImgList;
95 int m_iIndent; /* Indentation index */
96 LPITEMIDLIST pidlItem; /* absolute pidl of the item */
98 } SFOLDER,*LPSFOLDER;
100 typedef struct tagLookInInfo
102 int iMaxIndentation;
103 UINT uSelectedItem;
104 } LookInInfos;
107 /***********************************************************************
108 * Defines and global variables
111 /* Draw item constant */
112 #define XTEXTOFFSET 3
114 /* AddItem flags*/
115 #define LISTEND -1
117 /* SearchItem methods */
118 #define SEARCH_PIDL 1
119 #define SEARCH_EXP 2
120 #define ITEM_NOTFOUND -1
122 /* Undefined windows message sent by CreateViewObject*/
123 #define WM_GETISHELLBROWSER WM_USER+7
125 /* NOTE
126 * Those macros exist in windowsx.h. However, you can't really use them since
127 * they rely on the UNICODE defines and can't be used inside Wine itself.
130 /* Combo box macros */
131 #define CBAddString(hwnd,str) \
132 SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)(str));
134 #define CBInsertString(hwnd,str,pos) \
135 SendMessageW(hwnd, CB_INSERTSTRING, (WPARAM)(pos), (LPARAM)(str));
137 #define CBDeleteString(hwnd,pos) \
138 SendMessageW(hwnd, CB_DELETESTRING, (WPARAM)(pos), 0);
140 #define CBSetItemDataPtr(hwnd,iItemId,dataPtr) \
141 SendMessageW(hwnd, CB_SETITEMDATA, (WPARAM)(iItemId), (LPARAM)(dataPtr));
143 #define CBGetItemDataPtr(hwnd,iItemId) \
144 SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
146 #define CBGetLBText(hwnd,iItemId,str) \
147 SendMessageW(hwnd, CB_GETLBTEXT, (WPARAM)(iItemId), (LPARAM)(str));
149 #define CBGetCurSel(hwnd) \
150 SendMessageW(hwnd, CB_GETCURSEL, 0, 0);
152 #define CBSetCurSel(hwnd,pos) \
153 SendMessageW(hwnd, CB_SETCURSEL, (WPARAM)(pos), 0);
155 #define CBGetCount(hwnd) \
156 SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
157 #define CBShowDropDown(hwnd,show) \
158 SendMessageW(hwnd, CB_SHOWDROPDOWN, (WPARAM)(show), 0);
159 #define CBSetItemHeight(hwnd,index,height) \
160 SendMessageW(hwnd, CB_SETITEMHEIGHT, (WPARAM)(index), (LPARAM)(height));
162 #define CBSetExtendedUI(hwnd,flag) \
163 SendMessageW(hwnd, CB_SETEXTENDEDUI, (WPARAM)(flag), 0)
165 const char FileOpenDlgInfosStr[] = "FileOpenDlgInfos"; /* windows property description string */
166 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
167 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
169 static const WCHAR LastVisitedMRUW[] =
170 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
171 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
172 'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
173 'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
174 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
176 /***********************************************************************
177 * Prototypes
180 /* Internal functions used by the dialog */
181 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
182 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
183 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
184 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
185 static BOOL FILEDLG95_OnOpen(HWND hwnd);
186 static LRESULT FILEDLG95_InitControls(HWND hwnd);
187 static void FILEDLG95_Clean(HWND hwnd);
189 /* Functions used by the shell navigation */
190 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
191 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd);
192 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
193 static void FILEDLG95_SHELL_Clean(HWND hwnd);
194 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd);
196 /* Functions used by the EDIT box */
197 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
199 /* Functions used by the filetype combo box */
200 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
201 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
202 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
203 static void FILEDLG95_FILETYPE_Clean(HWND hwnd);
205 /* Functions used by the Look In combo box */
206 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo);
207 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
208 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
209 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
210 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
211 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
212 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
213 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
214 static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
216 /* Functions for dealing with the most-recently-used registry keys */
217 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
218 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
219 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
221 /* Miscellaneous tool functions */
222 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
223 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
224 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl);
225 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
226 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
227 static UINT GetNumSelected( IDataObject *doSelected );
228 static void COMCTL32_ReleaseStgMedium(STGMEDIUM medium);
230 /* Shell memory allocation */
231 static void *MemAlloc(UINT size);
232 static void MemFree(void *mem);
234 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
235 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
236 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
237 static BOOL BrowseSelectedFolder(HWND hwnd);
239 /***********************************************************************
240 * GetFileName95
242 * Creates an Open common dialog box that lets the user select
243 * the drive, directory, and the name of a file or set of files to open.
245 * IN : The FileOpenDlgInfos structure associated with the dialog
246 * OUT : TRUE on success
247 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
249 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
251 LRESULT lRes;
252 void *template;
253 HRSRC hRes;
254 HANDLE hDlgTmpl = 0;
256 /* test for missing functionality */
257 if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
259 FIXME("Flags 0x%08x not yet implemented\n",
260 fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
263 /* Create the dialog from a template */
265 if(!(hRes = FindResourceW(COMDLG32_hInstance,MAKEINTRESOURCEW(NEWFILEOPENORD),(LPCWSTR)RT_DIALOG)))
267 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
268 return FALSE;
270 if (!(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes )) ||
271 !(template = LockResource( hDlgTmpl )))
273 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
274 return FALSE;
277 /* msdn: explorer style dialogs permit sizing by default.
278 * The OFN_ENABLESIZING flag is only needed when a hook or
279 * custom template is provided */
280 if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
281 !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
282 fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
284 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
286 fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
287 fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
290 /* old style hook messages */
291 if (IsHooked(fodInfos))
293 fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
294 fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
295 fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
296 fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
299 if (fodInfos->unicode)
300 lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
301 template,
302 fodInfos->ofnInfos->hwndOwner,
303 FileOpenDlgProc95,
304 (LPARAM) fodInfos);
305 else
306 lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
307 template,
308 fodInfos->ofnInfos->hwndOwner,
309 FileOpenDlgProc95,
310 (LPARAM) fodInfos);
311 if (fodInfos->ole_initialized)
312 OleUninitialize();
314 /* Unable to create the dialog */
315 if( lRes == -1)
316 return FALSE;
318 return lRes;
321 static WCHAR *heap_strdupAtoW(const char *str)
323 WCHAR *ret;
324 INT len;
326 if (!str)
327 return NULL;
329 len = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
330 ret = MemAlloc(len * sizeof(WCHAR));
331 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
333 return ret;
336 static void init_filedlg_infoW(OPENFILENAMEW *ofn, FileOpenDlgInfos *info)
338 INITCOMMONCONTROLSEX icc;
340 /* Initialize ComboBoxEx32 */
341 icc.dwSize = sizeof(icc);
342 icc.dwICC = ICC_USEREX_CLASSES;
343 InitCommonControlsEx(&icc);
345 /* Initialize CommDlgExtendedError() */
346 COMDLG32_SetCommDlgExtendedError(0);
348 memset(info, 0, sizeof(*info));
350 /* Pass in the original ofn */
351 info->ofnInfos = ofn;
353 info->title = ofn->lpstrTitle;
354 info->defext = ofn->lpstrDefExt;
355 info->filter = ofn->lpstrFilter;
356 info->customfilter = ofn->lpstrCustomFilter;
358 if (ofn->lpstrFile)
360 info->filename = MemAlloc(ofn->nMaxFile * sizeof(WCHAR));
361 lstrcpynW(info->filename, ofn->lpstrFile, ofn->nMaxFile);
364 if (ofn->lpstrInitialDir)
366 DWORD len = ExpandEnvironmentStringsW(ofn->lpstrInitialDir, NULL, 0);
367 if (len)
369 info->initdir = MemAlloc(len * sizeof(WCHAR));
370 ExpandEnvironmentStringsW(ofn->lpstrInitialDir, info->initdir, len);
374 info->unicode = TRUE;
377 static void init_filedlg_infoA(OPENFILENAMEA *ofn, FileOpenDlgInfos *info)
379 OPENFILENAMEW ofnW;
381 ofnW = *(OPENFILENAMEW *)ofn;
383 ofnW.lpstrInitialDir = heap_strdupAtoW(ofn->lpstrInitialDir);
384 ofnW.lpstrFile = heap_strdupAtoW(ofn->lpstrFile);
385 ofnW.lpstrDefExt = heap_strdupAtoW(ofn->lpstrDefExt);
386 ofnW.lpstrTitle = heap_strdupAtoW(ofn->lpstrTitle);
388 if (ofn->lpstrFilter)
390 int n, len;
391 LPCSTR s;
393 /* filter is a list... title\0ext\0......\0\0 */
394 s = ofn->lpstrFilter;
395 while (*s) s = s+strlen(s)+1;
396 s++;
397 n = s - ofn->lpstrFilter;
398 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0);
399 ofnW.lpstrFilter = MemAlloc(len * sizeof(WCHAR));
400 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, (WCHAR *)ofnW.lpstrFilter, len);
403 /* convert lpstrCustomFilter */
404 if (ofn->lpstrCustomFilter)
406 int n, len;
407 LPCSTR s;
409 /* customfilter contains a pair of strings... title\0ext\0 */
410 s = ofn->lpstrCustomFilter;
411 if (*s) s = s+strlen(s)+1;
412 if (*s) s = s+strlen(s)+1;
413 n = s - ofn->lpstrCustomFilter;
414 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0);
415 ofnW.lpstrCustomFilter = MemAlloc(len * sizeof(WCHAR));
416 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, ofnW.lpstrCustomFilter, len);
419 init_filedlg_infoW(&ofnW, info);
421 /* fixup A-specific fields */
422 info->ofnInfos = (OPENFILENAMEW *)ofn;
423 info->unicode = FALSE;
425 /* free what was duplicated */
426 MemFree((WCHAR *)ofnW.lpstrInitialDir);
427 MemFree((WCHAR *)ofnW.lpstrFile);
430 /***********************************************************************
431 * GetFileDialog95
433 * Call GetFileName95 with this structure and clean the memory.
435 static BOOL GetFileDialog95(FileOpenDlgInfos *info, UINT dlg_type)
437 WCHAR *current_dir = NULL;
438 BOOL ret;
440 /* save current directory */
441 if (info->ofnInfos->Flags & OFN_NOCHANGEDIR)
443 current_dir = MemAlloc(MAX_PATH * sizeof(WCHAR));
444 GetCurrentDirectoryW(MAX_PATH, current_dir);
447 switch (dlg_type)
449 case OPEN_DIALOG:
450 ret = GetFileName95(info);
451 break;
452 case SAVE_DIALOG:
453 info->DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
454 ret = GetFileName95(info);
455 break;
456 default:
457 ret = FALSE;
460 if (current_dir)
462 SetCurrentDirectoryW(current_dir);
463 MemFree(current_dir);
466 if (!info->unicode)
468 MemFree((WCHAR *)info->defext);
469 MemFree((WCHAR *)info->title);
470 MemFree((WCHAR *)info->filter);
471 MemFree((WCHAR *)info->customfilter);
474 MemFree(info->filename);
475 MemFree(info->initdir);
476 return ret;
479 /******************************************************************************
480 * COMDLG32_GetDisplayNameOf [internal]
482 * Helper function to get the display name for a pidl.
484 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
485 LPSHELLFOLDER psfDesktop;
486 STRRET strret;
488 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
489 return FALSE;
491 if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
492 IShellFolder_Release(psfDesktop);
493 return FALSE;
496 IShellFolder_Release(psfDesktop);
497 return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
500 /******************************************************************************
501 * COMDLG32_GetCanonicalPath [internal]
503 * Helper function to get the canonical path.
505 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
506 LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
508 WCHAR lpstrTemp[MAX_PATH];
510 /* Get the current directory name */
511 if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
513 /* last fallback */
514 GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
516 PathAddBackslashW(lpstrPathAndFile);
518 TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile));
520 /* if the user specified a fully qualified path use it */
521 if(PathIsRelativeW(lpstrFile))
523 lstrcatW(lpstrPathAndFile, lpstrFile);
525 else
527 /* does the path have a drive letter? */
528 if (PathGetDriveNumberW(lpstrFile) == -1)
529 lstrcpyW(lpstrPathAndFile+2, lpstrFile);
530 else
531 lstrcpyW(lpstrPathAndFile, lpstrFile);
534 /* resolve "." and ".." */
535 PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
536 lstrcpyW(lpstrPathAndFile, lpstrTemp);
537 TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
540 /***********************************************************************
541 * COMDLG32_SplitFileNames [internal]
543 * Creates a delimited list of filenames.
545 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
547 UINT nStrCharCount = 0; /* index in src buffer */
548 UINT nFileIndex = 0; /* index in dest buffer */
549 UINT nFileCount = 0; /* number of files */
551 /* we might get single filename without any '"',
552 * so we need nStrLen + terminating \0 + end-of-list \0 */
553 *lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
554 *sizeUsed = 0;
556 /* build delimited file list from filenames */
557 while ( nStrCharCount <= nStrLen )
559 if ( lpstrEdit[nStrCharCount]=='"' )
561 nStrCharCount++;
562 while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
564 (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
565 nStrCharCount++;
567 (*lpstrFileList)[nFileIndex++] = 0;
568 nFileCount++;
570 nStrCharCount++;
573 /* single, unquoted string */
574 if ((nStrLen > 0) && (nFileIndex == 0) )
576 lstrcpyW(*lpstrFileList, lpstrEdit);
577 nFileIndex = lstrlenW(lpstrEdit) + 1;
578 nFileCount = 1;
581 /* trailing \0 */
582 (*lpstrFileList)[nFileIndex++] = '\0';
584 *sizeUsed = nFileIndex;
585 return nFileCount;
588 /***********************************************************************
589 * ArrangeCtrlPositions [internal]
591 * NOTE: Make sure to add testcases for any changes made here.
593 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
595 HWND hwndChild, hwndStc32;
596 RECT rectParent, rectChild, rectStc32;
597 INT help_fixup = 0;
598 int chgx, chgy;
600 /* Take into account if open as read only checkbox and help button
601 * are hidden
603 if (hide_help)
605 RECT rectHelp, rectCancel;
606 GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
607 GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
608 /* subtract the height of the help button plus the space between
609 * the help button and the cancel button to the height of the dialog
611 help_fixup = rectHelp.bottom - rectCancel.bottom;
615 There are two possibilities to add components to the default file dialog box.
617 By default, all the new components are added below the standard dialog box (the else case).
619 However, if there is a static text component with the stc32 id, a special case happens.
620 The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
621 in the window and the cx and cy indicate how to size the window.
622 Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
623 of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
627 GetClientRect(hwndParentDlg, &rectParent);
629 /* when arranging controls we have to use fixed parent size */
630 rectParent.bottom -= help_fixup;
632 hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
633 if (hwndStc32)
635 GetWindowRect(hwndStc32, &rectStc32);
636 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
638 /* set the size of the stc32 control according to the size of
639 * client area of the parent dialog
641 SetWindowPos(hwndStc32, 0,
642 0, 0,
643 rectParent.right, rectParent.bottom,
644 SWP_NOMOVE | SWP_NOZORDER);
646 else
647 SetRectEmpty(&rectStc32);
649 /* this part moves controls of the child dialog */
650 hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
651 while (hwndChild)
653 if (hwndChild != hwndStc32)
655 GetWindowRect(hwndChild, &rectChild);
656 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
658 /* move only if stc32 exist */
659 if (hwndStc32 && rectChild.left > rectStc32.right)
661 /* move to the right of visible controls of the parent dialog */
662 rectChild.left += rectParent.right;
663 rectChild.left -= rectStc32.right;
665 /* move even if stc32 doesn't exist */
666 if (rectChild.top >= rectStc32.bottom)
668 /* move below visible controls of the parent dialog */
669 rectChild.top += rectParent.bottom;
670 rectChild.top -= rectStc32.bottom - rectStc32.top;
673 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
674 0, 0, SWP_NOSIZE | SWP_NOZORDER);
676 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
679 /* this part moves controls of the parent dialog */
680 hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
681 while (hwndChild)
683 if (hwndChild != hwndChildDlg)
685 GetWindowRect(hwndChild, &rectChild);
686 MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
688 /* left,top of stc32 marks the position of controls
689 * from the parent dialog
691 rectChild.left += rectStc32.left;
692 rectChild.top += rectStc32.top;
694 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
695 0, 0, SWP_NOSIZE | SWP_NOZORDER);
697 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
700 /* calculate the size of the resulting dialog */
702 /* here we have to use original parent size */
703 GetClientRect(hwndParentDlg, &rectParent);
704 GetClientRect(hwndChildDlg, &rectChild);
705 TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
706 wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
708 if (hwndStc32)
710 /* width */
711 if (rectParent.right > rectStc32.right - rectStc32.left)
712 chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
713 else
714 chgx = rectChild.right - rectParent.right;
715 /* height */
716 if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
717 chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
718 else
719 /* Unconditionally set new dialog
720 * height to that of the child
722 chgy = rectChild.bottom - rectParent.bottom;
724 else
726 chgx = 0;
727 chgy = rectChild.bottom - help_fixup;
729 /* set the size of the parent dialog */
730 GetWindowRect(hwndParentDlg, &rectParent);
731 SetWindowPos(hwndParentDlg, 0,
732 0, 0,
733 rectParent.right - rectParent.left + chgx,
734 rectParent.bottom - rectParent.top + chgy,
735 SWP_NOMOVE | SWP_NOZORDER);
738 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
740 switch(uMsg) {
741 case WM_INITDIALOG:
742 return TRUE;
744 return FALSE;
747 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
749 LPCVOID template;
750 HRSRC hRes;
751 HANDLE hDlgTmpl = 0;
752 HWND hChildDlg = 0;
754 TRACE("\n");
757 * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
758 * structure's hInstance parameter is not a HINSTANCE, but
759 * instead a pointer to a template resource to use.
761 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
763 HINSTANCE hinst;
764 if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
766 hinst = COMDLG32_hInstance;
767 if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
769 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
770 return NULL;
773 else
775 hinst = fodInfos->ofnInfos->hInstance;
776 if(fodInfos->unicode)
778 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
779 hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
781 else
783 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
784 hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
786 if (!hRes)
788 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
789 return NULL;
791 if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
792 !(template = LockResource( hDlgTmpl )))
794 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
795 return NULL;
798 if (fodInfos->unicode)
799 hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
800 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
801 (LPARAM)fodInfos->ofnInfos);
802 else
803 hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
804 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
805 (LPARAM)fodInfos->ofnInfos);
806 return hChildDlg;
808 else if( IsHooked(fodInfos))
810 RECT rectHwnd;
811 struct {
812 DLGTEMPLATE tmplate;
813 WORD menu,class,title;
814 } temp;
815 GetClientRect(hwnd,&rectHwnd);
816 temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
817 temp.tmplate.dwExtendedStyle = 0;
818 temp.tmplate.cdit = 0;
819 temp.tmplate.x = 0;
820 temp.tmplate.y = 0;
821 temp.tmplate.cx = 0;
822 temp.tmplate.cy = 0;
823 temp.menu = temp.class = temp.title = 0;
825 hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
826 hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
828 return hChildDlg;
830 return NULL;
833 /***********************************************************************
834 * SendCustomDlgNotificationMessage
836 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
839 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
841 LRESULT hook_result = 0;
842 FileOpenDlgInfos *fodInfos = GetPropA(hwndParentDlg,FileOpenDlgInfosStr);
844 TRACE("%p 0x%04x\n",hwndParentDlg, uCode);
846 if(!fodInfos) return 0;
848 if(fodInfos->DlgInfos.hwndCustomDlg)
850 TRACE("CALL NOTIFY for %x\n", uCode);
851 if(fodInfos->unicode)
853 OFNOTIFYW ofnNotify;
854 ofnNotify.hdr.hwndFrom=hwndParentDlg;
855 ofnNotify.hdr.idFrom=0;
856 ofnNotify.hdr.code = uCode;
857 ofnNotify.lpOFN = fodInfos->ofnInfos;
858 ofnNotify.pszFile = NULL;
859 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
861 else
863 OFNOTIFYA ofnNotify;
864 ofnNotify.hdr.hwndFrom=hwndParentDlg;
865 ofnNotify.hdr.idFrom=0;
866 ofnNotify.hdr.code = uCode;
867 ofnNotify.lpOFN = (LPOPENFILENAMEA)fodInfos->ofnInfos;
868 ofnNotify.pszFile = NULL;
869 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg,WM_NOTIFY,0,(LPARAM)&ofnNotify);
871 TRACE("RET NOTIFY\n");
873 TRACE("Retval: 0x%08lx\n", hook_result);
874 return hook_result;
877 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
879 UINT len, total;
880 WCHAR *p, *buffer;
881 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
883 TRACE("CDM_GETFILEPATH:\n");
885 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
886 return -1;
888 /* get path and filenames */
889 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
890 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
891 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
892 if (len)
894 p = buffer + strlenW(buffer);
895 *p++ = '\\';
896 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
898 if (fodInfos->unicode)
900 total = strlenW( buffer) + 1;
901 if (result) lstrcpynW( result, buffer, size );
902 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
904 else
906 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
907 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
908 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
910 HeapFree( GetProcessHeap(), 0, buffer );
911 return total;
914 /***********************************************************************
915 * FILEDLG95_HandleCustomDialogMessages
917 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
919 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
921 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
922 WCHAR lpstrPath[MAX_PATH];
923 INT_PTR retval;
925 if(!fodInfos) return FALSE;
927 switch(uMsg)
929 case CDM_GETFILEPATH:
930 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
931 break;
933 case CDM_GETFOLDERPATH:
934 TRACE("CDM_GETFOLDERPATH:\n");
935 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
936 if (lParam)
938 if (fodInfos->unicode)
939 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
940 else
941 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
942 (LPSTR)lParam, (int)wParam, NULL, NULL);
944 retval = lstrlenW(lpstrPath) + 1;
945 break;
947 case CDM_GETFOLDERIDLIST:
948 retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
949 if (retval <= wParam)
950 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
951 break;
953 case CDM_GETSPEC:
954 TRACE("CDM_GETSPEC:\n");
955 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
956 if (lParam)
958 if (fodInfos->unicode)
959 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
960 else
961 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
963 break;
965 case CDM_SETCONTROLTEXT:
966 TRACE("CDM_SETCONTROLTEXT:\n");
967 if ( lParam )
969 if( fodInfos->unicode )
970 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
971 else
972 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
974 retval = TRUE;
975 break;
977 case CDM_HIDECONTROL:
978 /* MSDN states that it should fail for not OFN_EXPLORER case */
979 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
981 HWND control = GetDlgItem( hwnd, wParam );
982 if (control) ShowWindow( control, SW_HIDE );
983 retval = TRUE;
985 else retval = FALSE;
986 break;
988 default:
989 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
990 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
991 return FALSE;
993 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
994 return TRUE;
997 /***********************************************************************
998 * FILEDLG95_OnWMGetMMI
1000 * WM_GETMINMAXINFO message handler for resizable dialogs
1002 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
1004 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1005 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1006 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1008 mmiptr->ptMinTrackSize = fodInfos->initial_size;
1010 return TRUE;
1013 /***********************************************************************
1014 * FILEDLG95_OnWMSize
1016 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1018 * FIXME: this could be made more elaborate. Now use a simple scheme
1019 * where the file view is enlarged and the controls are either moved
1020 * vertically or horizontally to get out of the way. Only the "grip"
1021 * is moved in both directions to stay in the corner.
1023 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1025 RECT rc, rcview;
1026 int chgx, chgy;
1027 HWND ctrl;
1028 HDWP hdwp;
1029 FileOpenDlgInfos *fodInfos;
1031 if( wParam != SIZE_RESTORED) return FALSE;
1032 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1033 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1034 /* get the new dialog rectangle */
1035 GetWindowRect( hwnd, &rc);
1036 TRACE("Size from %d,%d to %d,%d\n", fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1037 rc.right -rc.left, rc.bottom -rc.top);
1038 /* not initialized yet */
1039 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1040 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1041 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1042 return FALSE;
1043 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1044 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1045 fodInfos->sizedlg.cx = rc.right - rc.left;
1046 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1047 /* change the size of the view window */
1048 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1049 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1050 hdwp = BeginDeferWindowPos( 10);
1051 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1052 rcview.right - rcview.left + chgx,
1053 rcview.bottom - rcview.top + chgy,
1054 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1055 /* change position and sizes of the controls */
1056 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1058 int ctrlid = GetDlgCtrlID( ctrl);
1059 GetWindowRect( ctrl, &rc);
1060 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1061 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1063 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1064 0, 0,
1065 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1067 else if( rc.top > rcview.bottom)
1069 /* if it was below the shell view
1070 * move to bottom */
1071 switch( ctrlid)
1073 /* file name (edit or comboboxex) and file types combo change also width */
1074 case edt1:
1075 case cmb13:
1076 case cmb1:
1077 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1078 rc.right - rc.left + chgx, rc.bottom - rc.top,
1079 SWP_NOACTIVATE | SWP_NOZORDER);
1080 break;
1081 /* then these buttons must move out of the way */
1082 case IDOK:
1083 case IDCANCEL:
1084 case pshHelp:
1085 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1086 0, 0,
1087 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1088 break;
1089 default:
1090 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1091 0, 0,
1092 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1095 else if( rc.left > rcview.right)
1097 /* if it was to the right of the shell view
1098 * move to right */
1099 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1100 0, 0,
1101 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1103 else
1104 /* special cases */
1106 switch( ctrlid)
1108 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1109 case IDC_LOOKIN:
1110 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1111 rc.right - rc.left + chgx, rc.bottom - rc.top,
1112 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1113 break;
1114 case IDC_TOOLBARSTATIC:
1115 case IDC_TOOLBAR:
1116 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1117 0, 0,
1118 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1119 break;
1120 #endif
1121 /* not resized in windows. Since wine uses this invisible control
1122 * to size the browser view it needs to be resized */
1123 case IDC_SHELLSTATIC:
1124 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1125 rc.right - rc.left + chgx,
1126 rc.bottom - rc.top + chgy,
1127 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1128 break;
1132 if(fodInfos->DlgInfos.hwndCustomDlg &&
1133 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1135 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1136 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1138 GetWindowRect( ctrl, &rc);
1139 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1140 if( rc.top > rcview.bottom)
1142 /* if it was below the shell view
1143 * move to bottom */
1144 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1145 rc.right - rc.left, rc.bottom - rc.top,
1146 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1148 else if( rc.left > rcview.right)
1150 /* if it was to the right of the shell view
1151 * move to right */
1152 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1153 rc.right - rc.left, rc.bottom - rc.top,
1154 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1157 /* size the custom dialog at the end: some applications do some
1158 * control re-arranging at this point */
1159 GetClientRect(hwnd, &rc);
1160 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1161 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1163 EndDeferWindowPos( hdwp);
1164 /* should not be needed */
1165 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1166 return TRUE;
1169 /***********************************************************************
1170 * FileOpenDlgProc95
1172 * File open dialog procedure
1174 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1176 #if 0
1177 TRACE("%p 0x%04x\n", hwnd, uMsg);
1178 #endif
1180 switch(uMsg)
1182 case WM_INITDIALOG:
1184 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1185 RECT rc, rcstc;
1186 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1187 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1189 /* Some shell namespace extensions depend on COM being initialized. */
1190 if (SUCCEEDED(OleInitialize(NULL)))
1191 fodInfos->ole_initialized = TRUE;
1193 /* Adds the FileOpenDlgInfos in the property list of the dialog
1194 so it will be easily accessible through a GetPropA(...) */
1195 SetPropA(hwnd, FileOpenDlgInfosStr, fodInfos);
1197 FILEDLG95_InitControls(hwnd);
1199 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1201 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1202 DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1203 RECT client, client_adjusted;
1205 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1207 style |= WS_SIZEBOX;
1208 ex_style |= WS_EX_WINDOWEDGE;
1210 else
1211 style &= ~WS_SIZEBOX;
1212 SetWindowLongW(hwnd, GWL_STYLE, style);
1213 SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1215 GetClientRect( hwnd, &client );
1216 GetClientRect( hwnd, &client_adjusted );
1217 AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1219 GetWindowRect( hwnd, &rc );
1220 rc.right += client_adjusted.right - client.right;
1221 rc.bottom += client_adjusted.bottom - client.bottom;
1222 SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1223 SWP_NOZORDER | SWP_NOMOVE);
1225 GetWindowRect( hwnd, &rc );
1226 fodInfos->DlgInfos.hwndGrip =
1227 CreateWindowExA( 0, "SCROLLBAR", NULL,
1228 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1229 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1230 rc.right - gripx, rc.bottom - gripy,
1231 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1234 fodInfos->DlgInfos.hwndCustomDlg =
1235 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1237 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1238 FILEDLG95_FillControls(hwnd, wParam, lParam);
1240 if( fodInfos->DlgInfos.hwndCustomDlg)
1241 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1243 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1244 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1245 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1248 /* if the app has changed the position of the invisible listbox,
1249 * change that of the listview (browser) as well */
1250 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1251 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1252 if( !EqualRect( &rc, &rcstc))
1254 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1255 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1256 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1257 SWP_NOACTIVATE | SWP_NOZORDER);
1260 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1262 GetWindowRect( hwnd, &rc);
1263 fodInfos->sizedlg.cx = rc.right - rc.left;
1264 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1265 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1266 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1267 GetClientRect( hwnd, &rc);
1268 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1269 rc.right - gripx, rc.bottom - gripy,
1270 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1271 /* resize the dialog to the previous invocation */
1272 if( MemDialogSize.cx && MemDialogSize.cy)
1273 SetWindowPos( hwnd, NULL,
1274 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1275 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1278 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1279 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1281 return 0;
1283 case WM_SIZE:
1284 return FILEDLG95_OnWMSize(hwnd, wParam);
1285 case WM_GETMINMAXINFO:
1286 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1287 case WM_COMMAND:
1288 return FILEDLG95_OnWMCommand(hwnd, wParam);
1289 case WM_DRAWITEM:
1291 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1293 case IDC_LOOKIN:
1294 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1295 return TRUE;
1298 return FALSE;
1300 case WM_GETISHELLBROWSER:
1301 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1303 case WM_DESTROY:
1305 FileOpenDlgInfos * fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1306 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1307 MemDialogSize = fodInfos->sizedlg;
1308 RemovePropA(hwnd, FileOpenDlgInfosStr);
1309 return FALSE;
1311 case WM_NOTIFY:
1313 LPNMHDR lpnmh = (LPNMHDR)lParam;
1314 UINT stringId = -1;
1316 /* set up the button tooltips strings */
1317 if(TTN_GETDISPINFOA == lpnmh->code )
1319 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1320 switch(lpnmh->idFrom )
1322 /* Up folder button */
1323 case FCIDM_TB_UPFOLDER:
1324 stringId = IDS_UPFOLDER;
1325 break;
1326 /* New folder button */
1327 case FCIDM_TB_NEWFOLDER:
1328 stringId = IDS_NEWFOLDER;
1329 break;
1330 /* List option button */
1331 case FCIDM_TB_SMALLICON:
1332 stringId = IDS_LISTVIEW;
1333 break;
1334 /* Details option button */
1335 case FCIDM_TB_REPORTVIEW:
1336 stringId = IDS_REPORTVIEW;
1337 break;
1338 /* Desktop button */
1339 case FCIDM_TB_DESKTOP:
1340 stringId = IDS_TODESKTOP;
1341 break;
1342 default:
1343 stringId = 0;
1345 lpdi->hinst = COMDLG32_hInstance;
1346 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1348 return FALSE;
1350 default :
1351 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1352 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1353 return FALSE;
1357 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1359 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1360 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1363 /***********************************************************************
1364 * FILEDLG95_InitControls
1366 * WM_INITDIALOG message handler (before hook notification)
1368 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1370 BOOL win2000plus = FALSE;
1371 BOOL win98plus = FALSE;
1372 BOOL handledPath = FALSE;
1373 OSVERSIONINFOW osVi;
1374 static const WCHAR szwSlash[] = { '\\', 0 };
1375 static const WCHAR szwStar[] = { '*',0 };
1377 static const TBBUTTON tbb[] =
1379 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1380 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1381 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1382 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1383 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1384 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1385 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1386 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1387 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1389 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1391 RECT rectTB;
1392 RECT rectlook;
1394 HIMAGELIST toolbarImageList;
1395 SHFILEINFOA shFileInfo;
1396 ITEMIDLIST *desktopPidl;
1398 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1400 TRACE("%p\n", fodInfos);
1402 /* Get windows version emulating */
1403 osVi.dwOSVersionInfoSize = sizeof(osVi);
1404 GetVersionExW(&osVi);
1405 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1406 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1407 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1408 win2000plus = (osVi.dwMajorVersion > 4);
1409 if (win2000plus) win98plus = TRUE;
1411 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1414 /* Use either the edit or the comboboxex for the filename control */
1415 if (filename_is_edit( fodInfos ))
1417 DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1418 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1420 else
1422 DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1423 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1426 /* Get the hwnd of the controls */
1427 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1428 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1430 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1431 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1433 /* construct the toolbar */
1434 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1435 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1437 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1438 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1439 rectTB.left = rectlook.right;
1440 rectTB.top = rectlook.top-1;
1442 if (fodInfos->unicode)
1443 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1444 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1445 rectTB.left, rectTB.top,
1446 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1447 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1448 else
1449 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1450 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1451 rectTB.left, rectTB.top,
1452 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1453 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1455 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1457 /* FIXME: use TB_LOADIMAGES when implemented */
1458 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1459 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1460 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1462 /* Retrieve and add desktop icon to the toolbar */
1463 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1464 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1465 SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1466 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1467 ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1469 DestroyIcon(shFileInfo.hIcon);
1470 CoTaskMemFree(desktopPidl);
1472 /* Finish Toolbar Construction */
1473 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1474 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1476 /* Set the window text with the text specified in the OPENFILENAME structure */
1477 if(fodInfos->title)
1479 SetWindowTextW(hwnd,fodInfos->title);
1481 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1483 WCHAR buf[64];
1484 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1485 SetWindowTextW(hwnd, buf);
1488 /* Initialise the file name edit control */
1489 handledPath = FALSE;
1490 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1492 if(fodInfos->filename)
1494 /* 1. If win2000 or higher and filename contains a path, use it
1495 in preference over the lpstrInitialDir */
1496 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1497 WCHAR tmpBuf[MAX_PATH];
1498 WCHAR *nameBit;
1499 DWORD result;
1501 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1502 if (result) {
1504 /* nameBit is always shorter than the original filename. It may be NULL
1505 * when the filename contains only a drive name instead of file name */
1506 if (nameBit)
1508 lstrcpyW(fodInfos->filename,nameBit);
1509 *nameBit = 0x00;
1511 else
1512 *fodInfos->filename = '\0';
1514 MemFree(fodInfos->initdir);
1515 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1516 lstrcpyW(fodInfos->initdir, tmpBuf);
1517 handledPath = TRUE;
1518 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1519 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1521 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1523 } else {
1524 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1528 /* 2. (All platforms) If initdir is not null, then use it */
1529 if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1531 /* Work out the proper path as supplied one might be relative */
1532 /* (Here because supplying '.' as dir browses to My Computer) */
1533 WCHAR tmpBuf[MAX_PATH];
1534 WCHAR tmpBuf2[MAX_PATH];
1535 WCHAR *nameBit;
1536 DWORD result;
1538 lstrcpyW(tmpBuf, fodInfos->initdir);
1539 if (PathFileExistsW(tmpBuf)) {
1540 /* initdir does not have to be a directory. If a file is
1541 * specified, the dir part is taken */
1542 if (PathIsDirectoryW(tmpBuf)) {
1543 PathAddBackslashW(tmpBuf);
1544 lstrcatW(tmpBuf, szwStar);
1546 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1547 if (result) {
1548 *nameBit = 0x00;
1549 MemFree(fodInfos->initdir);
1550 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1551 lstrcpyW(fodInfos->initdir, tmpBuf2);
1552 handledPath = TRUE;
1553 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1556 else if (fodInfos->initdir)
1558 MemFree(fodInfos->initdir);
1559 fodInfos->initdir = NULL;
1560 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1564 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1566 /* 3. All except w2k+: if filename contains a path use it */
1567 if (!win2000plus && fodInfos->filename &&
1568 *fodInfos->filename &&
1569 strpbrkW(fodInfos->filename, szwSlash)) {
1570 WCHAR tmpBuf[MAX_PATH];
1571 WCHAR *nameBit;
1572 DWORD result;
1574 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1575 tmpBuf, &nameBit);
1576 if (result) {
1577 int len;
1579 /* nameBit is always shorter than the original filename */
1580 lstrcpyW(fodInfos->filename, nameBit);
1581 *nameBit = 0x00;
1583 len = lstrlenW(tmpBuf);
1584 MemFree(fodInfos->initdir);
1585 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1586 lstrcpyW(fodInfos->initdir, tmpBuf);
1588 handledPath = TRUE;
1589 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1590 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1592 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1595 /* 4. Win2000+: Recently used */
1596 if (!handledPath && win2000plus) {
1597 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1598 fodInfos->initdir[0] = '\0';
1600 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1602 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1603 handledPath = TRUE;
1604 }else{
1605 MemFree(fodInfos->initdir);
1606 fodInfos->initdir = NULL;
1610 /* 5. win98+ and win2000+ if any files of specified filter types in
1611 current directory, use it */
1612 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1614 LPCWSTR lpstrPos = fodInfos->filter;
1615 WIN32_FIND_DATAW FindFileData;
1616 HANDLE hFind;
1618 while (1)
1620 /* filter is a list... title\0ext\0......\0\0 */
1622 /* Skip the title */
1623 if(! *lpstrPos) break; /* end */
1624 lpstrPos += lstrlenW(lpstrPos) + 1;
1626 /* See if any files exist in the current dir with this extension */
1627 if(! *lpstrPos) break; /* end */
1629 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1631 if (hFind == INVALID_HANDLE_VALUE) {
1632 /* None found - continue search */
1633 lpstrPos += lstrlenW(lpstrPos) + 1;
1635 } else {
1637 MemFree(fodInfos->initdir);
1638 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1639 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1641 handledPath = TRUE;
1642 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1643 debugstr_w(lpstrPos));
1644 FindClose(hFind);
1645 break;
1650 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1651 if (!handledPath && (win2000plus || win98plus)) {
1652 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1654 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1656 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1658 /* last fallback */
1659 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1660 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1661 } else {
1662 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1664 } else {
1665 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1667 handledPath = TRUE;
1668 } else if (!handledPath) {
1669 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1670 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1671 handledPath = TRUE;
1672 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1675 SetFocus( fodInfos->DlgInfos.hwndFileName );
1676 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1678 /* Must the open as read only check box be checked ?*/
1679 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1681 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1684 /* Must the open as read only check box be hidden? */
1685 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1687 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1688 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1691 /* Must the help button be hidden? */
1692 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1694 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1695 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1698 /* change Open to Save */
1699 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1701 WCHAR buf[16];
1702 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1703 SetDlgItemTextW(hwnd, IDOK, buf);
1704 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1705 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1708 /* Initialize the filter combo box */
1709 FILEDLG95_FILETYPE_Init(hwnd);
1711 return 0;
1714 /***********************************************************************
1715 * FILEDLG95_ResizeControls
1717 * WM_INITDIALOG message handler (after hook notification)
1719 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1721 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1723 if (fodInfos->DlgInfos.hwndCustomDlg)
1725 RECT rc;
1726 UINT flags = SWP_NOACTIVATE;
1728 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1729 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1731 /* resize the custom dialog to the parent size */
1732 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1733 GetClientRect(hwnd, &rc);
1734 else
1736 /* our own fake template is zero sized and doesn't have children, so
1737 * there is no need to resize it. Picasa depends on it.
1739 flags |= SWP_NOSIZE;
1740 SetRectEmpty(&rc);
1742 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1743 0, 0, rc.right, rc.bottom, flags);
1745 else
1747 /* Resize the height; if opened as read-only, checkbox and help button are
1748 * hidden and we are not using a custom template nor a customDialog
1750 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1751 (!(fodInfos->ofnInfos->Flags &
1752 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1754 RECT rectDlg, rectHelp, rectCancel;
1755 GetWindowRect(hwnd, &rectDlg);
1756 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1757 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1758 /* subtract the height of the help button plus the space between the help
1759 * button and the cancel button to the height of the dialog
1761 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1762 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1763 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1766 return TRUE;
1769 /***********************************************************************
1770 * FILEDLG95_FillControls
1772 * WM_INITDIALOG message handler (after hook notification)
1774 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1776 LPITEMIDLIST pidlItemId = NULL;
1778 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1780 TRACE("dir=%s file=%s\n",
1781 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1783 /* Get the initial directory pidl */
1785 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1787 WCHAR path[MAX_PATH];
1789 GetCurrentDirectoryW(MAX_PATH,path);
1790 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1793 /* Initialise shell objects */
1794 FILEDLG95_SHELL_Init(hwnd);
1796 /* Initialize the Look In combo box */
1797 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1799 /* Browse to the initial directory */
1800 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1802 /* Free pidlItem memory */
1803 COMDLG32_SHFree(pidlItemId);
1805 return TRUE;
1807 /***********************************************************************
1808 * FILEDLG95_Clean
1810 * Regroups all the cleaning functions of the filedlg
1812 void FILEDLG95_Clean(HWND hwnd)
1814 FILEDLG95_FILETYPE_Clean(hwnd);
1815 FILEDLG95_LOOKIN_Clean(hwnd);
1816 FILEDLG95_SHELL_Clean(hwnd);
1818 /***********************************************************************
1819 * FILEDLG95_OnWMCommand
1821 * WM_COMMAND message handler
1823 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1825 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1826 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1827 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1829 switch(wID)
1831 /* OK button */
1832 case IDOK:
1833 FILEDLG95_OnOpen(hwnd);
1834 break;
1835 /* Cancel button */
1836 case IDCANCEL:
1837 FILEDLG95_Clean(hwnd);
1838 EndDialog(hwnd, FALSE);
1839 break;
1840 /* Filetype combo box */
1841 case IDC_FILETYPE:
1842 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1843 break;
1844 /* LookIn combo box */
1845 case IDC_LOOKIN:
1846 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1847 break;
1849 /* --- toolbar --- */
1850 /* Up folder button */
1851 case FCIDM_TB_UPFOLDER:
1852 FILEDLG95_SHELL_UpFolder(hwnd);
1853 break;
1854 /* New folder button */
1855 case FCIDM_TB_NEWFOLDER:
1856 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1857 break;
1858 /* List option button */
1859 case FCIDM_TB_SMALLICON:
1860 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1861 break;
1862 /* Details option button */
1863 case FCIDM_TB_REPORTVIEW:
1864 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1865 break;
1866 /* Details option button */
1867 case FCIDM_TB_DESKTOP:
1868 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1869 break;
1871 case edt1:
1872 case cmb13:
1873 break;
1876 /* Do not use the listview selection anymore */
1877 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1878 return 0;
1881 /***********************************************************************
1882 * FILEDLG95_OnWMGetIShellBrowser
1884 * WM_GETISHELLBROWSER message handler
1886 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1888 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1890 TRACE("\n");
1892 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1894 return TRUE;
1898 /***********************************************************************
1899 * FILEDLG95_SendFileOK
1901 * Sends the CDN_FILEOK notification if required
1903 * RETURNS
1904 * TRUE if the dialog should close
1905 * FALSE if the dialog should not be closed
1907 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1909 /* ask the hook if we can close */
1910 if(IsHooked(fodInfos))
1912 LRESULT retval = 0;
1914 TRACE("---\n");
1915 /* First send CDN_FILEOK as MSDN doc says */
1916 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1917 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1918 if( retval)
1920 TRACE("canceled\n");
1921 return FALSE;
1924 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1925 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1926 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1927 if( retval)
1929 TRACE("canceled\n");
1930 return FALSE;
1933 return TRUE;
1936 /***********************************************************************
1937 * FILEDLG95_OnOpenMultipleFiles
1939 * Handles the opening of multiple files.
1941 * FIXME
1942 * check destination buffer size
1944 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
1946 WCHAR lpstrPathSpec[MAX_PATH] = {0};
1947 UINT nCount, nSizePath;
1948 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1950 TRACE("\n");
1952 if(fodInfos->unicode)
1954 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1955 ofn->lpstrFile[0] = '\0';
1957 else
1959 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
1960 ofn->lpstrFile[0] = '\0';
1963 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
1965 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
1966 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
1967 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
1969 LPWSTR lpstrTemp = lpstrFileList;
1971 for ( nCount = 0; nCount < nFileCount; nCount++ )
1973 LPITEMIDLIST pidl;
1975 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
1976 if (!pidl)
1978 WCHAR lpstrNotFound[100];
1979 WCHAR lpstrMsg[100];
1980 WCHAR tmp[400];
1981 static const WCHAR nl[] = {'\n',0};
1983 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
1984 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
1986 lstrcpyW(tmp, lpstrTemp);
1987 lstrcatW(tmp, nl);
1988 lstrcatW(tmp, lpstrNotFound);
1989 lstrcatW(tmp, nl);
1990 lstrcatW(tmp, lpstrMsg);
1992 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
1993 return FALSE;
1996 /* move to the next file in the list of files */
1997 lpstrTemp += lstrlenW(lpstrTemp) + 1;
1998 COMDLG32_SHFree(pidl);
2002 nSizePath = lstrlenW(lpstrPathSpec) + 1;
2003 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2005 /* For "oldstyle" dialog the components have to
2006 be separated by blanks (not '\0'!) and short
2007 filenames have to be used! */
2008 FIXME("Components have to be separated by blanks\n");
2010 if(fodInfos->unicode)
2012 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2013 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2014 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2016 else
2018 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2020 if (ofn->lpstrFile != NULL)
2022 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2023 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2024 if (ofn->nMaxFile > nSizePath)
2026 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2027 ofn->lpstrFile + nSizePath,
2028 ofn->nMaxFile - nSizePath, NULL, NULL);
2033 fodInfos->ofnInfos->nFileOffset = nSizePath;
2034 fodInfos->ofnInfos->nFileExtension = 0;
2036 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2037 return FALSE;
2039 /* clean and exit */
2040 FILEDLG95_Clean(hwnd);
2041 return EndDialog(hwnd,TRUE);
2044 /* Returns the 'slot name' of the given module_name in the registry's
2045 * most-recently-used list. This will be an ASCII value in the
2046 * range ['a','z'). Returns zero on error.
2048 * The slot's value in the registry has the form:
2049 * module_name\0mru_path\0
2051 * If stored_path is given, then stored_path will contain the path name
2052 * stored in the registry's MRU list for the given module_name.
2054 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2055 * MRU list key for the given module_name.
2057 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2059 WCHAR mru_list[32], *cur_mru_slot;
2060 BOOL taken[25] = {0};
2061 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2062 HKEY hkey_tmp, *hkey;
2063 LONG ret;
2065 if(hkey_ret)
2066 hkey = hkey_ret;
2067 else
2068 hkey = &hkey_tmp;
2070 if(stored_path)
2071 *stored_path = '\0';
2073 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2074 if(ret){
2075 WARN("Unable to create MRU key: %d\n", ret);
2076 return 0;
2079 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2080 (LPBYTE)mru_list, &mru_list_size);
2081 if(ret || key_type != REG_SZ){
2082 if(ret == ERROR_FILE_NOT_FOUND)
2083 return 'a';
2085 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2086 RegCloseKey(*hkey);
2087 return 0;
2090 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2091 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2092 DWORD value_data_size = sizeof(value_data);
2094 *value_name = *cur_mru_slot;
2096 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2097 &key_type, (LPBYTE)value_data, &value_data_size);
2098 if(ret || key_type != REG_BINARY){
2099 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2100 continue;
2103 if(!strcmpiW(module_name, value_data)){
2104 if(!hkey_ret)
2105 RegCloseKey(*hkey);
2106 if(stored_path)
2107 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2108 return *value_name;
2112 if(!hkey_ret)
2113 RegCloseKey(*hkey);
2115 /* the module name isn't in the registry, so find the next open slot */
2116 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2117 taken[*cur_mru_slot - 'a'] = TRUE;
2118 for(i = 0; i < 25; ++i){
2119 if(!taken[i])
2120 return i + 'a';
2123 /* all slots are taken, so return the last one in MRUList */
2124 --cur_mru_slot;
2125 return *cur_mru_slot;
2128 /* save the given filename as most-recently-used path for this module */
2129 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2131 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2132 LONG ret;
2133 HKEY hkey;
2135 /* get the current executable's name */
2136 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2137 WARN("GotModuleFileName failed: %d\n", GetLastError());
2138 return;
2140 module_name = strrchrW(module_path, '\\');
2141 if(!module_name)
2142 module_name = module_path;
2143 else
2144 module_name += 1;
2146 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2147 if(!slot)
2148 return;
2149 *slot_name = slot;
2151 { /* update the slot's info */
2152 WCHAR *path_ends, *final;
2153 DWORD path_len, final_len;
2155 /* use only the path segment of `filename' */
2156 path_ends = strrchrW(filename, '\\');
2157 path_len = path_ends - filename;
2159 final_len = path_len + lstrlenW(module_name) + 2;
2161 final = MemAlloc(final_len * sizeof(WCHAR));
2162 if(!final)
2163 return;
2164 lstrcpyW(final, module_name);
2165 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2166 final[final_len-1] = '\0';
2168 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2169 final_len * sizeof(WCHAR));
2170 if(ret){
2171 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2172 MemFree(final);
2173 RegCloseKey(hkey);
2174 return;
2177 MemFree(final);
2180 { /* update MRUList value */
2181 WCHAR old_mru_list[32], new_mru_list[32];
2182 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2183 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2185 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2186 (LPBYTE)old_mru_list, &mru_list_size);
2187 if(ret || key_type != REG_SZ){
2188 if(ret == ERROR_FILE_NOT_FOUND){
2189 new_mru_list[0] = slot;
2190 new_mru_list[1] = '\0';
2191 }else{
2192 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2193 RegCloseKey(hkey);
2194 return;
2196 }else{
2197 /* copy old list data over so that the new slot is at the start
2198 * of the list */
2199 *new_mru_slot++ = slot;
2200 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2201 if(*old_mru_slot != slot)
2202 *new_mru_slot++ = *old_mru_slot;
2204 *new_mru_slot = '\0';
2207 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2208 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2209 if(ret){
2210 WARN("Error saving MRUList data: %d\n", ret);
2211 RegCloseKey(hkey);
2212 return;
2217 /* load the most-recently-used path for this module */
2218 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2220 WCHAR module_path[MAX_PATH], *module_name;
2222 /* get the current executable's name */
2223 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2224 WARN("GotModuleFileName failed: %d\n", GetLastError());
2225 return;
2227 module_name = strrchrW(module_path, '\\');
2228 if(!module_name)
2229 module_name = module_path;
2230 else
2231 module_name += 1;
2233 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2234 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2237 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2239 WCHAR strMsgTitle[MAX_PATH];
2240 WCHAR strMsgText [MAX_PATH];
2241 if (idCaption)
2242 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2243 else
2244 strMsgTitle[0] = '\0';
2245 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2246 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2249 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2250 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2252 int nOpenAction = defAction;
2253 LPWSTR lpszTemp, lpszTemp1;
2254 LPITEMIDLIST pidl = NULL;
2255 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2257 /* check for invalid chars */
2258 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2260 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2261 return FALSE;
2264 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2266 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2267 while (lpszTemp1)
2269 LPSHELLFOLDER lpsfChild;
2270 WCHAR lpwstrTemp[MAX_PATH];
2271 DWORD dwEaten, dwAttributes;
2272 LPWSTR p;
2274 lstrcpyW(lpwstrTemp, lpszTemp);
2275 p = PathFindNextComponentW(lpwstrTemp);
2277 if (!p) break; /* end of path */
2279 *p = 0;
2280 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2282 /* There are no wildcards when OFN_NOVALIDATE is set */
2283 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2285 static const WCHAR wszWild[] = { '*', '?', 0 };
2286 /* if the last element is a wildcard do a search */
2287 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2289 nOpenAction = ONOPEN_SEARCH;
2290 break;
2293 lpszTemp1 = lpszTemp;
2295 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2297 /* append a backslash to drive letters */
2298 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2299 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2300 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2302 PathAddBackslashW(lpwstrTemp);
2305 dwAttributes = SFGAO_FOLDER;
2306 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2308 /* the path component is valid, we have a pidl of the next path component */
2309 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2310 if(dwAttributes & SFGAO_FOLDER)
2312 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2314 ERR("bind to failed\n"); /* should not fail */
2315 break;
2317 IShellFolder_Release(*ppsf);
2318 *ppsf = lpsfChild;
2319 lpsfChild = NULL;
2321 else
2323 TRACE("value\n");
2325 /* end dialog, return value */
2326 nOpenAction = ONOPEN_OPEN;
2327 break;
2329 COMDLG32_SHFree(pidl);
2330 pidl = NULL;
2332 else if (!(flags & OFN_NOVALIDATE))
2334 if(*lpszTemp || /* points to trailing null for last path element */
2335 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2337 if(flags & OFN_PATHMUSTEXIST)
2339 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2340 break;
2343 else
2345 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2347 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2348 break;
2351 /* change to the current folder */
2352 nOpenAction = ONOPEN_OPEN;
2353 break;
2355 else
2357 nOpenAction = ONOPEN_OPEN;
2358 break;
2361 if(pidl) COMDLG32_SHFree(pidl);
2363 return nOpenAction;
2366 /***********************************************************************
2367 * FILEDLG95_OnOpen
2369 * Ok button WM_COMMAND message handler
2371 * If the function succeeds, the return value is nonzero.
2373 BOOL FILEDLG95_OnOpen(HWND hwnd)
2375 LPWSTR lpstrFileList;
2376 UINT nFileCount = 0;
2377 UINT sizeUsed = 0;
2378 BOOL ret = TRUE;
2379 WCHAR lpstrPathAndFile[MAX_PATH];
2380 LPSHELLFOLDER lpsf = NULL;
2381 int nOpenAction;
2382 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2384 TRACE("hwnd=%p\n", hwnd);
2386 /* try to browse the selected item */
2387 if(BrowseSelectedFolder(hwnd))
2388 return FALSE;
2390 /* get the files from the edit control */
2391 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2393 if(nFileCount == 0)
2394 return FALSE;
2396 if(nFileCount > 1)
2398 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2399 goto ret;
2402 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2405 Step 1: Build a complete path name from the current folder and
2406 the filename or path in the edit box.
2407 Special cases:
2408 - the path in the edit box is a root path
2409 (with or without drive letter)
2410 - the edit box contains ".." (or a path with ".." in it)
2413 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2414 MemFree(lpstrFileList);
2417 Step 2: here we have a cleaned up path
2419 We have to parse the path step by step to see if we have to browse
2420 to a folder if the path points to a directory or the last
2421 valid element is a directory.
2423 valid variables:
2424 lpstrPathAndFile: cleaned up path
2427 if (nFileCount &&
2428 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2429 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2430 nOpenAction = ONOPEN_OPEN;
2431 else
2432 nOpenAction = ONOPEN_BROWSE;
2434 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2435 fodInfos->ofnInfos->Flags,
2436 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2437 nOpenAction);
2438 if(!nOpenAction)
2439 goto ret;
2442 Step 3: here we have a cleaned up and validated path
2444 valid variables:
2445 lpsf: ShellFolder bound to the rightmost valid path component
2446 lpstrPathAndFile: cleaned up path
2447 nOpenAction: action to do
2449 TRACE("end validate sf=%p\n", lpsf);
2451 switch(nOpenAction)
2453 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2454 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2456 int iPos;
2457 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2458 DWORD len;
2460 /* replace the current filter */
2461 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2462 len = lstrlenW(lpszTemp)+1;
2463 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2464 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2466 /* set the filter cb to the extension when possible */
2467 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2468 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2470 /* fall through */
2471 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2472 TRACE("ONOPEN_BROWSE\n");
2474 IPersistFolder2 * ppf2;
2475 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2477 LPITEMIDLIST pidlCurrent;
2478 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2479 IPersistFolder2_Release(ppf2);
2480 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2482 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2483 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2485 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2486 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2489 else if( nOpenAction == ONOPEN_SEARCH )
2491 if (fodInfos->Shell.FOIShellView)
2492 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2494 COMDLG32_SHFree(pidlCurrent);
2495 if (filename_is_edit( fodInfos ))
2496 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2497 else
2499 HWND hwnd;
2501 hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2502 SendMessageW(hwnd, EM_SETSEL, 0, -1);
2506 ret = FALSE;
2507 break;
2508 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2509 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2511 WCHAR *ext = NULL;
2513 /* update READONLY check box flag */
2514 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2515 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2516 else
2517 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2519 /* Attach the file extension with file name*/
2520 ext = PathFindExtensionW(lpstrPathAndFile);
2521 if (! *ext && fodInfos->defext)
2523 /* if no extension is specified with file name, then */
2524 /* attach the extension from file filter or default one */
2526 WCHAR *filterExt = NULL;
2527 LPWSTR lpstrFilter = NULL;
2528 static const WCHAR szwDot[] = {'.',0};
2529 int PathLength = lstrlenW(lpstrPathAndFile);
2531 /*Get the file extension from file type filter*/
2532 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2533 fodInfos->ofnInfos->nFilterIndex-1);
2535 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2537 WCHAR* filterSearchIndex;
2538 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2539 strcpyW(filterExt, lpstrFilter);
2541 /* if a semicolon-separated list of file extensions was given, do not include the
2542 semicolon or anything after it in the extension.
2543 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2544 filterSearchIndex = strchrW(filterExt, ';');
2545 if (filterSearchIndex)
2547 filterSearchIndex[0] = '\0';
2550 /* find the file extension by searching for the first dot in filterExt */
2551 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2552 /* if the extension is invalid or contains a glob, ignore it */
2553 filterSearchIndex = strchrW(filterExt, '.');
2554 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2556 strcpyW(filterExt, filterSearchIndex);
2558 else
2560 HeapFree(GetProcessHeap(), 0, filterExt);
2561 filterExt = NULL;
2565 if (!filterExt)
2567 /* use the default file extension */
2568 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2569 strcpyW(filterExt, fodInfos->defext);
2572 if (*filterExt) /* ignore filterExt="" */
2574 /* Attach the dot*/
2575 lstrcatW(lpstrPathAndFile, szwDot);
2576 /* Attach the extension */
2577 lstrcatW(lpstrPathAndFile, filterExt);
2580 HeapFree(GetProcessHeap(), 0, filterExt);
2582 /* In Open dialog: if file does not exist try without extension */
2583 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2584 lpstrPathAndFile[PathLength] = '\0';
2586 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2587 if (*ext)
2588 ext++;
2589 if (!lstrcmpiW(fodInfos->defext, ext))
2590 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2591 else
2592 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2595 /* In Save dialog: check if the file already exists */
2596 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2597 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2598 && PathFileExistsW(lpstrPathAndFile))
2600 WCHAR lpstrOverwrite[100];
2601 int answer;
2603 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2604 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2605 MB_YESNO | MB_ICONEXCLAMATION);
2606 if (answer == IDNO || answer == IDCANCEL)
2608 ret = FALSE;
2609 goto ret;
2613 /* In Open dialog: check if it should be created if it doesn't exist */
2614 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2615 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2616 && !PathFileExistsW(lpstrPathAndFile))
2618 WCHAR lpstrCreate[100];
2619 int answer;
2621 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2622 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2623 MB_YESNO | MB_ICONEXCLAMATION);
2624 if (answer == IDNO || answer == IDCANCEL)
2626 ret = FALSE;
2627 goto ret;
2631 /* Check that the size of the file does not exceed buffer size.
2632 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2633 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2634 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2637 /* fill destination buffer */
2638 if (fodInfos->ofnInfos->lpstrFile)
2640 if(fodInfos->unicode)
2642 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2644 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2645 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2646 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2648 else
2650 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2652 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2653 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2654 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2655 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2659 if(fodInfos->unicode)
2661 LPWSTR lpszTemp;
2663 /* set filename offset */
2664 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2665 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2667 /* set extension offset */
2668 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2669 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2671 else
2673 LPSTR lpszTemp;
2674 CHAR tempFileA[MAX_PATH];
2676 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2677 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2678 tempFileA, sizeof(tempFileA), NULL, NULL);
2680 /* set filename offset */
2681 lpszTemp = PathFindFileNameA(tempFileA);
2682 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2684 /* set extension offset */
2685 lpszTemp = PathFindExtensionA(tempFileA);
2686 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2689 /* set the lpstrFileTitle */
2690 if(fodInfos->ofnInfos->lpstrFileTitle)
2692 LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2693 if(fodInfos->unicode)
2695 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2696 lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2698 else
2700 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2701 WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2702 ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2706 /* copy currently selected filter to lpstrCustomFilter */
2707 if (fodInfos->ofnInfos->lpstrCustomFilter)
2709 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2710 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2711 NULL, 0, NULL, NULL);
2712 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2714 LPSTR s = ofn->lpstrCustomFilter;
2715 s += strlen(ofn->lpstrCustomFilter)+1;
2716 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2717 s, len, NULL, NULL);
2722 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2723 goto ret;
2725 FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2727 TRACE("close\n");
2728 FILEDLG95_Clean(hwnd);
2729 ret = EndDialog(hwnd, TRUE);
2731 else
2733 WORD size;
2735 size = lstrlenW(lpstrPathAndFile) + 1;
2736 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2737 size += 1;
2738 /* return needed size in first two bytes of lpstrFile */
2739 if(fodInfos->ofnInfos->lpstrFile)
2740 *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2741 FILEDLG95_Clean(hwnd);
2742 ret = EndDialog(hwnd, FALSE);
2743 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2746 break;
2749 ret:
2750 if(lpsf) IShellFolder_Release(lpsf);
2751 return ret;
2754 /***********************************************************************
2755 * FILEDLG95_SHELL_Init
2757 * Initialisation of the shell objects
2759 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2761 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2763 TRACE("\n");
2766 * Initialisation of the FileOpenDialogInfos structure
2769 /* Shell */
2771 /*ShellInfos */
2772 fodInfos->ShellInfos.hwndOwner = hwnd;
2774 /* Disable multi-select if flag not set */
2775 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2777 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2779 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2780 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2782 /* Construct the IShellBrowser interface */
2783 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2785 return NOERROR;
2788 /***********************************************************************
2789 * FILEDLG95_SHELL_ExecuteCommand
2791 * Change the folder option and refresh the view
2792 * If the function succeeds, the return value is nonzero.
2794 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2796 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2797 IContextMenu * pcm;
2799 TRACE("(%p,%p)\n", hwnd, lpVerb);
2801 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2802 SVGIO_BACKGROUND,
2803 &IID_IContextMenu,
2804 (LPVOID*)&pcm)))
2806 CMINVOKECOMMANDINFO ci;
2807 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2808 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2809 ci.lpVerb = lpVerb;
2810 ci.hwnd = hwnd;
2812 IContextMenu_InvokeCommand(pcm, &ci);
2813 IContextMenu_Release(pcm);
2816 return FALSE;
2819 /***********************************************************************
2820 * FILEDLG95_SHELL_UpFolder
2822 * Browse to the specified object
2823 * If the function succeeds, the return value is nonzero.
2825 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2827 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2829 TRACE("\n");
2831 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2832 NULL,
2833 SBSP_PARENT)))
2835 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2836 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2837 return TRUE;
2839 return FALSE;
2842 /***********************************************************************
2843 * FILEDLG95_SHELL_BrowseToDesktop
2845 * Browse to the Desktop
2846 * If the function succeeds, the return value is nonzero.
2848 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2850 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2851 LPITEMIDLIST pidl;
2852 HRESULT hres;
2854 TRACE("\n");
2856 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2857 hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2858 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2859 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2860 COMDLG32_SHFree(pidl);
2861 return SUCCEEDED(hres);
2863 /***********************************************************************
2864 * FILEDLG95_SHELL_Clean
2866 * Cleans the memory used by shell objects
2868 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2870 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2872 TRACE("\n");
2874 COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2876 /* clean Shell interfaces */
2877 if (fodInfos->Shell.FOIShellView)
2879 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2880 IShellView_Release(fodInfos->Shell.FOIShellView);
2882 if (fodInfos->Shell.FOIShellFolder)
2883 IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2884 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2885 if (fodInfos->Shell.FOIDataObject)
2886 IDataObject_Release(fodInfos->Shell.FOIDataObject);
2889 /***********************************************************************
2890 * FILEDLG95_FILETYPE_Init
2892 * Initialisation of the file type combo box
2894 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2896 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2897 int nFilters = 0; /* number of filters */
2898 int nFilterIndexCB;
2900 TRACE("\n");
2902 if(fodInfos->customfilter)
2904 /* customfilter has one entry... title\0ext\0
2905 * Set first entry of combo box item with customfilter
2907 LPWSTR lpstrExt;
2908 LPCWSTR lpstrPos = fodInfos->customfilter;
2910 /* Get the title */
2911 lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2913 /* Copy the extensions */
2914 if (! *lpstrPos) return E_FAIL; /* malformed filter */
2915 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2916 lstrcpyW(lpstrExt,lpstrPos);
2918 /* Add the item at the end of the combo */
2919 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2920 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2921 nFilters++;
2923 if(fodInfos->filter)
2925 LPCWSTR lpstrPos = fodInfos->filter;
2927 for(;;)
2929 /* filter is a list... title\0ext\0......\0\0
2930 * Set the combo item text to the title and the item data
2931 * to the ext
2933 LPCWSTR lpstrDisplay;
2934 LPWSTR lpstrExt;
2936 /* Get the title */
2937 if(! *lpstrPos) break; /* end */
2938 lpstrDisplay = lpstrPos;
2939 lpstrPos += lstrlenW(lpstrPos) + 1;
2941 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2943 nFilters++;
2945 /* Copy the extensions */
2946 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2947 lstrcpyW(lpstrExt,lpstrPos);
2948 lpstrPos += lstrlenW(lpstrPos) + 1;
2950 /* Add the item at the end of the combo */
2951 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
2953 /* malformed filters are added anyway... */
2954 if (!*lpstrExt) break;
2959 * Set the current filter to the one specified
2960 * in the initialisation structure
2962 if (fodInfos->filter || fodInfos->customfilter)
2964 LPWSTR lpstrFilter;
2966 /* Check to make sure our index isn't out of bounds. */
2967 if ( fodInfos->ofnInfos->nFilterIndex >
2968 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
2969 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
2971 /* set default filter index */
2972 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
2973 fodInfos->ofnInfos->nFilterIndex = 1;
2975 /* calculate index of Combo Box item */
2976 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
2977 if (fodInfos->customfilter == NULL)
2978 nFilterIndexCB--;
2980 /* Set the current index selection. */
2981 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
2983 /* Get the corresponding text string from the combo box. */
2984 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2985 nFilterIndexCB);
2987 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
2988 lpstrFilter = NULL;
2990 if(lpstrFilter)
2992 DWORD len;
2993 CharLowerW(lpstrFilter); /* lowercase */
2994 len = lstrlenW(lpstrFilter)+1;
2995 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
2996 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
2998 } else
2999 fodInfos->ofnInfos->nFilterIndex = 0;
3000 return S_OK;
3003 /***********************************************************************
3004 * FILEDLG95_FILETYPE_OnCommand
3006 * WM_COMMAND of the file type combo box
3007 * If the function succeeds, the return value is nonzero.
3009 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
3011 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3013 switch(wNotifyCode)
3015 case CBN_SELENDOK:
3017 LPWSTR lpstrFilter;
3019 /* Get the current item of the filetype combo box */
3020 int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
3022 /* set the current filter index */
3023 fodInfos->ofnInfos->nFilterIndex = iItem +
3024 (fodInfos->customfilter == NULL ? 1 : 0);
3026 /* Set the current filter with the current selection */
3027 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3029 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3030 iItem);
3031 if((INT_PTR)lpstrFilter != CB_ERR)
3033 DWORD len;
3034 CharLowerW(lpstrFilter); /* lowercase */
3035 len = lstrlenW(lpstrFilter)+1;
3036 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3037 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3038 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3039 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3042 /* Refresh the actual view to display the included items*/
3043 if (fodInfos->Shell.FOIShellView)
3044 IShellView_Refresh(fodInfos->Shell.FOIShellView);
3047 return FALSE;
3049 /***********************************************************************
3050 * FILEDLG95_FILETYPE_SearchExt
3052 * searches for an extension in the filetype box
3054 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3056 int i, iCount = CBGetCount(hwnd);
3058 TRACE("%s\n", debugstr_w(lpstrExt));
3060 if(iCount != CB_ERR)
3062 for(i=0;i<iCount;i++)
3064 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3065 return i;
3068 return -1;
3071 /***********************************************************************
3072 * FILEDLG95_FILETYPE_Clean
3074 * Clean the memory used by the filetype combo box
3076 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3078 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3079 int iPos;
3080 int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3082 TRACE("\n");
3084 /* Delete each string of the combo and their associated data */
3085 if(iCount != CB_ERR)
3087 for(iPos = iCount-1;iPos>=0;iPos--)
3089 MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3090 CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3093 /* Current filter */
3094 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3098 /***********************************************************************
3099 * FILEDLG95_LOOKIN_Init
3101 * Initialisation of the look in combo box
3104 /* Small helper function, to determine if the unixfs shell extension is rooted
3105 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3107 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3108 HKEY hKey;
3109 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3110 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3111 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3112 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3113 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3114 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3115 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3117 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3118 return FALSE;
3120 RegCloseKey(hKey);
3121 return TRUE;
3124 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3126 IShellFolder *psfRoot, *psfDrives;
3127 IEnumIDList *lpeRoot, *lpeDrives;
3128 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3129 HDC hdc;
3130 TEXTMETRICW tm;
3131 LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3133 TRACE("\n");
3135 liInfos->iMaxIndentation = 0;
3137 SetPropA(hwndCombo, LookInInfosStr, liInfos);
3139 hdc = GetDC( hwndCombo );
3140 SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3141 GetTextMetricsW( hdc, &tm );
3142 ReleaseDC( hwndCombo, hdc );
3144 /* set item height for both text field and listbox */
3145 CBSetItemHeight( hwndCombo, -1, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3146 CBSetItemHeight( hwndCombo, 0, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3148 /* Turn on the extended UI for the combo box like Windows does */
3149 CBSetExtendedUI(hwndCombo, TRUE);
3151 /* Initialise data of Desktop folder */
3152 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3153 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3154 COMDLG32_SHFree(pidlTmp);
3156 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3158 SHGetDesktopFolder(&psfRoot);
3160 if (psfRoot)
3162 /* enumerate the contents of the desktop */
3163 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3165 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3167 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3169 /* If the unixfs extension is rooted, we don't expand the drives by default */
3170 if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3172 /* special handling for CSIDL_DRIVES */
3173 if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3175 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3177 /* enumerate the drives */
3178 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3180 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3182 pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3183 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3184 COMDLG32_SHFree(pidlAbsTmp);
3185 COMDLG32_SHFree(pidlTmp1);
3187 IEnumIDList_Release(lpeDrives);
3189 IShellFolder_Release(psfDrives);
3194 COMDLG32_SHFree(pidlTmp);
3196 IEnumIDList_Release(lpeRoot);
3198 IShellFolder_Release(psfRoot);
3201 COMDLG32_SHFree(pidlDrives);
3204 /***********************************************************************
3205 * FILEDLG95_LOOKIN_DrawItem
3207 * WM_DRAWITEM message handler
3209 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3211 COLORREF crWin = GetSysColor(COLOR_WINDOW);
3212 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3213 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3214 RECT rectText;
3215 RECT rectIcon;
3216 SHFILEINFOW sfi;
3217 HIMAGELIST ilItemImage;
3218 int iIndentation;
3219 TEXTMETRICW tm;
3220 LPSFOLDER tmpFolder;
3221 UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3222 UINT icon_width, icon_height;
3224 TRACE("\n");
3226 if(pDIStruct->itemID == -1)
3227 return 0;
3229 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3230 pDIStruct->itemID)))
3231 return 0;
3234 icon_width = GetSystemMetrics(SM_CXICON);
3235 icon_height = GetSystemMetrics(SM_CYICON);
3236 if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3238 icon_width = GetSystemMetrics(SM_CXSMICON);
3239 icon_height = GetSystemMetrics(SM_CYSMICON);
3240 shgfi_flags |= SHGFI_SMALLICON;
3243 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3244 0, &sfi, sizeof (sfi), shgfi_flags );
3246 /* Is this item selected ? */
3247 if(pDIStruct->itemState & ODS_SELECTED)
3249 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3250 SetBkColor(pDIStruct->hDC,crHighLight);
3251 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3253 else
3255 SetTextColor(pDIStruct->hDC,crText);
3256 SetBkColor(pDIStruct->hDC,crWin);
3257 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3260 /* Do not indent item if drawing in the edit of the combo */
3261 if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3262 iIndentation = 0;
3263 else
3264 iIndentation = tmpFolder->m_iIndent;
3266 /* Draw text and icon */
3268 /* Initialise the icon display area */
3269 rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3270 rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3271 rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3272 rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3274 /* Initialise the text display area */
3275 GetTextMetricsW(pDIStruct->hDC, &tm);
3276 rectText.left = rectIcon.right;
3277 rectText.top =
3278 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3279 rectText.right = pDIStruct->rcItem.right;
3280 rectText.bottom =
3281 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3283 /* Draw the icon from the image list */
3284 ImageList_Draw(ilItemImage,
3285 sfi.iIcon,
3286 pDIStruct->hDC,
3287 rectIcon.left,
3288 rectIcon.top,
3289 ILD_TRANSPARENT );
3291 /* Draw the associated text */
3292 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3293 return NOERROR;
3296 /***********************************************************************
3297 * FILEDLG95_LOOKIN_OnCommand
3299 * LookIn combo box WM_COMMAND message handler
3300 * If the function succeeds, the return value is nonzero.
3302 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3304 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3306 TRACE("%p\n", fodInfos);
3308 switch(wNotifyCode)
3310 case CBN_SELENDOK:
3312 LPSFOLDER tmpFolder;
3313 int iItem;
3315 iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3317 if( iItem == CB_ERR) return FALSE;
3319 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3320 iItem)))
3321 return FALSE;
3324 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3325 tmpFolder->pidlItem,
3326 SBSP_ABSOLUTE)))
3328 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3329 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3330 return TRUE;
3332 break;
3336 return FALSE;
3339 /***********************************************************************
3340 * FILEDLG95_LOOKIN_AddItem
3342 * Adds an absolute pidl item to the lookin combo box
3343 * returns the index of the inserted item
3345 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3347 LPITEMIDLIST pidlNext;
3348 SHFILEINFOW sfi;
3349 SFOLDER *tmpFolder;
3350 LookInInfos *liInfos;
3352 TRACE("%08x\n", iInsertId);
3354 if(!pidl)
3355 return -1;
3357 if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3358 return -1;
3360 tmpFolder = MemAlloc(sizeof(SFOLDER));
3361 tmpFolder->m_iIndent = 0;
3363 /* Calculate the indentation of the item in the lookin*/
3364 pidlNext = pidl;
3365 while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3367 tmpFolder->m_iIndent++;
3370 tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3372 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3373 liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3375 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3376 SHGetFileInfoW((LPCWSTR)pidl,
3378 &sfi,
3379 sizeof(sfi),
3380 SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX
3381 | SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3383 TRACE("-- Add %s attr=%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3385 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3387 int iItemID;
3389 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3391 /* Add the item at the end of the list */
3392 if(iInsertId < 0)
3394 iItemID = CBAddString(hwnd,sfi.szDisplayName);
3396 /* Insert the item at the iInsertId position*/
3397 else
3399 iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3402 CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3403 return iItemID;
3406 COMDLG32_SHFree( tmpFolder->pidlItem );
3407 MemFree( tmpFolder );
3408 return -1;
3412 /***********************************************************************
3413 * FILEDLG95_LOOKIN_InsertItemAfterParent
3415 * Insert an item below its parent
3417 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3420 LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3421 int iParentPos;
3423 TRACE("\n");
3425 if (pidl == pidlParent)
3426 return -1;
3428 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3430 if(iParentPos < 0)
3432 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3435 /* Free pidlParent memory */
3436 COMDLG32_SHFree(pidlParent);
3438 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3441 /***********************************************************************
3442 * FILEDLG95_LOOKIN_SelectItem
3444 * Adds an absolute pidl item to the lookin combo box
3445 * returns the index of the inserted item
3447 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3449 int iItemPos;
3450 LookInInfos *liInfos;
3452 TRACE("\n");
3454 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3456 liInfos = GetPropA(hwnd,LookInInfosStr);
3458 if(iItemPos < 0)
3460 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3461 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3464 else
3466 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3467 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3469 int iRemovedItem;
3471 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3472 break;
3473 if(iRemovedItem < iItemPos)
3474 iItemPos--;
3478 CBSetCurSel(hwnd,iItemPos);
3479 liInfos->uSelectedItem = iItemPos;
3481 return 0;
3485 /***********************************************************************
3486 * FILEDLG95_LOOKIN_RemoveMostExpandedItem
3488 * Remove the item with an expansion level over iExpansionLevel
3490 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3492 int iItemPos;
3493 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3495 TRACE("\n");
3497 if(liInfos->iMaxIndentation <= 2)
3498 return -1;
3500 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3502 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3503 COMDLG32_SHFree(tmpFolder->pidlItem);
3504 MemFree(tmpFolder);
3505 CBDeleteString(hwnd,iItemPos);
3506 liInfos->iMaxIndentation--;
3508 return iItemPos;
3511 return -1;
3514 /***********************************************************************
3515 * FILEDLG95_LOOKIN_SearchItem
3517 * Search for pidl in the lookin combo box
3518 * returns the index of the found item
3520 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3522 int i = 0;
3523 int iCount = CBGetCount(hwnd);
3525 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3527 if (iCount != CB_ERR)
3529 for(;i<iCount;i++)
3531 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3533 if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3534 return i;
3535 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3536 return i;
3540 return -1;
3543 /***********************************************************************
3544 * FILEDLG95_LOOKIN_Clean
3546 * Clean the memory used by the lookin combo box
3548 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3550 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3551 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3552 int iPos;
3553 int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3555 TRACE("\n");
3557 /* Delete each string of the combo and their associated data */
3558 if (iCount != CB_ERR)
3560 for(iPos = iCount-1;iPos>=0;iPos--)
3562 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3563 COMDLG32_SHFree(tmpFolder->pidlItem);
3564 MemFree(tmpFolder);
3565 CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3569 /* LookInInfos structure */
3570 MemFree(liInfos);
3571 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3574 /***********************************************************************
3575 * get_def_format
3577 * Fill the FORMATETC used in the shell id list
3579 static FORMATETC get_def_format(void)
3581 static CLIPFORMAT cfFormat;
3582 FORMATETC formatetc;
3584 if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA);
3585 formatetc.cfFormat = cfFormat;
3586 formatetc.ptd = 0;
3587 formatetc.dwAspect = DVASPECT_CONTENT;
3588 formatetc.lindex = -1;
3589 formatetc.tymed = TYMED_HGLOBAL;
3590 return formatetc;
3593 /***********************************************************************
3594 * FILEDLG95_FILENAME_FillFromSelection
3596 * fills the edit box from the cached DataObject
3598 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3600 FileOpenDlgInfos *fodInfos;
3601 LPITEMIDLIST pidl;
3602 LPWSTR lpstrAllFiles, lpstrTmp;
3603 UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength;
3604 STGMEDIUM medium;
3605 LPIDA cida;
3606 FORMATETC formatetc = get_def_format();
3608 TRACE("\n");
3609 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3611 if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium)))
3612 return;
3614 cida = GlobalLock(medium.u.hGlobal);
3615 nFileSelected = cida->cidl;
3617 /* Allocate a buffer */
3618 nAllFilesMaxLength = MAX_PATH + 3;
3619 lpstrAllFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nAllFilesMaxLength * sizeof(WCHAR));
3620 if (!lpstrAllFiles)
3621 goto ret;
3623 /* Loop through the selection, handle only files (not folders) */
3624 for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++)
3626 pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]);
3627 if (pidl)
3629 if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl))
3631 if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength)
3633 nAllFilesMaxLength *= 2;
3634 lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR));
3635 if (!lpstrTmp)
3636 goto ret;
3637 lpstrAllFiles = lpstrTmp;
3639 nFiles += 1;
3640 lpstrAllFiles[nAllFilesLength++] = '"';
3641 GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength);
3642 nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength);
3643 nAllFilesLength += nThisFileLength;
3644 lpstrAllFiles[nAllFilesLength++] = '"';
3645 lpstrAllFiles[nAllFilesLength++] = ' ';
3650 if (nFiles != 0)
3652 /* If there's only one file, use the name as-is without quotes */
3653 lpstrTmp = lpstrAllFiles;
3654 if (nFiles == 1)
3656 lpstrTmp += 1;
3657 lpstrTmp[nThisFileLength] = 0;
3659 SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp);
3660 /* Select the file name like Windows does */
3661 if (filename_is_edit(fodInfos))
3662 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3665 ret:
3666 HeapFree(GetProcessHeap(), 0, lpstrAllFiles);
3667 COMCTL32_ReleaseStgMedium(medium);
3671 /* copied from shell32 to avoid linking to it
3672 * Although shell32 is already linked the behaviour of exported StrRetToStrN
3673 * is dependent on whether emulated OS is unicode or not.
3675 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3677 switch (src->uType)
3679 case STRRET_WSTR:
3680 lstrcpynW(dest, src->u.pOleStr, len);
3681 COMDLG32_SHFree(src->u.pOleStr);
3682 break;
3684 case STRRET_CSTR:
3685 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3686 dest[len-1] = 0;
3687 break;
3689 case STRRET_OFFSET:
3690 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3691 dest[len-1] = 0;
3692 break;
3694 default:
3695 FIXME("unknown type %x!\n", src->uType);
3696 if (len) *dest = '\0';
3697 return E_FAIL;
3699 return S_OK;
3702 /***********************************************************************
3703 * FILEDLG95_FILENAME_GetFileNames
3705 * Copies the filenames to a delimited string list.
3707 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3709 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3710 UINT nFileCount = 0; /* number of files */
3711 UINT nStrLen = 0; /* length of string in edit control */
3712 LPWSTR lpstrEdit; /* buffer for string from edit control */
3714 TRACE("\n");
3716 /* get the filenames from the filename control */
3717 nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
3718 lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3719 GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
3721 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3723 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3724 MemFree(lpstrEdit);
3725 return nFileCount;
3729 * DATAOBJECT Helper functions
3732 /***********************************************************************
3733 * COMCTL32_ReleaseStgMedium
3735 * like ReleaseStgMedium from ole32
3737 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3739 if(medium.pUnkForRelease)
3741 IUnknown_Release(medium.pUnkForRelease);
3743 else
3745 GlobalUnlock(medium.u.hGlobal);
3746 GlobalFree(medium.u.hGlobal);
3750 /***********************************************************************
3751 * GetPidlFromDataObject
3753 * Return pidl(s) by number from the cached DataObject
3755 * nPidlIndex=0 gets the fully qualified root path
3757 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3760 STGMEDIUM medium;
3761 FORMATETC formatetc = get_def_format();
3762 LPITEMIDLIST pidl = NULL;
3764 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3766 if (!doSelected)
3767 return NULL;
3769 /* Get the pidls from IDataObject */
3770 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3772 LPIDA cida = GlobalLock(medium.u.hGlobal);
3773 if(nPidlIndex <= cida->cidl)
3775 pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3777 COMCTL32_ReleaseStgMedium(medium);
3779 return pidl;
3782 /***********************************************************************
3783 * GetNumSelected
3785 * Return the number of selected items in the DataObject.
3788 static UINT GetNumSelected( IDataObject *doSelected )
3790 UINT retVal = 0;
3791 STGMEDIUM medium;
3792 FORMATETC formatetc = get_def_format();
3794 TRACE("sv=%p\n", doSelected);
3796 if (!doSelected) return 0;
3798 /* Get the pidls from IDataObject */
3799 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3801 LPIDA cida = GlobalLock(medium.u.hGlobal);
3802 retVal = cida->cidl;
3803 COMCTL32_ReleaseStgMedium(medium);
3804 return retVal;
3806 return 0;
3810 * TOOLS
3813 /***********************************************************************
3814 * GetName
3816 * Get the pidl's display name (relative to folder) and
3817 * put it in lpstrFileName.
3819 * Return NOERROR on success,
3820 * E_FAIL otherwise
3823 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3825 STRRET str;
3826 HRESULT hRes;
3828 TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3830 if(!lpsf)
3832 SHGetDesktopFolder(&lpsf);
3833 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3834 IShellFolder_Release(lpsf);
3835 return hRes;
3838 /* Get the display name of the pidl relative to the folder */
3839 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3841 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3843 return E_FAIL;
3846 /***********************************************************************
3847 * GetShellFolderFromPidl
3849 * pidlRel is the item pidl relative
3850 * Return the IShellFolder of the absolute pidl
3852 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3854 IShellFolder *psf = NULL,*psfParent;
3856 TRACE("%p\n", pidlAbs);
3858 if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3860 psf = psfParent;
3861 if(pidlAbs && pidlAbs->mkid.cb)
3863 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3865 IShellFolder_Release(psfParent);
3866 return psf;
3869 /* return the desktop */
3870 return psfParent;
3872 return NULL;
3875 /***********************************************************************
3876 * GetParentPidl
3878 * Return the LPITEMIDLIST to the parent of the pidl in the list
3880 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3882 LPITEMIDLIST pidlParent;
3884 TRACE("%p\n", pidl);
3886 pidlParent = COMDLG32_PIDL_ILClone(pidl);
3887 COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3889 return pidlParent;
3892 /***********************************************************************
3893 * GetPidlFromName
3895 * returns the pidl of the file name relative to folder
3896 * NULL if an error occurred
3898 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3900 LPITEMIDLIST pidl = NULL;
3901 ULONG ulEaten;
3903 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3905 if(!lpcstrFileName) return NULL;
3906 if(!*lpcstrFileName) return NULL;
3908 if(!lpsf)
3910 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3911 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3912 IShellFolder_Release(lpsf);
3915 else
3917 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3919 return pidl;
3924 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3926 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3927 HRESULT ret;
3929 TRACE("%p, %p\n", psf, pidl);
3931 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3933 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
3934 /* see documentation shell 4.1*/
3935 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3938 /***********************************************************************
3939 * BrowseSelectedFolder
3941 static BOOL BrowseSelectedFolder(HWND hwnd)
3943 BOOL bBrowseSelFolder = FALSE;
3944 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3946 TRACE("\n");
3948 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
3950 LPITEMIDLIST pidlSelection;
3952 /* get the file selected */
3953 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
3954 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
3956 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
3957 pidlSelection, SBSP_RELATIVE ) ) )
3959 WCHAR buf[64];
3960 LoadStringW( COMDLG32_hInstance, IDS_PATHNOTEXISTING, buf, sizeof(buf)/sizeof(WCHAR) );
3961 MessageBoxW( hwnd, buf, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
3963 bBrowseSelFolder = TRUE;
3964 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3965 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
3967 COMDLG32_SHFree( pidlSelection );
3970 return bBrowseSelFolder;
3974 * Memory allocation methods */
3975 static void *MemAlloc(UINT size)
3977 return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
3980 static void MemFree(void *mem)
3982 HeapFree(GetProcessHeap(),0,mem);
3985 static inline BOOL valid_struct_size( DWORD size )
3987 return (size == OPENFILENAME_SIZE_VERSION_400W) ||
3988 (size == sizeof( OPENFILENAMEW ));
3991 static inline BOOL is_win16_looks(DWORD flags)
3993 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
3994 !(flags & OFN_EXPLORER));
3997 /* ------------------ APIs ---------------------- */
3999 /***********************************************************************
4000 * GetOpenFileNameA (COMDLG32.@)
4002 * Creates a dialog box for the user to select a file to open.
4004 * RETURNS
4005 * TRUE on success: user enters a valid file
4006 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4009 BOOL WINAPI GetOpenFileNameA(OPENFILENAMEA *ofn)
4011 TRACE("flags %08x\n", ofn->Flags);
4013 if (!valid_struct_size( ofn->lStructSize ))
4015 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4016 return FALSE;
4019 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4020 if (ofn->Flags & OFN_FILEMUSTEXIST)
4021 ofn->Flags |= OFN_PATHMUSTEXIST;
4023 if (is_win16_looks(ofn->Flags))
4024 return GetFileName31A(ofn, OPEN_DIALOG);
4025 else
4027 FileOpenDlgInfos info;
4029 init_filedlg_infoA(ofn, &info);
4030 return GetFileDialog95(&info, OPEN_DIALOG);
4034 /***********************************************************************
4035 * GetOpenFileNameW (COMDLG32.@)
4037 * Creates a dialog box for the user to select a file to open.
4039 * RETURNS
4040 * TRUE on success: user enters a valid file
4041 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4044 BOOL WINAPI GetOpenFileNameW(OPENFILENAMEW *ofn)
4046 TRACE("flags %08x\n", ofn->Flags);
4048 if (!valid_struct_size( ofn->lStructSize ))
4050 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4051 return FALSE;
4054 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4055 if (ofn->Flags & OFN_FILEMUSTEXIST)
4056 ofn->Flags |= OFN_PATHMUSTEXIST;
4058 if (is_win16_looks(ofn->Flags))
4059 return GetFileName31W(ofn, OPEN_DIALOG);
4060 else
4062 FileOpenDlgInfos info;
4064 init_filedlg_infoW(ofn, &info);
4065 return GetFileDialog95(&info, OPEN_DIALOG);
4070 /***********************************************************************
4071 * GetSaveFileNameA (COMDLG32.@)
4073 * Creates a dialog box for the user to select a file to save.
4075 * RETURNS
4076 * TRUE on success: user enters a valid file
4077 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4080 BOOL WINAPI GetSaveFileNameA(OPENFILENAMEA *ofn)
4082 if (!valid_struct_size( ofn->lStructSize ))
4084 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4085 return FALSE;
4088 if (is_win16_looks(ofn->Flags))
4089 return GetFileName31A(ofn, SAVE_DIALOG);
4090 else
4092 FileOpenDlgInfos info;
4094 init_filedlg_infoA(ofn, &info);
4095 return GetFileDialog95(&info, SAVE_DIALOG);
4099 /***********************************************************************
4100 * GetSaveFileNameW (COMDLG32.@)
4102 * Creates a dialog box for the user to select a file to save.
4104 * RETURNS
4105 * TRUE on success: user enters a valid file
4106 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4109 BOOL WINAPI GetSaveFileNameW(
4110 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4112 if (!valid_struct_size( ofn->lStructSize ))
4114 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4115 return FALSE;
4118 if (is_win16_looks(ofn->Flags))
4119 return GetFileName31W(ofn, SAVE_DIALOG);
4120 else
4122 FileOpenDlgInfos info;
4124 init_filedlg_infoW(ofn, &info);
4125 return GetFileDialog95(&info, SAVE_DIALOG);
4129 /***********************************************************************
4130 * GetFileTitleA (COMDLG32.@)
4132 * See GetFileTitleW.
4134 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4136 int ret;
4137 UNICODE_STRING strWFile;
4138 LPWSTR lpWTitle;
4140 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4141 lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4142 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4143 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4144 RtlFreeUnicodeString( &strWFile );
4145 RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4146 return ret;
4150 /***********************************************************************
4151 * GetFileTitleW (COMDLG32.@)
4153 * Get the name of a file.
4155 * PARAMS
4156 * lpFile [I] name and location of file
4157 * lpTitle [O] returned file name
4158 * cbBuf [I] buffer size of lpTitle
4160 * RETURNS
4161 * Success: zero
4162 * Failure: negative number.
4164 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4166 int i, len;
4167 static const WCHAR brkpoint[] = {'*','[',']',0};
4168 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4170 if(lpFile == NULL || lpTitle == NULL)
4171 return -1;
4173 len = lstrlenW(lpFile);
4175 if (len == 0)
4176 return -1;
4178 if(strpbrkW(lpFile, brkpoint))
4179 return -1;
4181 len--;
4183 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4184 return -1;
4186 for(i = len; i >= 0; i--)
4188 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':')
4190 i++;
4191 break;
4195 if(i == -1)
4196 i++;
4198 TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4200 len = lstrlenW(lpFile+i)+1;
4201 if(cbBuf < len)
4202 return len;
4204 lstrcpyW(lpTitle, &lpFile[i]);
4205 return 0;