[COMDLG32] Sync with Wine 3.0. CORE-14225
[reactos.git] / dll / win32 / comdlg32 / filedlg.c
1 /*
2 * COMMDLG - File Open Dialogs Win95 look and feel
3 *
4 * Copyright 1999 Francois Boisvert
5 * Copyright 1999, 2000 Juergen Schmied
6 *
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.
11 *
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.
16 *
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
20 *
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).
28 *
29 * FIXME: any hook gets a OPENFILENAMEA structure
30 *
31 * FIXME: CDN_FILEOK is wrong implemented, other CDN_ messages likely too
32 *
33 * FIXME: old style hook messages are not implemented (except FILEOKSTRING)
34 *
35 * FIXME: algorithm for selecting the initial directory is too simple
36 *
37 * FIXME: add to recent docs
38 *
39 * FIXME: flags not implemented: OFN_DONTADDTORECENT,
40 * OFN_NODEREFERENCELINKS, OFN_NOREADONLYRETURN,
41 * OFN_NOTESTFILECREATE, OFN_USEMONIKERS
42 *
43 * FIXME: lCustData for lpfnHook (WM_INITDIALOG)
44 *
45 *
46 */
47
48 #include "cdlg.h"
49
50 #include <shellapi.h>
51
52 #define UNIMPLEMENTED_FLAGS \
53 (OFN_DONTADDTORECENT |\
54 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
55 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
56
57 #define IsHooked(fodInfos) \
58 ((fodInfos->ofnInfos->Flags & OFN_ENABLEHOOK) && fodInfos->ofnInfos->lpfnHook)
59 /***********************************************************************
60 * Data structure and global variables
61 */
62 typedef struct SFolder
63 {
64 int m_iImageIndex; /* Index of picture in image list */
65 HIMAGELIST hImgList;
66 int m_iIndent; /* Indentation index */
67 LPITEMIDLIST pidlItem; /* absolute pidl of the item */
68
69 } SFOLDER,*LPSFOLDER;
70
71 typedef struct tagLookInInfo
72 {
73 int iMaxIndentation;
74 UINT uSelectedItem;
75 } LookInInfos;
76
77
78 /***********************************************************************
79 * Defines and global variables
80 */
81
82 /* Draw item constant */
83 #define XTEXTOFFSET 3
84
85 /* AddItem flags*/
86 #define LISTEND -1
87
88 /* SearchItem methods */
89 #define SEARCH_PIDL 1
90 #define SEARCH_EXP 2
91 #define ITEM_NOTFOUND -1
92
93 /* Undefined windows message sent by CreateViewObject*/
94 #define WM_GETISHELLBROWSER WM_USER+7
95
96 /* NOTE
97 * Those macros exist in windowsx.h. However, you can't really use them since
98 * they rely on the UNICODE defines and can't be used inside Wine itself.
99 */
100
101 /* Combo box macros */
102 #define CBAddString(hwnd,str) \
103 SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)(str));
104
105 #define CBInsertString(hwnd,str,pos) \
106 SendMessageW(hwnd, CB_INSERTSTRING, (WPARAM)(pos), (LPARAM)(str));
107
108 #define CBDeleteString(hwnd,pos) \
109 SendMessageW(hwnd, CB_DELETESTRING, (WPARAM)(pos), 0);
110
111 #define CBSetItemDataPtr(hwnd,iItemId,dataPtr) \
112 SendMessageW(hwnd, CB_SETITEMDATA, (WPARAM)(iItemId), (LPARAM)(dataPtr));
113
114 #define CBGetItemDataPtr(hwnd,iItemId) \
115 SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
116
117 #define CBGetLBText(hwnd,iItemId,str) \
118 SendMessageW(hwnd, CB_GETLBTEXT, (WPARAM)(iItemId), (LPARAM)(str));
119
120 #define CBGetCurSel(hwnd) \
121 SendMessageW(hwnd, CB_GETCURSEL, 0, 0);
122
123 #define CBSetCurSel(hwnd,pos) \
124 SendMessageW(hwnd, CB_SETCURSEL, (WPARAM)(pos), 0);
125
126 #define CBGetCount(hwnd) \
127 SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
128 #define CBShowDropDown(hwnd,show) \
129 SendMessageW(hwnd, CB_SHOWDROPDOWN, (WPARAM)(show), 0);
130 #define CBSetItemHeight(hwnd,index,height) \
131 SendMessageW(hwnd, CB_SETITEMHEIGHT, (WPARAM)(index), (LPARAM)(height));
132
133 #define CBSetExtendedUI(hwnd,flag) \
134 SendMessageW(hwnd, CB_SETEXTENDEDUI, (WPARAM)(flag), 0)
135
136 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
137 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
138
139 static const WCHAR LastVisitedMRUW[] =
140 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
141 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
142 'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
143 'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
144 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
145
146 static const WCHAR filedlg_info_propnameW[] = {'F','i','l','e','O','p','e','n','D','l','g','I','n','f','o','s',0};
147
148 FileOpenDlgInfos *get_filedlg_infoptr(HWND hwnd)
149 {
150 return GetPropW(hwnd, filedlg_info_propnameW);
151 }
152
153 /***********************************************************************
154 * Prototypes
155 */
156
157 /* Internal functions used by the dialog */
158 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
159 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
160 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
161 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
162 static BOOL FILEDLG95_OnOpen(HWND hwnd);
163 static LRESULT FILEDLG95_InitControls(HWND hwnd);
164 static void FILEDLG95_Clean(HWND hwnd);
165
166 /* Functions used by the shell navigation */
167 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd);
168 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd);
169 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb);
170 static void FILEDLG95_SHELL_Clean(HWND hwnd);
171 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd);
172
173 /* Functions used by the EDIT box */
174 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
175
176 /* Functions used by the filetype combo box */
177 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd);
178 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
179 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
180 static void FILEDLG95_FILETYPE_Clean(HWND hwnd);
181
182 /* Functions used by the Look In combo box */
183 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo);
184 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct);
185 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
186 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
187 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
188 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl);
189 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
190 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
191 static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
192
193 /* Functions for dealing with the most-recently-used registry keys */
194 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
195 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
196 static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
197
198 /* Miscellaneous tool functions */
199 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
200 IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
201 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl);
202 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
203 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
204 static UINT GetNumSelected( IDataObject *doSelected );
205 static void COMCTL32_ReleaseStgMedium(STGMEDIUM medium);
206
207 /* Shell memory allocation */
208 static void *MemAlloc(UINT size);
209 static void MemFree(void *mem);
210
211 static INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
212 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
213 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
214 static BOOL BrowseSelectedFolder(HWND hwnd);
215
216 /***********************************************************************
217 * GetFileName95
218 *
219 * Creates an Open common dialog box that lets the user select
220 * the drive, directory, and the name of a file or set of files to open.
221 *
222 * IN : The FileOpenDlgInfos structure associated with the dialog
223 * OUT : TRUE on success
224 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
225 */
226 static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
227 {
228 LRESULT lRes;
229 void *template;
230 HRSRC hRes;
231 HANDLE hDlgTmpl = 0;
232
233 /* test for missing functionality */
234 if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
235 {
236 FIXME("Flags 0x%08x not yet implemented\n",
237 fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
238 }
239
240 /* Create the dialog from a template */
241
242 if(!(hRes = FindResourceW(COMDLG32_hInstance,MAKEINTRESOURCEW(NEWFILEOPENORD),(LPCWSTR)RT_DIALOG)))
243 {
244 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
245 return FALSE;
246 }
247 if (!(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes )) ||
248 !(template = LockResource( hDlgTmpl )))
249 {
250 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
251 return FALSE;
252 }
253
254 /* msdn: explorer style dialogs permit sizing by default.
255 * The OFN_ENABLESIZING flag is only needed when a hook or
256 * custom template is provided */
257 if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
258 !(fodInfos->ofnInfos->Flags & ( OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
259 fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
260
261 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
262 {
263 fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
264 fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
265 }
266
267 /* old style hook messages */
268 if (IsHooked(fodInfos))
269 {
270 fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
271 fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
272 fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
273 fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
274 }
275
276 if (fodInfos->unicode)
277 lRes = DialogBoxIndirectParamW(COMDLG32_hInstance,
278 template,
279 fodInfos->ofnInfos->hwndOwner,
280 FileOpenDlgProc95,
281 (LPARAM) fodInfos);
282 else
283 lRes = DialogBoxIndirectParamA(COMDLG32_hInstance,
284 template,
285 fodInfos->ofnInfos->hwndOwner,
286 FileOpenDlgProc95,
287 (LPARAM) fodInfos);
288 if (fodInfos->ole_initialized)
289 OleUninitialize();
290
291 /* Unable to create the dialog */
292 if( lRes == -1)
293 return FALSE;
294
295 return lRes;
296 }
297
298 static WCHAR *heap_strdupAtoW(const char *str)
299 {
300 WCHAR *ret;
301 INT len;
302
303 if (!str)
304 return NULL;
305
306 len = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
307 ret = MemAlloc(len * sizeof(WCHAR));
308 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
309
310 return ret;
311 }
312
313 static void init_filedlg_infoW(OPENFILENAMEW *ofn, FileOpenDlgInfos *info)
314 {
315 INITCOMMONCONTROLSEX icc;
316
317 /* Initialize ComboBoxEx32 */
318 icc.dwSize = sizeof(icc);
319 icc.dwICC = ICC_USEREX_CLASSES;
320 InitCommonControlsEx(&icc);
321
322 /* Initialize CommDlgExtendedError() */
323 COMDLG32_SetCommDlgExtendedError(0);
324
325 memset(info, 0, sizeof(*info));
326
327 /* Pass in the original ofn */
328 info->ofnInfos = ofn;
329
330 info->title = ofn->lpstrTitle;
331 info->defext = ofn->lpstrDefExt;
332 info->filter = ofn->lpstrFilter;
333 info->customfilter = ofn->lpstrCustomFilter;
334
335 if (ofn->lpstrFile)
336 {
337 info->filename = MemAlloc(ofn->nMaxFile * sizeof(WCHAR));
338 lstrcpynW(info->filename, ofn->lpstrFile, ofn->nMaxFile);
339 }
340
341 if (ofn->lpstrInitialDir)
342 {
343 DWORD len = ExpandEnvironmentStringsW(ofn->lpstrInitialDir, NULL, 0);
344 if (len)
345 {
346 info->initdir = MemAlloc(len * sizeof(WCHAR));
347 ExpandEnvironmentStringsW(ofn->lpstrInitialDir, info->initdir, len);
348 }
349 }
350
351 info->unicode = TRUE;
352 }
353
354 static void init_filedlg_infoA(OPENFILENAMEA *ofn, FileOpenDlgInfos *info)
355 {
356 OPENFILENAMEW ofnW;
357 int len;
358
359 ofnW = *(OPENFILENAMEW *)ofn;
360
361 ofnW.lpstrInitialDir = heap_strdupAtoW(ofn->lpstrInitialDir);
362 ofnW.lpstrDefExt = heap_strdupAtoW(ofn->lpstrDefExt);
363 ofnW.lpstrTitle = heap_strdupAtoW(ofn->lpstrTitle);
364
365 if (ofn->lpstrFile)
366 {
367 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, NULL, 0);
368 ofnW.lpstrFile = MemAlloc(len * sizeof(WCHAR));
369 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFile, ofn->nMaxFile, ofnW.lpstrFile, len);
370 ofnW.nMaxFile = len;
371 }
372
373 if (ofn->lpstrFilter)
374 {
375 LPCSTR s;
376 int n;
377
378 /* filter is a list... title\0ext\0......\0\0 */
379 s = ofn->lpstrFilter;
380 while (*s) s = s+strlen(s)+1;
381 s++;
382 n = s - ofn->lpstrFilter;
383 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, NULL, 0);
384 ofnW.lpstrFilter = MemAlloc(len * sizeof(WCHAR));
385 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrFilter, n, (WCHAR *)ofnW.lpstrFilter, len);
386 }
387
388 /* convert lpstrCustomFilter */
389 if (ofn->lpstrCustomFilter)
390 {
391 int n, len;
392 LPCSTR s;
393
394 /* customfilter contains a pair of strings... title\0ext\0 */
395 s = ofn->lpstrCustomFilter;
396 if (*s) s = s+strlen(s)+1;
397 if (*s) s = s+strlen(s)+1;
398 n = s - ofn->lpstrCustomFilter;
399 len = MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, NULL, 0);
400 ofnW.lpstrCustomFilter = MemAlloc(len * sizeof(WCHAR));
401 MultiByteToWideChar(CP_ACP, 0, ofn->lpstrCustomFilter, n, ofnW.lpstrCustomFilter, len);
402 }
403
404 init_filedlg_infoW(&ofnW, info);
405
406 /* fixup A-specific fields */
407 info->ofnInfos = (OPENFILENAMEW *)ofn;
408 info->unicode = FALSE;
409
410 /* free what was duplicated */
411 MemFree((WCHAR *)ofnW.lpstrInitialDir);
412 MemFree((WCHAR *)ofnW.lpstrFile);
413 }
414
415 /***********************************************************************
416 * GetFileDialog95
417 *
418 * Call GetFileName95 with this structure and clean the memory.
419 */
420 static BOOL GetFileDialog95(FileOpenDlgInfos *info, UINT dlg_type)
421 {
422 WCHAR *current_dir = NULL;
423 BOOL ret;
424
425 /* save current directory */
426 if (info->ofnInfos->Flags & OFN_NOCHANGEDIR)
427 {
428 current_dir = MemAlloc(MAX_PATH * sizeof(WCHAR));
429 GetCurrentDirectoryW(MAX_PATH, current_dir);
430 }
431
432 switch (dlg_type)
433 {
434 case OPEN_DIALOG:
435 ret = GetFileName95(info);
436 break;
437 case SAVE_DIALOG:
438 info->DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
439 ret = GetFileName95(info);
440 break;
441 default:
442 ret = FALSE;
443 }
444
445 if (current_dir)
446 {
447 SetCurrentDirectoryW(current_dir);
448 MemFree(current_dir);
449 }
450
451 if (!info->unicode)
452 {
453 MemFree((WCHAR *)info->defext);
454 MemFree((WCHAR *)info->title);
455 MemFree((WCHAR *)info->filter);
456 MemFree((WCHAR *)info->customfilter);
457 }
458
459 MemFree(info->filename);
460 MemFree(info->initdir);
461 return ret;
462 }
463
464 /******************************************************************************
465 * COMDLG32_GetDisplayNameOf [internal]
466 *
467 * Helper function to get the display name for a pidl.
468 */
469 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
470 LPSHELLFOLDER psfDesktop;
471 STRRET strret;
472
473 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
474 return FALSE;
475
476 if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
477 IShellFolder_Release(psfDesktop);
478 return FALSE;
479 }
480
481 IShellFolder_Release(psfDesktop);
482 return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
483 }
484
485 /******************************************************************************
486 * COMDLG32_GetCanonicalPath [internal]
487 *
488 * Helper function to get the canonical path.
489 */
490 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
491 LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
492 {
493 WCHAR lpstrTemp[MAX_PATH];
494
495 /* Get the current directory name */
496 if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
497 {
498 /* last fallback */
499 GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
500 }
501 PathAddBackslashW(lpstrPathAndFile);
502
503 TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile));
504
505 /* if the user specified a fully qualified path use it */
506 if(PathIsRelativeW(lpstrFile))
507 {
508 lstrcatW(lpstrPathAndFile, lpstrFile);
509 }
510 else
511 {
512 /* does the path have a drive letter? */
513 if (PathGetDriveNumberW(lpstrFile) == -1)
514 lstrcpyW(lpstrPathAndFile+2, lpstrFile);
515 else
516 lstrcpyW(lpstrPathAndFile, lpstrFile);
517 }
518
519 /* resolve "." and ".." */
520 PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
521 lstrcpyW(lpstrPathAndFile, lpstrTemp);
522 TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
523 }
524
525 /***********************************************************************
526 * COMDLG32_SplitFileNames [internal]
527 *
528 * Creates a delimited list of filenames.
529 */
530 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
531 {
532 UINT nStrCharCount = 0; /* index in src buffer */
533 UINT nFileIndex = 0; /* index in dest buffer */
534 UINT nFileCount = 0; /* number of files */
535
536 /* we might get single filename without any '"',
537 * so we need nStrLen + terminating \0 + end-of-list \0 */
538 *lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
539 *sizeUsed = 0;
540
541 /* build delimited file list from filenames */
542 while ( nStrCharCount <= nStrLen )
543 {
544 if ( lpstrEdit[nStrCharCount]=='"' )
545 {
546 nStrCharCount++;
547 while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
548 {
549 (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
550 nStrCharCount++;
551 }
552 (*lpstrFileList)[nFileIndex++] = 0;
553 nFileCount++;
554 }
555 nStrCharCount++;
556 }
557
558 /* single, unquoted string */
559 if ((nStrLen > 0) && (nFileIndex == 0) )
560 {
561 lstrcpyW(*lpstrFileList, lpstrEdit);
562 nFileIndex = lstrlenW(lpstrEdit) + 1;
563 nFileCount = 1;
564 }
565
566 /* trailing \0 */
567 (*lpstrFileList)[nFileIndex++] = '\0';
568
569 *sizeUsed = nFileIndex;
570 return nFileCount;
571 }
572
573 /***********************************************************************
574 * ArrangeCtrlPositions [internal]
575 *
576 * NOTE: Make sure to add testcases for any changes made here.
577 */
578 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
579 {
580 HWND hwndChild, hwndStc32;
581 RECT rectParent, rectChild, rectStc32;
582 INT help_fixup = 0;
583 int chgx, chgy;
584
585 /* Take into account if open as read only checkbox and help button
586 * are hidden
587 */
588 if (hide_help)
589 {
590 RECT rectHelp, rectCancel;
591 GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
592 GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
593 /* subtract the height of the help button plus the space between
594 * the help button and the cancel button to the height of the dialog
595 */
596 help_fixup = rectHelp.bottom - rectCancel.bottom;
597 }
598
599 /*
600 There are two possibilities to add components to the default file dialog box.
601
602 By default, all the new components are added below the standard dialog box (the else case).
603
604 However, if there is a static text component with the stc32 id, a special case happens.
605 The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
606 in the window and the cx and cy indicate how to size the window.
607 Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
608 of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
609
610 */
611
612 GetClientRect(hwndParentDlg, &rectParent);
613
614 /* when arranging controls we have to use fixed parent size */
615 rectParent.bottom -= help_fixup;
616
617 hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
618 if (hwndStc32)
619 {
620 GetWindowRect(hwndStc32, &rectStc32);
621 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
622
623 /* set the size of the stc32 control according to the size of
624 * client area of the parent dialog
625 */
626 SetWindowPos(hwndStc32, 0,
627 0, 0,
628 rectParent.right, rectParent.bottom,
629 SWP_NOMOVE | SWP_NOZORDER);
630 }
631 else
632 SetRectEmpty(&rectStc32);
633
634 /* this part moves controls of the child dialog */
635 hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
636 while (hwndChild)
637 {
638 if (hwndChild != hwndStc32)
639 {
640 GetWindowRect(hwndChild, &rectChild);
641 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
642
643 /* move only if stc32 exist */
644 if (hwndStc32 && rectChild.left > rectStc32.right)
645 {
646 /* move to the right of visible controls of the parent dialog */
647 rectChild.left += rectParent.right;
648 rectChild.left -= rectStc32.right;
649 }
650 /* move even if stc32 doesn't exist */
651 if (rectChild.top >= rectStc32.bottom)
652 {
653 /* move below visible controls of the parent dialog */
654 rectChild.top += rectParent.bottom;
655 rectChild.top -= rectStc32.bottom - rectStc32.top;
656 }
657
658 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
659 0, 0, SWP_NOSIZE | SWP_NOZORDER);
660 }
661 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
662 }
663
664 /* this part moves controls of the parent dialog */
665 hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
666 while (hwndChild)
667 {
668 if (hwndChild != hwndChildDlg)
669 {
670 GetWindowRect(hwndChild, &rectChild);
671 MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
672
673 /* left,top of stc32 marks the position of controls
674 * from the parent dialog
675 */
676 rectChild.left += rectStc32.left;
677 rectChild.top += rectStc32.top;
678
679 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
680 0, 0, SWP_NOSIZE | SWP_NOZORDER);
681 }
682 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
683 }
684
685 /* calculate the size of the resulting dialog */
686
687 /* here we have to use original parent size */
688 GetClientRect(hwndParentDlg, &rectParent);
689 GetClientRect(hwndChildDlg, &rectChild);
690 TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
691 wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
692
693 if (hwndStc32)
694 {
695 /* width */
696 if (rectParent.right > rectStc32.right - rectStc32.left)
697 chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
698 else
699 chgx = rectChild.right - rectParent.right;
700 /* height */
701 if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
702 chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
703 else
704 /* Unconditionally set new dialog
705 * height to that of the child
706 */
707 chgy = rectChild.bottom - rectParent.bottom;
708 }
709 else
710 {
711 chgx = 0;
712 chgy = rectChild.bottom - help_fixup;
713 }
714 /* set the size of the parent dialog */
715 GetWindowRect(hwndParentDlg, &rectParent);
716 SetWindowPos(hwndParentDlg, 0,
717 0, 0,
718 rectParent.right - rectParent.left + chgx,
719 rectParent.bottom - rectParent.top + chgy,
720 SWP_NOMOVE | SWP_NOZORDER);
721 }
722
723 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
724 {
725 switch(uMsg) {
726 case WM_INITDIALOG:
727 return TRUE;
728 }
729 return FALSE;
730 }
731
732 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
733 {
734 LPCVOID template;
735 HRSRC hRes;
736 HANDLE hDlgTmpl = 0;
737 HWND hChildDlg = 0;
738
739 TRACE("%p, %p\n", fodInfos, hwnd);
740
741 /*
742 * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
743 * structure's hInstance parameter is not a HINSTANCE, but
744 * instead a pointer to a template resource to use.
745 */
746 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
747 {
748 HINSTANCE hinst;
749 if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
750 {
751 hinst = COMDLG32_hInstance;
752 if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
753 {
754 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
755 return NULL;
756 }
757 }
758 else
759 {
760 hinst = fodInfos->ofnInfos->hInstance;
761 if(fodInfos->unicode)
762 {
763 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
764 hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
765 }
766 else
767 {
768 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
769 hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
770 }
771 if (!hRes)
772 {
773 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
774 return NULL;
775 }
776 if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
777 !(template = LockResource( hDlgTmpl )))
778 {
779 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
780 return NULL;
781 }
782 }
783 if (fodInfos->unicode)
784 hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
785 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
786 (LPARAM)fodInfos->ofnInfos);
787 else
788 hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
789 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
790 (LPARAM)fodInfos->ofnInfos);
791 return hChildDlg;
792 }
793 else if( IsHooked(fodInfos))
794 {
795 RECT rectHwnd;
796 struct {
797 DLGTEMPLATE tmplate;
798 WORD menu,class,title;
799 } temp;
800 GetClientRect(hwnd,&rectHwnd);
801 temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
802 temp.tmplate.dwExtendedStyle = 0;
803 temp.tmplate.cdit = 0;
804 temp.tmplate.x = 0;
805 temp.tmplate.y = 0;
806 temp.tmplate.cx = 0;
807 temp.tmplate.cy = 0;
808 temp.menu = temp.class = temp.title = 0;
809
810 hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
811 hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
812
813 return hChildDlg;
814 }
815 return NULL;
816 }
817
818 /***********************************************************************
819 * SendCustomDlgNotificationMessage
820 *
821 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
822 */
823
824 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
825 {
826 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwndParentDlg);
827 LRESULT hook_result;
828 OFNOTIFYW ofnNotify;
829
830 TRACE("%p %d\n", hwndParentDlg, uCode);
831
832 if (!fodInfos || !fodInfos->DlgInfos.hwndCustomDlg)
833 return 0;
834
835 TRACE("CALL NOTIFY for %d\n", uCode);
836
837 ofnNotify.hdr.hwndFrom = hwndParentDlg;
838 ofnNotify.hdr.idFrom = 0;
839 ofnNotify.hdr.code = uCode;
840 ofnNotify.lpOFN = fodInfos->ofnInfos;
841 ofnNotify.pszFile = NULL;
842
843 if (fodInfos->unicode)
844 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
845 else
846 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
847
848 TRACE("RET NOTIFY retval %#lx\n", hook_result);
849
850 return hook_result;
851 }
852
853 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
854 {
855 UINT len, total;
856 WCHAR *p, *buffer;
857 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
858
859 TRACE("CDM_GETFILEPATH:\n");
860
861 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
862 return -1;
863
864 /* get path and filenames */
865 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
866 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
867 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
868 if (len)
869 {
870 p = buffer + strlenW(buffer);
871 *p++ = '\\';
872 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
873 }
874 if (fodInfos->unicode)
875 {
876 total = strlenW( buffer) + 1;
877 if (result) lstrcpynW( result, buffer, size );
878 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
879 }
880 else
881 {
882 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
883 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
884 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
885 }
886 HeapFree( GetProcessHeap(), 0, buffer );
887 return total;
888 }
889
890 /***********************************************************************
891 * FILEDLG95_HandleCustomDialogMessages
892 *
893 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
894 */
895 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
896 {
897 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
898 WCHAR lpstrPath[MAX_PATH];
899 INT_PTR retval;
900
901 if(!fodInfos) return FALSE;
902
903 switch(uMsg)
904 {
905 case CDM_GETFILEPATH:
906 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
907 break;
908
909 case CDM_GETFOLDERPATH:
910 TRACE("CDM_GETFOLDERPATH:\n");
911 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
912 if (lParam)
913 {
914 if (fodInfos->unicode)
915 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
916 else
917 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
918 (LPSTR)lParam, (int)wParam, NULL, NULL);
919 }
920 retval = lstrlenW(lpstrPath) + 1;
921 break;
922
923 case CDM_GETFOLDERIDLIST:
924 retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
925 if (retval <= wParam)
926 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
927 break;
928
929 case CDM_GETSPEC:
930 TRACE("CDM_GETSPEC:\n");
931 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
932 if (lParam)
933 {
934 if (fodInfos->unicode)
935 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
936 else
937 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
938 }
939 break;
940
941 case CDM_SETCONTROLTEXT:
942 TRACE("CDM_SETCONTROLTEXT:\n");
943 if ( lParam )
944 {
945 if( fodInfos->unicode )
946 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
947 else
948 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
949 }
950 retval = TRUE;
951 break;
952
953 case CDM_HIDECONTROL:
954 /* MSDN states that it should fail for not OFN_EXPLORER case */
955 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
956 {
957 HWND control = GetDlgItem( hwnd, wParam );
958 if (control) ShowWindow( control, SW_HIDE );
959 retval = TRUE;
960 }
961 else retval = FALSE;
962 break;
963
964 default:
965 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
966 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
967 return FALSE;
968 }
969 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
970 return TRUE;
971 }
972
973 /***********************************************************************
974 * FILEDLG95_OnWMGetMMI
975 *
976 * WM_GETMINMAXINFO message handler for resizable dialogs
977 */
978 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
979 {
980 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
981 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
982 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
983 {
984 mmiptr->ptMinTrackSize = fodInfos->initial_size;
985 }
986 return TRUE;
987 }
988
989 /***********************************************************************
990 * FILEDLG95_OnWMSize
991 *
992 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
993 *
994 * FIXME: this could be made more elaborate. Now use a simple scheme
995 * where the file view is enlarged and the controls are either moved
996 * vertically or horizontally to get out of the way. Only the "grip"
997 * is moved in both directions to stay in the corner.
998 */
999 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1000 {
1001 RECT rc, rcview;
1002 int chgx, chgy;
1003 HWND ctrl;
1004 HDWP hdwp;
1005 FileOpenDlgInfos *fodInfos;
1006
1007 if( wParam != SIZE_RESTORED) return FALSE;
1008 fodInfos = get_filedlg_infoptr(hwnd);
1009 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1010 /* get the new dialog rectangle */
1011 GetWindowRect( hwnd, &rc);
1012 TRACE("%p, size from %d,%d to %d,%d\n", hwnd, fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1013 rc.right -rc.left, rc.bottom -rc.top);
1014 /* not initialized yet */
1015 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1016 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1017 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1018 return FALSE;
1019 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1020 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1021 fodInfos->sizedlg.cx = rc.right - rc.left;
1022 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1023 /* change the size of the view window */
1024 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1025 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1026 hdwp = BeginDeferWindowPos( 10);
1027 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1028 rcview.right - rcview.left + chgx,
1029 rcview.bottom - rcview.top + chgy,
1030 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1031 /* change position and sizes of the controls */
1032 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1033 {
1034 int ctrlid = GetDlgCtrlID( ctrl);
1035 GetWindowRect( ctrl, &rc);
1036 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1037 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1038 {
1039 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1040 0, 0,
1041 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1042 }
1043 else if( rc.top > rcview.bottom)
1044 {
1045 /* if it was below the shell view
1046 * move to bottom */
1047 switch( ctrlid)
1048 {
1049 /* file name (edit or comboboxex) and file types combo change also width */
1050 case edt1:
1051 case cmb13:
1052 case cmb1:
1053 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1054 rc.right - rc.left + chgx, rc.bottom - rc.top,
1055 SWP_NOACTIVATE | SWP_NOZORDER);
1056 break;
1057 /* then these buttons must move out of the way */
1058 case IDOK:
1059 case IDCANCEL:
1060 case pshHelp:
1061 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1062 0, 0,
1063 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1064 break;
1065 default:
1066 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1067 0, 0,
1068 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1069 }
1070 }
1071 else if( rc.left > rcview.right)
1072 {
1073 /* if it was to the right of the shell view
1074 * move to right */
1075 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1076 0, 0,
1077 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1078 }
1079 else
1080 /* special cases */
1081 {
1082 switch( ctrlid)
1083 {
1084 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1085 case IDC_LOOKIN:
1086 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1087 rc.right - rc.left + chgx, rc.bottom - rc.top,
1088 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1089 break;
1090 case IDC_TOOLBARSTATIC:
1091 case IDC_TOOLBAR:
1092 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1093 0, 0,
1094 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1095 break;
1096 #endif
1097 /* not resized in windows. Since wine uses this invisible control
1098 * to size the browser view it needs to be resized */
1099 case IDC_SHELLSTATIC:
1100 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1101 rc.right - rc.left + chgx,
1102 rc.bottom - rc.top + chgy,
1103 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1104 break;
1105 }
1106 }
1107 }
1108 if(fodInfos->DlgInfos.hwndCustomDlg &&
1109 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1110 {
1111 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1112 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1113 {
1114 GetWindowRect( ctrl, &rc);
1115 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1116 if( rc.top > rcview.bottom)
1117 {
1118 /* if it was below the shell view
1119 * move to bottom */
1120 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1121 rc.right - rc.left, rc.bottom - rc.top,
1122 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1123 }
1124 else if( rc.left > rcview.right)
1125 {
1126 /* if it was to the right of the shell view
1127 * move to right */
1128 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1129 rc.right - rc.left, rc.bottom - rc.top,
1130 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1131 }
1132 }
1133 /* size the custom dialog at the end: some applications do some
1134 * control re-arranging at this point */
1135 GetClientRect(hwnd, &rc);
1136 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1137 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1138 }
1139 EndDeferWindowPos( hdwp);
1140 /* should not be needed */
1141 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1142 return TRUE;
1143 }
1144
1145 /***********************************************************************
1146 * FileOpenDlgProc95
1147 *
1148 * File open dialog procedure
1149 */
1150 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1151 {
1152 #if 0
1153 TRACE("%p 0x%04x\n", hwnd, uMsg);
1154 #endif
1155
1156 switch(uMsg)
1157 {
1158 case WM_INITDIALOG:
1159 {
1160 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1161 RECT rc, rcstc;
1162 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1163 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1164
1165 /* Some shell namespace extensions depend on COM being initialized. */
1166 if (SUCCEEDED(OleInitialize(NULL)))
1167 fodInfos->ole_initialized = TRUE;
1168
1169 SetPropW(hwnd, filedlg_info_propnameW, fodInfos);
1170
1171 FILEDLG95_InitControls(hwnd);
1172
1173 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1174 {
1175 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1176 DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1177 RECT client, client_adjusted;
1178
1179 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1180 {
1181 style |= WS_SIZEBOX;
1182 ex_style |= WS_EX_WINDOWEDGE;
1183 }
1184 else
1185 style &= ~WS_SIZEBOX;
1186 SetWindowLongW(hwnd, GWL_STYLE, style);
1187 SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1188
1189 GetClientRect( hwnd, &client );
1190 GetClientRect( hwnd, &client_adjusted );
1191 AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1192
1193 GetWindowRect( hwnd, &rc );
1194 rc.right += client_adjusted.right - client.right;
1195 rc.bottom += client_adjusted.bottom - client.bottom;
1196 SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1197 SWP_NOZORDER | SWP_NOMOVE);
1198
1199 GetWindowRect( hwnd, &rc );
1200 fodInfos->DlgInfos.hwndGrip =
1201 CreateWindowExA( 0, "SCROLLBAR", NULL,
1202 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1203 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1204 rc.right - gripx, rc.bottom - gripy,
1205 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1206 }
1207
1208 fodInfos->DlgInfos.hwndCustomDlg =
1209 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1210
1211 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1212 FILEDLG95_FillControls(hwnd, wParam, lParam);
1213
1214 if( fodInfos->DlgInfos.hwndCustomDlg)
1215 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1216
1217 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1218 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1219 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1220 }
1221
1222 /* if the app has changed the position of the invisible listbox,
1223 * change that of the listview (browser) as well */
1224 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1225 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1226 if( !EqualRect( &rc, &rcstc))
1227 {
1228 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1229 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1230 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1231 SWP_NOACTIVATE | SWP_NOZORDER);
1232 }
1233
1234 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1235 {
1236 GetWindowRect( hwnd, &rc);
1237 fodInfos->sizedlg.cx = rc.right - rc.left;
1238 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1239 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1240 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1241 GetClientRect( hwnd, &rc);
1242 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1243 rc.right - gripx, rc.bottom - gripy,
1244 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1245 /* resize the dialog to the previous invocation */
1246 if( MemDialogSize.cx && MemDialogSize.cy)
1247 SetWindowPos( hwnd, NULL,
1248 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1249 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1250 }
1251
1252 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1253 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1254
1255 return 0;
1256 }
1257 case WM_SIZE:
1258 return FILEDLG95_OnWMSize(hwnd, wParam);
1259 case WM_GETMINMAXINFO:
1260 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1261 case WM_COMMAND:
1262 return FILEDLG95_OnWMCommand(hwnd, wParam);
1263 case WM_DRAWITEM:
1264 {
1265 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1266 {
1267 case IDC_LOOKIN:
1268 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1269 return TRUE;
1270 }
1271 }
1272 return FALSE;
1273
1274 case WM_GETISHELLBROWSER:
1275 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1276
1277 case WM_DESTROY:
1278 {
1279 FileOpenDlgInfos * fodInfos = get_filedlg_infoptr(hwnd);
1280 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1281 MemDialogSize = fodInfos->sizedlg;
1282 RemovePropW(hwnd, filedlg_info_propnameW);
1283 return FALSE;
1284 }
1285 case WM_NOTIFY:
1286 {
1287 LPNMHDR lpnmh = (LPNMHDR)lParam;
1288 UINT stringId = -1;
1289
1290 /* set up the button tooltips strings */
1291 if(TTN_GETDISPINFOA == lpnmh->code )
1292 {
1293 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1294 switch(lpnmh->idFrom )
1295 {
1296 /* Up folder button */
1297 case FCIDM_TB_UPFOLDER:
1298 stringId = IDS_UPFOLDER;
1299 break;
1300 /* New folder button */
1301 case FCIDM_TB_NEWFOLDER:
1302 stringId = IDS_NEWFOLDER;
1303 break;
1304 /* List option button */
1305 case FCIDM_TB_SMALLICON:
1306 stringId = IDS_LISTVIEW;
1307 break;
1308 /* Details option button */
1309 case FCIDM_TB_REPORTVIEW:
1310 stringId = IDS_REPORTVIEW;
1311 break;
1312 /* Desktop button */
1313 case FCIDM_TB_DESKTOP:
1314 stringId = IDS_TODESKTOP;
1315 break;
1316 default:
1317 stringId = 0;
1318 }
1319 lpdi->hinst = COMDLG32_hInstance;
1320 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1321 }
1322 return FALSE;
1323 }
1324 default :
1325 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1326 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1327 return FALSE;
1328 }
1329 }
1330
1331 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1332 {
1333 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1334 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1335 }
1336
1337 /***********************************************************************
1338 * FILEDLG95_InitControls
1339 *
1340 * WM_INITDIALOG message handler (before hook notification)
1341 */
1342 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1343 {
1344 BOOL win2000plus = FALSE;
1345 BOOL win98plus = FALSE;
1346 BOOL handledPath = FALSE;
1347 OSVERSIONINFOW osVi;
1348 static const WCHAR szwSlash[] = { '\\', 0 };
1349 static const WCHAR szwStar[] = { '*',0 };
1350
1351 static const TBBUTTON tbb[] =
1352 {
1353 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1354 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1355 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1356 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1357 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1358 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1359 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1360 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1361 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1362 };
1363 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1364
1365 RECT rectTB;
1366 RECT rectlook;
1367
1368 HIMAGELIST toolbarImageList;
1369 SHFILEINFOA shFileInfo;
1370 ITEMIDLIST *desktopPidl;
1371
1372 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1373
1374 TRACE("%p\n", fodInfos);
1375
1376 /* Get windows version emulating */
1377 osVi.dwOSVersionInfoSize = sizeof(osVi);
1378 GetVersionExW(&osVi);
1379 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1380 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1381 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1382 win2000plus = (osVi.dwMajorVersion > 4);
1383 if (win2000plus) win98plus = TRUE;
1384 }
1385 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1386
1387
1388 /* Use either the edit or the comboboxex for the filename control */
1389 if (filename_is_edit( fodInfos ))
1390 {
1391 DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1392 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1393 }
1394 else
1395 {
1396 DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1397 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1398 }
1399
1400 /* Get the hwnd of the controls */
1401 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1402 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1403
1404 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1405 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1406
1407 /* construct the toolbar */
1408 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1409 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1410
1411 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1412 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1413 rectTB.left = rectlook.right;
1414 rectTB.top = rectlook.top-1;
1415
1416 if (fodInfos->unicode)
1417 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1418 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1419 rectTB.left, rectTB.top,
1420 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1421 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1422 else
1423 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1424 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1425 rectTB.left, rectTB.top,
1426 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1427 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1428
1429 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1430
1431 /* FIXME: use TB_LOADIMAGES when implemented */
1432 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1433 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1434 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1435
1436 /* Retrieve and add desktop icon to the toolbar */
1437 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1438 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1439 SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1440 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1441 ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1442
1443 DestroyIcon(shFileInfo.hIcon);
1444 CoTaskMemFree(desktopPidl);
1445
1446 /* Finish Toolbar Construction */
1447 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1448 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1449
1450 /* Set the window text with the text specified in the OPENFILENAME structure */
1451 if(fodInfos->title)
1452 {
1453 SetWindowTextW(hwnd,fodInfos->title);
1454 }
1455 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1456 {
1457 WCHAR buf[64];
1458 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1459 SetWindowTextW(hwnd, buf);
1460 }
1461
1462 /* Initialise the file name edit control */
1463 handledPath = FALSE;
1464 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1465
1466 if(fodInfos->filename)
1467 {
1468 /* 1. If win2000 or higher and filename contains a path, use it
1469 in preference over the lpstrInitialDir */
1470 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1471 WCHAR tmpBuf[MAX_PATH];
1472 WCHAR *nameBit;
1473 DWORD result;
1474
1475 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1476 if (result) {
1477
1478 /* nameBit is always shorter than the original filename. It may be NULL
1479 * when the filename contains only a drive name instead of file name */
1480 if (nameBit)
1481 {
1482 lstrcpyW(fodInfos->filename,nameBit);
1483 *nameBit = 0x00;
1484 }
1485 else
1486 *fodInfos->filename = '\0';
1487
1488 MemFree(fodInfos->initdir);
1489 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1490 lstrcpyW(fodInfos->initdir, tmpBuf);
1491 handledPath = TRUE;
1492 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1493 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1494 }
1495 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1496
1497 } else {
1498 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1499 }
1500 }
1501
1502 /* 2. (All platforms) If initdir is not null, then use it */
1503 if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1504 {
1505 /* Work out the proper path as supplied one might be relative */
1506 /* (Here because supplying '.' as dir browses to My Computer) */
1507 WCHAR tmpBuf[MAX_PATH];
1508 WCHAR tmpBuf2[MAX_PATH];
1509 WCHAR *nameBit;
1510 DWORD result;
1511
1512 lstrcpyW(tmpBuf, fodInfos->initdir);
1513 if (PathFileExistsW(tmpBuf)) {
1514 /* initdir does not have to be a directory. If a file is
1515 * specified, the dir part is taken */
1516 if (PathIsDirectoryW(tmpBuf)) {
1517 PathAddBackslashW(tmpBuf);
1518 lstrcatW(tmpBuf, szwStar);
1519 }
1520 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1521 if (result) {
1522 *nameBit = 0x00;
1523 MemFree(fodInfos->initdir);
1524 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1525 lstrcpyW(fodInfos->initdir, tmpBuf2);
1526 handledPath = TRUE;
1527 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1528 }
1529 }
1530 else if (fodInfos->initdir)
1531 {
1532 MemFree(fodInfos->initdir);
1533 fodInfos->initdir = NULL;
1534 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1535 }
1536 }
1537
1538 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1539 {
1540 /* 3. All except w2k+: if filename contains a path use it */
1541 if (!win2000plus && fodInfos->filename &&
1542 *fodInfos->filename &&
1543 strpbrkW(fodInfos->filename, szwSlash)) {
1544 WCHAR tmpBuf[MAX_PATH];
1545 WCHAR *nameBit;
1546 DWORD result;
1547
1548 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1549 tmpBuf, &nameBit);
1550 if (result) {
1551 int len;
1552
1553 /* nameBit is always shorter than the original filename */
1554 lstrcpyW(fodInfos->filename, nameBit);
1555 *nameBit = 0x00;
1556
1557 len = lstrlenW(tmpBuf);
1558 MemFree(fodInfos->initdir);
1559 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1560 lstrcpyW(fodInfos->initdir, tmpBuf);
1561
1562 handledPath = TRUE;
1563 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1564 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1565 }
1566 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1567 }
1568
1569 /* 4. Win2000+: Recently used */
1570 if (!handledPath && win2000plus) {
1571 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1572 fodInfos->initdir[0] = '\0';
1573
1574 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1575
1576 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1577 handledPath = TRUE;
1578 }else{
1579 MemFree(fodInfos->initdir);
1580 fodInfos->initdir = NULL;
1581 }
1582 }
1583
1584 /* 5. win98+ and win2000+ if any files of specified filter types in
1585 current directory, use it */
1586 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1587
1588 LPCWSTR lpstrPos = fodInfos->filter;
1589 WIN32_FIND_DATAW FindFileData;
1590 HANDLE hFind;
1591
1592 while (1)
1593 {
1594 /* filter is a list... title\0ext\0......\0\0 */
1595
1596 /* Skip the title */
1597 if(! *lpstrPos) break; /* end */
1598 lpstrPos += lstrlenW(lpstrPos) + 1;
1599
1600 /* See if any files exist in the current dir with this extension */
1601 if(! *lpstrPos) break; /* end */
1602
1603 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1604
1605 if (hFind == INVALID_HANDLE_VALUE) {
1606 /* None found - continue search */
1607 lpstrPos += lstrlenW(lpstrPos) + 1;
1608
1609 } else {
1610
1611 MemFree(fodInfos->initdir);
1612 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1613 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1614
1615 handledPath = TRUE;
1616 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1617 debugstr_w(lpstrPos));
1618 FindClose(hFind);
1619 break;
1620 }
1621 }
1622 }
1623
1624 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1625 if (!handledPath && (win2000plus || win98plus)) {
1626 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1627
1628 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1629 {
1630 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1631 {
1632 /* last fallback */
1633 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1634 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1635 } else {
1636 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1637 }
1638 } else {
1639 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1640 }
1641 handledPath = TRUE;
1642 } else if (!handledPath) {
1643 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1644 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1645 handledPath = TRUE;
1646 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1647 }
1648 }
1649 SetFocus( fodInfos->DlgInfos.hwndFileName );
1650 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1651
1652 /* Must the open as read only check box be checked ?*/
1653 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1654 {
1655 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1656 }
1657
1658 /* Must the open as read only check box be hidden? */
1659 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1660 {
1661 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1662 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1663 }
1664
1665 /* Must the help button be hidden? */
1666 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1667 {
1668 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1669 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1670 }
1671
1672 /* change Open to Save */
1673 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1674 {
1675 WCHAR buf[16];
1676 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1677 SetDlgItemTextW(hwnd, IDOK, buf);
1678 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1679 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1680 }
1681
1682 /* Initialize the filter combo box */
1683 FILEDLG95_FILETYPE_Init(hwnd);
1684
1685 return 0;
1686 }
1687
1688 /***********************************************************************
1689 * FILEDLG95_ResizeControls
1690 *
1691 * WM_INITDIALOG message handler (after hook notification)
1692 */
1693 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1694 {
1695 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1696
1697 if (fodInfos->DlgInfos.hwndCustomDlg)
1698 {
1699 RECT rc;
1700 UINT flags = SWP_NOACTIVATE;
1701
1702 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1703 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1704
1705 /* resize the custom dialog to the parent size */
1706 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1707 GetClientRect(hwnd, &rc);
1708 else
1709 {
1710 /* our own fake template is zero sized and doesn't have children, so
1711 * there is no need to resize it. Picasa depends on it.
1712 */
1713 flags |= SWP_NOSIZE;
1714 SetRectEmpty(&rc);
1715 }
1716 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1717 0, 0, rc.right, rc.bottom, flags);
1718 }
1719 else
1720 {
1721 /* Resize the height; if opened as read-only, checkbox and help button are
1722 * hidden and we are not using a custom template nor a customDialog
1723 */
1724 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1725 (!(fodInfos->ofnInfos->Flags &
1726 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1727 {
1728 RECT rectDlg, rectHelp, rectCancel;
1729 GetWindowRect(hwnd, &rectDlg);
1730 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1731 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1732 /* subtract the height of the help button plus the space between the help
1733 * button and the cancel button to the height of the dialog
1734 */
1735 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1736 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1737 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1738 }
1739 }
1740 return TRUE;
1741 }
1742
1743 /***********************************************************************
1744 * FILEDLG95_FillControls
1745 *
1746 * WM_INITDIALOG message handler (after hook notification)
1747 */
1748 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1749 {
1750 LPITEMIDLIST pidlItemId = NULL;
1751
1752 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1753
1754 TRACE("dir=%s file=%s\n",
1755 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1756
1757 /* Get the initial directory pidl */
1758
1759 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1760 {
1761 WCHAR path[MAX_PATH];
1762
1763 GetCurrentDirectoryW(MAX_PATH,path);
1764 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1765 }
1766
1767 /* Initialise shell objects */
1768 FILEDLG95_SHELL_Init(hwnd);
1769
1770 /* Initialize the Look In combo box */
1771 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1772
1773 /* Browse to the initial directory */
1774 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1775
1776 /* Free pidlItem memory */
1777 COMDLG32_SHFree(pidlItemId);
1778
1779 return TRUE;
1780 }
1781 /***********************************************************************
1782 * FILEDLG95_Clean
1783 *
1784 * Regroups all the cleaning functions of the filedlg
1785 */
1786 void FILEDLG95_Clean(HWND hwnd)
1787 {
1788 FILEDLG95_FILETYPE_Clean(hwnd);
1789 FILEDLG95_LOOKIN_Clean(hwnd);
1790 FILEDLG95_SHELL_Clean(hwnd);
1791 }
1792 /***********************************************************************
1793 * FILEDLG95_OnWMCommand
1794 *
1795 * WM_COMMAND message handler
1796 */
1797 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1798 {
1799 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1800 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1801 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1802
1803 switch(wID)
1804 {
1805 /* OK button */
1806 case IDOK:
1807 FILEDLG95_OnOpen(hwnd);
1808 break;
1809 /* Cancel button */
1810 case IDCANCEL:
1811 FILEDLG95_Clean(hwnd);
1812 EndDialog(hwnd, FALSE);
1813 break;
1814 /* Filetype combo box */
1815 case IDC_FILETYPE:
1816 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1817 break;
1818 /* LookIn combo box */
1819 case IDC_LOOKIN:
1820 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1821 break;
1822
1823 /* --- toolbar --- */
1824 /* Up folder button */
1825 case FCIDM_TB_UPFOLDER:
1826 FILEDLG95_SHELL_UpFolder(hwnd);
1827 break;
1828 /* New folder button */
1829 case FCIDM_TB_NEWFOLDER:
1830 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1831 break;
1832 /* List option button */
1833 case FCIDM_TB_SMALLICON:
1834 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1835 break;
1836 /* Details option button */
1837 case FCIDM_TB_REPORTVIEW:
1838 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1839 break;
1840 /* Details option button */
1841 case FCIDM_TB_DESKTOP:
1842 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1843 break;
1844
1845 case edt1:
1846 case cmb13:
1847 break;
1848
1849 }
1850 /* Do not use the listview selection anymore */
1851 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1852 return 0;
1853 }
1854
1855 /***********************************************************************
1856 * FILEDLG95_OnWMGetIShellBrowser
1857 *
1858 * WM_GETISHELLBROWSER message handler
1859 */
1860 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1861 {
1862 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1863
1864 TRACE("\n");
1865
1866 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1867
1868 return TRUE;
1869 }
1870
1871
1872 /***********************************************************************
1873 * FILEDLG95_SendFileOK
1874 *
1875 * Sends the CDN_FILEOK notification if required
1876 *
1877 * RETURNS
1878 * TRUE if the dialog should close
1879 * FALSE if the dialog should not be closed
1880 */
1881 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1882 {
1883 /* ask the hook if we can close */
1884 if(IsHooked(fodInfos))
1885 {
1886 LRESULT retval = 0;
1887
1888 TRACE("---\n");
1889 /* First send CDN_FILEOK as MSDN doc says */
1890 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1891 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1892 if( retval)
1893 {
1894 TRACE("canceled\n");
1895 return FALSE;
1896 }
1897
1898 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1899 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1900 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1901 if( retval)
1902 {
1903 TRACE("canceled\n");
1904 return FALSE;
1905 }
1906 }
1907 return TRUE;
1908 }
1909
1910 /***********************************************************************
1911 * FILEDLG95_OnOpenMultipleFiles
1912 *
1913 * Handles the opening of multiple files.
1914 *
1915 * FIXME
1916 * check destination buffer size
1917 */
1918 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
1919 {
1920 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1921 WCHAR lpstrPathSpec[MAX_PATH] = {0};
1922 UINT nCount, nSizePath;
1923
1924 TRACE("\n");
1925
1926 if(fodInfos->unicode)
1927 {
1928 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1929 ofn->lpstrFile[0] = '\0';
1930 }
1931 else
1932 {
1933 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
1934 ofn->lpstrFile[0] = '\0';
1935 }
1936
1937 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
1938
1939 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
1940 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
1941 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
1942 {
1943 LPWSTR lpstrTemp = lpstrFileList;
1944
1945 for ( nCount = 0; nCount < nFileCount; nCount++ )
1946 {
1947 LPITEMIDLIST pidl;
1948
1949 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
1950 if (!pidl)
1951 {
1952 WCHAR lpstrNotFound[100];
1953 WCHAR lpstrMsg[100];
1954 WCHAR tmp[400];
1955 static const WCHAR nl[] = {'\n',0};
1956
1957 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
1958 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
1959
1960 lstrcpyW(tmp, lpstrTemp);
1961 lstrcatW(tmp, nl);
1962 lstrcatW(tmp, lpstrNotFound);
1963 lstrcatW(tmp, nl);
1964 lstrcatW(tmp, lpstrMsg);
1965
1966 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
1967 return FALSE;
1968 }
1969
1970 /* move to the next file in the list of files */
1971 lpstrTemp += lstrlenW(lpstrTemp) + 1;
1972 COMDLG32_SHFree(pidl);
1973 }
1974 }
1975
1976 nSizePath = lstrlenW(lpstrPathSpec) + 1;
1977 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
1978 {
1979 /* For "oldstyle" dialog the components have to
1980 be separated by blanks (not '\0'!) and short
1981 filenames have to be used! */
1982 FIXME("Components have to be separated by blanks\n");
1983 }
1984 if(fodInfos->unicode)
1985 {
1986 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1987 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
1988 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
1989 }
1990 else
1991 {
1992 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
1993
1994 if (ofn->lpstrFile != NULL)
1995 {
1996 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
1997 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
1998 if (ofn->nMaxFile > nSizePath)
1999 {
2000 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2001 ofn->lpstrFile + nSizePath,
2002 ofn->nMaxFile - nSizePath, NULL, NULL);
2003 }
2004 }
2005 }
2006
2007 fodInfos->ofnInfos->nFileOffset = nSizePath;
2008 fodInfos->ofnInfos->nFileExtension = 0;
2009
2010 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2011 return FALSE;
2012
2013 /* clean and exit */
2014 FILEDLG95_Clean(hwnd);
2015 return EndDialog(hwnd,TRUE);
2016 }
2017
2018 /* Returns the 'slot name' of the given module_name in the registry's
2019 * most-recently-used list. This will be an ASCII value in the
2020 * range ['a','z'). Returns zero on error.
2021 *
2022 * The slot's value in the registry has the form:
2023 * module_name\0mru_path\0
2024 *
2025 * If stored_path is given, then stored_path will contain the path name
2026 * stored in the registry's MRU list for the given module_name.
2027 *
2028 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2029 * MRU list key for the given module_name.
2030 */
2031 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2032 {
2033 WCHAR mru_list[32], *cur_mru_slot;
2034 BOOL taken[25] = {0};
2035 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2036 HKEY hkey_tmp, *hkey;
2037 LONG ret;
2038
2039 if(hkey_ret)
2040 hkey = hkey_ret;
2041 else
2042 hkey = &hkey_tmp;
2043
2044 if(stored_path)
2045 *stored_path = '\0';
2046
2047 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2048 if(ret){
2049 WARN("Unable to create MRU key: %d\n", ret);
2050 return 0;
2051 }
2052
2053 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2054 (LPBYTE)mru_list, &mru_list_size);
2055 if(ret || key_type != REG_SZ){
2056 if(ret == ERROR_FILE_NOT_FOUND)
2057 return 'a';
2058
2059 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2060 RegCloseKey(*hkey);
2061 return 0;
2062 }
2063
2064 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2065 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2066 DWORD value_data_size = sizeof(value_data);
2067
2068 *value_name = *cur_mru_slot;
2069
2070 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2071 &key_type, (LPBYTE)value_data, &value_data_size);
2072 if(ret || key_type != REG_BINARY){
2073 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2074 continue;
2075 }
2076
2077 if(!strcmpiW(module_name, value_data)){
2078 if(!hkey_ret)
2079 RegCloseKey(*hkey);
2080 if(stored_path)
2081 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2082 return *value_name;
2083 }
2084 }
2085
2086 if(!hkey_ret)
2087 RegCloseKey(*hkey);
2088
2089 /* the module name isn't in the registry, so find the next open slot */
2090 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2091 taken[*cur_mru_slot - 'a'] = TRUE;
2092 for(i = 0; i < 25; ++i){
2093 if(!taken[i])
2094 return i + 'a';
2095 }
2096
2097 /* all slots are taken, so return the last one in MRUList */
2098 --cur_mru_slot;
2099 return *cur_mru_slot;
2100 }
2101
2102 /* save the given filename as most-recently-used path for this module */
2103 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2104 {
2105 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2106 LONG ret;
2107 HKEY hkey;
2108
2109 /* get the current executable's name */
2110 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2111 WARN("GotModuleFileName failed: %d\n", GetLastError());
2112 return;
2113 }
2114 module_name = strrchrW(module_path, '\\');
2115 if(!module_name)
2116 module_name = module_path;
2117 else
2118 module_name += 1;
2119
2120 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2121 if(!slot)
2122 return;
2123 *slot_name = slot;
2124
2125 { /* update the slot's info */
2126 WCHAR *path_ends, *final;
2127 DWORD path_len, final_len;
2128
2129 /* use only the path segment of `filename' */
2130 path_ends = strrchrW(filename, '\\');
2131 path_len = path_ends - filename;
2132
2133 final_len = path_len + lstrlenW(module_name) + 2;
2134
2135 final = MemAlloc(final_len * sizeof(WCHAR));
2136 if(!final)
2137 return;
2138 lstrcpyW(final, module_name);
2139 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2140 final[final_len-1] = '\0';
2141
2142 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2143 final_len * sizeof(WCHAR));
2144 if(ret){
2145 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2146 MemFree(final);
2147 RegCloseKey(hkey);
2148 return;
2149 }
2150
2151 MemFree(final);
2152 }
2153
2154 { /* update MRUList value */
2155 WCHAR old_mru_list[32], new_mru_list[32];
2156 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2157 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2158
2159 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2160 (LPBYTE)old_mru_list, &mru_list_size);
2161 if(ret || key_type != REG_SZ){
2162 if(ret == ERROR_FILE_NOT_FOUND){
2163 new_mru_list[0] = slot;
2164 new_mru_list[1] = '\0';
2165 }else{
2166 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2167 RegCloseKey(hkey);
2168 return;
2169 }
2170 }else{
2171 /* copy old list data over so that the new slot is at the start
2172 * of the list */
2173 *new_mru_slot++ = slot;
2174 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2175 if(*old_mru_slot != slot)
2176 *new_mru_slot++ = *old_mru_slot;
2177 }
2178 *new_mru_slot = '\0';
2179 }
2180
2181 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2182 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2183 if(ret){
2184 WARN("Error saving MRUList data: %d\n", ret);
2185 RegCloseKey(hkey);
2186 return;
2187 }
2188 }
2189 }
2190
2191 /* load the most-recently-used path for this module */
2192 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2193 {
2194 WCHAR module_path[MAX_PATH], *module_name;
2195
2196 /* get the current executable's name */
2197 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2198 WARN("GotModuleFileName failed: %d\n", GetLastError());
2199 return;
2200 }
2201 module_name = strrchrW(module_path, '\\');
2202 if(!module_name)
2203 module_name = module_path;
2204 else
2205 module_name += 1;
2206
2207 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2208 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2209 }
2210
2211 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2212 {
2213 WCHAR strMsgTitle[MAX_PATH];
2214 WCHAR strMsgText [MAX_PATH];
2215 if (idCaption)
2216 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2217 else
2218 strMsgTitle[0] = '\0';
2219 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2220 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2221 }
2222
2223 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2224 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2225 {
2226 int nOpenAction = defAction;
2227 LPWSTR lpszTemp, lpszTemp1;
2228 LPITEMIDLIST pidl = NULL;
2229 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2230
2231 /* check for invalid chars */
2232 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2233 {
2234 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2235 return FALSE;
2236 }
2237
2238 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2239
2240 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2241 while (lpszTemp1)
2242 {
2243 LPSHELLFOLDER lpsfChild;
2244 WCHAR lpwstrTemp[MAX_PATH];
2245 DWORD dwEaten, dwAttributes;
2246 LPWSTR p;
2247
2248 lstrcpyW(lpwstrTemp, lpszTemp);
2249 p = PathFindNextComponentW(lpwstrTemp);
2250
2251 if (!p) break; /* end of path */
2252
2253 *p = 0;
2254 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2255
2256 /* There are no wildcards when OFN_NOVALIDATE is set */
2257 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2258 {
2259 static const WCHAR wszWild[] = { '*', '?', 0 };
2260 /* if the last element is a wildcard do a search */
2261 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2262 {
2263 nOpenAction = ONOPEN_SEARCH;
2264 break;
2265 }
2266 }
2267 lpszTemp1 = lpszTemp;
2268
2269 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2270
2271 /* append a backslash to drive letters */
2272 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2273 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2274 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2275 {
2276 PathAddBackslashW(lpwstrTemp);
2277 }
2278
2279 dwAttributes = SFGAO_FOLDER;
2280 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2281 {
2282 /* the path component is valid, we have a pidl of the next path component */
2283 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2284 if(dwAttributes & SFGAO_FOLDER)
2285 {
2286 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2287 {
2288 ERR("bind to failed\n"); /* should not fail */
2289 break;
2290 }
2291 IShellFolder_Release(*ppsf);
2292 *ppsf = lpsfChild;
2293 lpsfChild = NULL;
2294 }
2295 else
2296 {
2297 TRACE("value\n");
2298
2299 /* end dialog, return value */
2300 nOpenAction = ONOPEN_OPEN;
2301 break;
2302 }
2303 COMDLG32_SHFree(pidl);
2304 pidl = NULL;
2305 }
2306 else if (!(flags & OFN_NOVALIDATE))
2307 {
2308 if(*lpszTemp || /* points to trailing null for last path element */
2309 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2310 {
2311 if(flags & OFN_PATHMUSTEXIST)
2312 {
2313 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2314 break;
2315 }
2316 }
2317 else
2318 {
2319 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2320 {
2321 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2322 break;
2323 }
2324 }
2325 /* change to the current folder */
2326 nOpenAction = ONOPEN_OPEN;
2327 break;
2328 }
2329 else
2330 {
2331 nOpenAction = ONOPEN_OPEN;
2332 break;
2333 }
2334 }
2335 if(pidl) COMDLG32_SHFree(pidl);
2336
2337 return nOpenAction;
2338 }
2339
2340 /***********************************************************************
2341 * FILEDLG95_OnOpen
2342 *
2343 * Ok button WM_COMMAND message handler
2344 *
2345 * If the function succeeds, the return value is nonzero.
2346 */
2347 BOOL FILEDLG95_OnOpen(HWND hwnd)
2348 {
2349 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2350 LPWSTR lpstrFileList;
2351 UINT nFileCount = 0;
2352 UINT sizeUsed = 0;
2353 BOOL ret = TRUE;
2354 WCHAR lpstrPathAndFile[MAX_PATH];
2355 LPSHELLFOLDER lpsf = NULL;
2356 int nOpenAction;
2357
2358 TRACE("hwnd=%p\n", hwnd);
2359
2360 /* try to browse the selected item */
2361 if(BrowseSelectedFolder(hwnd))
2362 return FALSE;
2363
2364 /* get the files from the edit control */
2365 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2366
2367 if(nFileCount == 0)
2368 return FALSE;
2369
2370 if(nFileCount > 1)
2371 {
2372 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2373 goto ret;
2374 }
2375
2376 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2377
2378 /*
2379 Step 1: Build a complete path name from the current folder and
2380 the filename or path in the edit box.
2381 Special cases:
2382 - the path in the edit box is a root path
2383 (with or without drive letter)
2384 - the edit box contains ".." (or a path with ".." in it)
2385 */
2386
2387 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2388 MemFree(lpstrFileList);
2389
2390 /*
2391 Step 2: here we have a cleaned up path
2392
2393 We have to parse the path step by step to see if we have to browse
2394 to a folder if the path points to a directory or the last
2395 valid element is a directory.
2396
2397 valid variables:
2398 lpstrPathAndFile: cleaned up path
2399 */
2400
2401 if (nFileCount &&
2402 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2403 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2404 nOpenAction = ONOPEN_OPEN;
2405 else
2406 nOpenAction = ONOPEN_BROWSE;
2407
2408 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2409 fodInfos->ofnInfos->Flags,
2410 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2411 nOpenAction);
2412 if(!nOpenAction)
2413 goto ret;
2414
2415 /*
2416 Step 3: here we have a cleaned up and validated path
2417
2418 valid variables:
2419 lpsf: ShellFolder bound to the rightmost valid path component
2420 lpstrPathAndFile: cleaned up path
2421 nOpenAction: action to do
2422 */
2423 TRACE("end validate sf=%p\n", lpsf);
2424
2425 switch(nOpenAction)
2426 {
2427 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2428 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2429 {
2430 int iPos;
2431 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2432 DWORD len;
2433
2434 /* replace the current filter */
2435 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2436 len = lstrlenW(lpszTemp)+1;
2437 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2438 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2439
2440 /* set the filter cb to the extension when possible */
2441 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2442 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2443 }
2444 /* fall through */
2445 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2446 TRACE("ONOPEN_BROWSE\n");
2447 {
2448 IPersistFolder2 * ppf2;
2449 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2450 {
2451 LPITEMIDLIST pidlCurrent;
2452 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2453 IPersistFolder2_Release(ppf2);
2454 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2455 {
2456 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2457 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2458 {
2459 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2460 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2461 }
2462 }
2463 else if( nOpenAction == ONOPEN_SEARCH )
2464 {
2465 if (fodInfos->Shell.FOIShellView)
2466 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2467 }
2468 COMDLG32_SHFree(pidlCurrent);
2469 if (filename_is_edit( fodInfos ))
2470 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2471 else
2472 {
2473 HWND hwnd;
2474
2475 hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2476 SendMessageW(hwnd, EM_SETSEL, 0, -1);
2477 }
2478 }
2479 }
2480 ret = FALSE;
2481 break;
2482 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2483 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2484 {
2485 WCHAR *ext = NULL;
2486
2487 /* update READONLY check box flag */
2488 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2489 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2490 else
2491 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2492
2493 /* Attach the file extension with file name*/
2494 ext = PathFindExtensionW(lpstrPathAndFile);
2495 if (! *ext && fodInfos->defext)
2496 {
2497 /* if no extension is specified with file name, then */
2498 /* attach the extension from file filter or default one */
2499
2500 WCHAR *filterExt = NULL;
2501 LPWSTR lpstrFilter = NULL;
2502 static const WCHAR szwDot[] = {'.',0};
2503 int PathLength = lstrlenW(lpstrPathAndFile);
2504
2505 /*Get the file extension from file type filter*/
2506 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2507 fodInfos->ofnInfos->nFilterIndex-1);
2508
2509 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2510 {
2511 WCHAR* filterSearchIndex;
2512 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2513 strcpyW(filterExt, lpstrFilter);
2514
2515 /* if a semicolon-separated list of file extensions was given, do not include the
2516 semicolon or anything after it in the extension.
2517 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2518 filterSearchIndex = strchrW(filterExt, ';');
2519 if (filterSearchIndex)
2520 {
2521 filterSearchIndex[0] = '\0';
2522 }
2523
2524 /* find the file extension by searching for the first dot in filterExt */
2525 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2526 /* if the extension is invalid or contains a glob, ignore it */
2527 filterSearchIndex = strchrW(filterExt, '.');
2528 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2529 {
2530 strcpyW(filterExt, filterSearchIndex);
2531 }
2532 else
2533 {
2534 HeapFree(GetProcessHeap(), 0, filterExt);
2535 filterExt = NULL;
2536 }
2537 }
2538
2539 if (!filterExt)
2540 {
2541 /* use the default file extension */
2542 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2543 strcpyW(filterExt, fodInfos->defext);
2544 }
2545
2546 if (*filterExt) /* ignore filterExt="" */
2547 {
2548 /* Attach the dot*/
2549 lstrcatW(lpstrPathAndFile, szwDot);
2550 /* Attach the extension */
2551 lstrcatW(lpstrPathAndFile, filterExt);
2552 }
2553
2554 HeapFree(GetProcessHeap(), 0, filterExt);
2555
2556 /* In Open dialog: if file does not exist try without extension */
2557 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2558 lpstrPathAndFile[PathLength] = '\0';
2559
2560 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2561 if (*ext)
2562 ext++;
2563 if (!lstrcmpiW(fodInfos->defext, ext))
2564 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2565 else
2566 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2567 }
2568
2569 /* In Save dialog: check if the file already exists */
2570 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2571 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2572 && PathFileExistsW(lpstrPathAndFile))
2573 {
2574 WCHAR lpstrOverwrite[100];
2575 int answer;
2576
2577 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2578 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2579 MB_YESNO | MB_ICONEXCLAMATION);
2580 if (answer == IDNO || answer == IDCANCEL)
2581 {
2582 ret = FALSE;
2583 goto ret;
2584 }
2585 }
2586
2587 /* In Open dialog: check if it should be created if it doesn't exist */
2588 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2589 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2590 && !PathFileExistsW(lpstrPathAndFile))
2591 {
2592 WCHAR lpstrCreate[100];
2593 int answer;
2594
2595 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2596 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2597 MB_YESNO | MB_ICONEXCLAMATION);
2598 if (answer == IDNO || answer == IDCANCEL)
2599 {
2600 ret = FALSE;
2601 goto ret;
2602 }
2603 }
2604
2605 /* Check that the size of the file does not exceed buffer size.
2606 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2607 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2608 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2609 {
2610
2611 /* fill destination buffer */
2612 if (fodInfos->ofnInfos->lpstrFile)
2613 {
2614 if(fodInfos->unicode)
2615 {
2616 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2617
2618 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2619 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2620 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2621 }
2622 else
2623 {
2624 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2625
2626 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2627 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2628 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2629 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2630 }
2631 }
2632
2633 if(fodInfos->unicode)
2634 {
2635 LPWSTR lpszTemp;
2636
2637 /* set filename offset */
2638 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2639 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2640
2641 /* set extension offset */
2642 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2643 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2644 }
2645 else
2646 {
2647 LPSTR lpszTemp;
2648 CHAR tempFileA[MAX_PATH];
2649
2650 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2651 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2652 tempFileA, sizeof(tempFileA), NULL, NULL);
2653
2654 /* set filename offset */
2655 lpszTemp = PathFindFileNameA(tempFileA);
2656 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2657
2658 /* set extension offset */
2659 lpszTemp = PathFindExtensionA(tempFileA);
2660 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2661 }
2662
2663 /* set the lpstrFileTitle */
2664 if(fodInfos->ofnInfos->lpstrFileTitle)
2665 {
2666 LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2667 if(fodInfos->unicode)
2668 {
2669 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2670 lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2671 }
2672 else
2673 {
2674 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2675 WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2676 ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2677 }
2678 }
2679
2680 /* copy currently selected filter to lpstrCustomFilter */
2681 if (fodInfos->ofnInfos->lpstrCustomFilter)
2682 {
2683 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2684 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2685 NULL, 0, NULL, NULL);
2686 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2687 {
2688 LPSTR s = ofn->lpstrCustomFilter;
2689 s += strlen(ofn->lpstrCustomFilter)+1;
2690 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2691 s, len, NULL, NULL);
2692 }
2693 }
2694
2695
2696 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2697 goto ret;
2698
2699 FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2700
2701 TRACE("close\n");
2702 FILEDLG95_Clean(hwnd);
2703 ret = EndDialog(hwnd, TRUE);
2704 }
2705 else
2706 {
2707 WORD size;
2708
2709 size = lstrlenW(lpstrPathAndFile) + 1;
2710 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2711 size += 1;
2712 /* return needed size in first two bytes of lpstrFile */
2713 if(fodInfos->ofnInfos->lpstrFile)
2714 *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2715 FILEDLG95_Clean(hwnd);
2716 ret = EndDialog(hwnd, FALSE);
2717 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2718 }
2719 }
2720 break;
2721 }
2722
2723 ret:
2724 if(lpsf) IShellFolder_Release(lpsf);
2725 return ret;
2726 }
2727
2728 /***********************************************************************
2729 * FILEDLG95_SHELL_Init
2730 *
2731 * Initialisation of the shell objects
2732 */
2733 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2734 {
2735 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2736
2737 TRACE("%p\n", hwnd);
2738
2739 /*
2740 * Initialisation of the FileOpenDialogInfos structure
2741 */
2742
2743 /* Shell */
2744
2745 /*ShellInfos */
2746 fodInfos->ShellInfos.hwndOwner = hwnd;
2747
2748 /* Disable multi-select if flag not set */
2749 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2750 {
2751 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2752 }
2753 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2754 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2755
2756 /* Construct the IShellBrowser interface */
2757 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2758
2759 return NOERROR;
2760 }
2761
2762 /***********************************************************************
2763 * FILEDLG95_SHELL_ExecuteCommand
2764 *
2765 * Change the folder option and refresh the view
2766 * If the function succeeds, the return value is nonzero.
2767 */
2768 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2769 {
2770 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2771 IContextMenu * pcm;
2772
2773 TRACE("(%p,%p)\n", hwnd, lpVerb);
2774
2775 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2776 SVGIO_BACKGROUND,
2777 &IID_IContextMenu,
2778 (LPVOID*)&pcm)))
2779 {
2780 CMINVOKECOMMANDINFO ci;
2781 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2782 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2783 ci.lpVerb = lpVerb;
2784 ci.hwnd = hwnd;
2785
2786 IContextMenu_InvokeCommand(pcm, &ci);
2787 IContextMenu_Release(pcm);
2788 }
2789
2790 return FALSE;
2791 }
2792
2793 /***********************************************************************
2794 * FILEDLG95_SHELL_UpFolder
2795 *
2796 * Browse to the specified object
2797 * If the function succeeds, the return value is nonzero.
2798 */
2799 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2800 {
2801 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2802
2803 TRACE("\n");
2804
2805 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2806 NULL,
2807 SBSP_PARENT)))
2808 {
2809 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2810 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2811 return TRUE;
2812 }
2813 return FALSE;
2814 }
2815
2816 /***********************************************************************
2817 * FILEDLG95_SHELL_BrowseToDesktop
2818 *
2819 * Browse to the Desktop
2820 * If the function succeeds, the return value is nonzero.
2821 */
2822 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2823 {
2824 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2825 LPITEMIDLIST pidl;
2826 HRESULT hres;
2827
2828 TRACE("\n");
2829
2830 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2831 hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2832 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2833 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2834 COMDLG32_SHFree(pidl);
2835 return SUCCEEDED(hres);
2836 }
2837 /***********************************************************************
2838 * FILEDLG95_SHELL_Clean
2839 *
2840 * Cleans the memory used by shell objects
2841 */
2842 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2843 {
2844 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2845
2846 TRACE("\n");
2847
2848 COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2849
2850 /* clean Shell interfaces */
2851 if (fodInfos->Shell.FOIShellView)
2852 {
2853 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2854 IShellView_Release(fodInfos->Shell.FOIShellView);
2855 }
2856 if (fodInfos->Shell.FOIShellFolder)
2857 IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2858 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2859 if (fodInfos->Shell.FOIDataObject)
2860 IDataObject_Release(fodInfos->Shell.FOIDataObject);
2861 }
2862
2863 /***********************************************************************
2864 * FILEDLG95_FILETYPE_Init
2865 *
2866 * Initialisation of the file type combo box
2867 */
2868 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2869 {
2870 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2871 int nFilters = 0; /* number of filters */
2872 int nFilterIndexCB;
2873
2874 TRACE("%p\n", hwnd);
2875
2876 if(fodInfos->customfilter)
2877 {
2878 /* customfilter has one entry... title\0ext\0
2879 * Set first entry of combo box item with customfilter
2880 */
2881 LPWSTR lpstrExt;
2882 LPCWSTR lpstrPos = fodInfos->customfilter;
2883
2884 /* Get the title */
2885 lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2886
2887 /* Copy the extensions */
2888 if (! *lpstrPos) return E_FAIL; /* malformed filter */
2889 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2890 lstrcpyW(lpstrExt,lpstrPos);
2891
2892 /* Add the item at the end of the combo */
2893 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2894 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2895 nFilters++;
2896 }
2897 if(fodInfos->filter)
2898 {
2899 LPCWSTR lpstrPos = fodInfos->filter;
2900
2901 for(;;)
2902 {
2903 /* filter is a list... title\0ext\0......\0\0
2904 * Set the combo item text to the title and the item data
2905 * to the ext
2906 */
2907 LPCWSTR lpstrDisplay;
2908 LPWSTR lpstrExt;
2909
2910 /* Get the title */
2911 if(! *lpstrPos) break; /* end */
2912 lpstrDisplay = lpstrPos;
2913 lpstrPos += lstrlenW(lpstrPos) + 1;
2914
2915 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2916
2917 nFilters++;
2918
2919 /* Copy the extensions */
2920 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2921 lstrcpyW(lpstrExt,lpstrPos);
2922 lpstrPos += lstrlenW(lpstrPos) + 1;
2923
2924 /* Add the item at the end of the combo */
2925 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
2926
2927 /* malformed filters are added anyway... */
2928 if (!*lpstrExt) break;
2929 }
2930 }
2931
2932 /*
2933 * Set the current filter to the one specified
2934 * in the initialisation structure
2935 */
2936 if (fodInfos->filter || fodInfos->customfilter)
2937 {
2938 LPWSTR lpstrFilter;
2939
2940 /* Check to make sure our index isn't out of bounds. */
2941 if ( fodInfos->ofnInfos->nFilterIndex >
2942 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
2943 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
2944
2945 /* set default filter index */
2946 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
2947 fodInfos->ofnInfos->nFilterIndex = 1;
2948
2949 /* calculate index of Combo Box item */
2950 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
2951 if (fodInfos->customfilter == NULL)
2952 nFilterIndexCB--;
2953
2954 /* Set the current index selection. */
2955 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
2956
2957 /* Get the corresponding text string from the combo box. */
2958 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2959 nFilterIndexCB);
2960
2961 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
2962 lpstrFilter = NULL;
2963
2964 if(lpstrFilter)
2965 {
2966 DWORD len;
2967 CharLowerW(lpstrFilter); /* lowercase */
2968 len = lstrlenW(lpstrFilter)+1;
2969 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
2970 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
2971 }
2972 } else
2973 fodInfos->ofnInfos->nFilterIndex = 0;
2974 return S_OK;
2975 }
2976
2977 /***********************************************************************
2978 * FILEDLG95_FILETYPE_OnCommand
2979 *
2980 * WM_COMMAND of the file type combo box
2981 * If the function succeeds, the return value is nonzero.
2982 */
2983 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
2984 {
2985 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2986
2987 switch(wNotifyCode)
2988 {
2989 case CBN_SELENDOK:
2990 {
2991 LPWSTR lpstrFilter;
2992
2993 /* Get the current item of the filetype combo box */
2994 int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
2995
2996 /* set the current filter index */
2997 fodInfos->ofnInfos->nFilterIndex = iItem +
2998 (fodInfos->customfilter == NULL ? 1 : 0);
2999
3000 /* Set the current filter with the current selection */
3001 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3002
3003 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3004 iItem);
3005 if((INT_PTR)lpstrFilter != CB_ERR)
3006 {
3007 DWORD len;
3008 CharLowerW(lpstrFilter); /* lowercase */
3009 len = lstrlenW(lpstrFilter)+1;
3010 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3011 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3012 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3013 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3014 }
3015
3016 /* Refresh the actual view to display the included items*/
3017 if (fodInfos->Shell.FOIShellView)
3018 IShellView_Refresh(fodInfos->Shell.FOIShellView);
3019 }
3020 }
3021 return FALSE;
3022 }
3023 /***********************************************************************
3024 * FILEDLG95_FILETYPE_SearchExt
3025 *
3026 * searches for an extension in the filetype box
3027 */
3028 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3029 {
3030 int i, iCount = CBGetCount(hwnd);
3031
3032 TRACE("%s\n", debugstr_w(lpstrExt));
3033
3034 if(iCount != CB_ERR)
3035 {
3036 for(i=0;i<iCount;i++)
3037 {
3038 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3039 return i;
3040 }
3041 }
3042 return -1;
3043 }
3044
3045 /***********************************************************************
3046 * FILEDLG95_FILETYPE_Clean
3047 *
3048 * Clean the memory used by the filetype combo box
3049 */
3050 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3051 {
3052 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3053 int iPos;
3054 int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3055
3056 TRACE("\n");
3057
3058 /* Delete each string of the combo and their associated data */
3059 if(iCount != CB_ERR)
3060 {
3061 for(iPos = iCount-1;iPos>=0;iPos--)
3062 {
3063 MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3064 CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3065 }
3066 }
3067 /* Current filter */
3068 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3069
3070 }
3071
3072 /***********************************************************************
3073 * FILEDLG95_LOOKIN_Init
3074 *
3075 * Initialisation of the look in combo box
3076 */
3077
3078 /* Small helper function, to determine if the unixfs shell extension is rooted
3079 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3080 */
3081 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3082 HKEY hKey;
3083 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3084 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3085 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3086 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3087 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3088 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3089 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3090
3091 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3092 return FALSE;
3093
3094 RegCloseKey(hKey);
3095 return TRUE;
3096 }
3097
3098 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3099 {
3100 IShellFolder *psfRoot, *psfDrives;
3101 IEnumIDList *lpeRoot, *lpeDrives;
3102 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3103 HDC hdc;
3104 TEXTMETRICW tm;
3105 LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3106
3107 TRACE("%p\n", hwndCombo);
3108
3109 liInfos->iMaxIndentation = 0;
3110
3111 SetPropA(hwndCombo, LookInInfosStr, liInfos);
3112
3113 hdc = GetDC( hwndCombo );
3114 SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3115 GetTextMetricsW( hdc, &tm );
3116 ReleaseDC( hwndCombo, hdc );
3117
3118 /* set item height for both text field and listbox */
3119 CBSetItemHeight( hwndCombo, -1, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3120 CBSetItemHeight( hwndCombo, 0, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3121
3122 /* Turn on the extended UI for the combo box like Windows does */
3123 CBSetExtendedUI(hwndCombo, TRUE);
3124
3125 /* Initialise data of Desktop folder */
3126 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3127 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3128 COMDLG32_SHFree(pidlTmp);
3129
3130 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3131
3132 SHGetDesktopFolder(&psfRoot);
3133
3134 if (psfRoot)
3135 {
3136 /* enumerate the contents of the desktop */
3137 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3138 {
3139 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3140 {
3141 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3142
3143 /* If the unixfs extension is rooted, we don't expand the drives by default */
3144 if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3145 {
3146 /* special handling for CSIDL_DRIVES */
3147 if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3148 {
3149 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3150 {
3151 /* enumerate the drives */
3152 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3153 {
3154 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3155 {
3156 pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3157 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3158 COMDLG32_SHFree(pidlAbsTmp);
3159 COMDLG32_SHFree(pidlTmp1);
3160 }
3161 IEnumIDList_Release(lpeDrives);
3162 }
3163 IShellFolder_Release(psfDrives);
3164 }
3165 }
3166 }
3167
3168 COMDLG32_SHFree(pidlTmp);
3169 }
3170 IEnumIDList_Release(lpeRoot);
3171 }
3172 IShellFolder_Release(psfRoot);
3173 }
3174
3175 COMDLG32_SHFree(pidlDrives);
3176 }
3177
3178 /***********************************************************************
3179 * FILEDLG95_LOOKIN_DrawItem
3180 *
3181 * WM_DRAWITEM message handler
3182 */
3183 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3184 {
3185 COLORREF crWin = GetSysColor(COLOR_WINDOW);
3186 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3187 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3188 RECT rectText;
3189 RECT rectIcon;
3190 SHFILEINFOW sfi;
3191 HIMAGELIST ilItemImage;
3192 int iIndentation;
3193 TEXTMETRICW tm;
3194 LPSFOLDER tmpFolder;
3195 UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3196 UINT icon_width, icon_height;
3197
3198 TRACE("\n");
3199
3200 if(pDIStruct->itemID == -1)
3201 return 0;
3202
3203 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3204 pDIStruct->itemID)))
3205 return 0;
3206
3207
3208 icon_width = GetSystemMetrics(SM_CXICON);
3209 icon_height = GetSystemMetrics(SM_CYICON);
3210 if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3211 {
3212 icon_width = GetSystemMetrics(SM_CXSMICON);
3213 icon_height = GetSystemMetrics(SM_CYSMICON);
3214 shgfi_flags |= SHGFI_SMALLICON;
3215 }
3216
3217 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3218 0, &sfi, sizeof (sfi), shgfi_flags );
3219
3220 /* Is this item selected ? */
3221 if(pDIStruct->itemState & ODS_SELECTED)
3222 {
3223 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3224 SetBkColor(pDIStruct->hDC,crHighLight);
3225 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3226 }
3227 else
3228 {
3229 SetTextColor(pDIStruct->hDC,crText);
3230 SetBkColor(pDIStruct->hDC,crWin);
3231 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3232 }
3233
3234 /* Do not indent item if drawing in the edit of the combo */
3235 if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3236 iIndentation = 0;
3237 else
3238 iIndentation = tmpFolder->m_iIndent;
3239
3240 /* Draw text and icon */
3241
3242 /* Initialise the icon display area */
3243 rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3244 rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3245 rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3246 rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3247
3248 /* Initialise the text display area */
3249 GetTextMetricsW(pDIStruct->hDC, &tm);
3250 rectText.left = rectIcon.right;
3251 rectText.top =
3252 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3253 rectText.right = pDIStruct->rcItem.right;
3254 rectText.bottom =
3255 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3256
3257 /* Draw the icon from the image list */
3258 ImageList_Draw(ilItemImage,
3259 sfi.iIcon,
3260 pDIStruct->hDC,
3261 rectIcon.left,
3262 rectIcon.top,
3263 ILD_TRANSPARENT );
3264
3265 /* Draw the associated text */
3266 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3267 return NOERROR;
3268 }
3269
3270 /***********************************************************************
3271 * FILEDLG95_LOOKIN_OnCommand
3272 *
3273 * LookIn combo box WM_COMMAND message handler
3274 * If the function succeeds, the return value is nonzero.
3275 */
3276 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3277 {
3278 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3279
3280 TRACE("%p\n", fodInfos);
3281
3282 switch(wNotifyCode)
3283 {
3284 case CBN_SELENDOK:
3285 {
3286 LPSFOLDER tmpFolder;
3287 int iItem;
3288
3289 iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3290
3291 if( iItem == CB_ERR) return FALSE;
3292
3293 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3294 iItem)))
3295 return FALSE;
3296
3297
3298 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3299 tmpFolder->pidlItem,
3300 SBSP_ABSOLUTE)))
3301 {
3302 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3303 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3304 return TRUE;
3305 }
3306 break;
3307 }
3308
3309 }
3310 return FALSE;
3311 }
3312
3313 /***********************************************************************
3314 * FILEDLG95_LOOKIN_AddItem
3315 *
3316 * Adds an absolute pidl item to the lookin combo box
3317 * returns the index of the inserted item
3318 */
3319 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3320 {
3321 LPITEMIDLIST pidlNext;
3322 SHFILEINFOW sfi;
3323 SFOLDER *tmpFolder;
3324 LookInInfos *liInfos;
3325
3326 TRACE("%p, %p, %d\n", hwnd, pidl, iInsertId);
3327
3328 if(!pidl)
3329 return -1;
3330
3331 if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3332 return -1;
3333
3334 tmpFolder = MemAlloc(sizeof(SFOLDER));
3335 tmpFolder->m_iIndent = 0;
3336
3337 /* Calculate the indentation of the item in the lookin*/
3338 pidlNext = pidl;
3339 while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3340 {
3341 tmpFolder->m_iIndent++;
3342 }
3343
3344 tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3345
3346 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3347 liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3348
3349 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3350 SHGetFileInfoW((LPCWSTR)pidl,
3351 0,
3352 &sfi,
3353 sizeof(sfi),
3354 SHGFI_DISPLAYNAME | SHGFI_PIDL | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3355
3356 TRACE("-- Add %s attr=0x%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3357
3358 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3359 {
3360 int iItemID;
3361
3362 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3363
3364 /* Add the item at the end of the list */
3365 if(iInsertId < 0)
3366 {
3367 iItemID = CBAddString(hwnd,sfi.szDisplayName);
3368 }
3369 /* Insert the item at the iInsertId position*/
3370 else
3371 {
3372 iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3373 }
3374
3375 CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3376 return iItemID;
3377 }
3378
3379 COMDLG32_SHFree( tmpFolder->pidlItem );
3380 MemFree( tmpFolder );
3381 return -1;
3382
3383 }
3384
3385 /***********************************************************************
3386 * FILEDLG95_LOOKIN_InsertItemAfterParent
3387 *
3388 * Insert an item below its parent
3389 */
3390 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3391 {
3392
3393 LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3394 int iParentPos;
3395
3396 TRACE("\n");
3397
3398 if (pidl == pidlParent)
3399 return -1;
3400
3401 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3402
3403 if(iParentPos < 0)
3404 {
3405 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3406 }
3407
3408 /* Free pidlParent memory */
3409 COMDLG32_SHFree(pidlParent);
3410
3411 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3412 }
3413
3414 /***********************************************************************
3415 * FILEDLG95_LOOKIN_SelectItem
3416 *
3417 * Adds an absolute pidl item to the lookin combo box
3418 * returns the index of the inserted item
3419 */
3420 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3421 {
3422 int iItemPos;
3423 LookInInfos *liInfos;
3424
3425 TRACE("%p, %p\n", hwnd, pidl);
3426
3427 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3428
3429 liInfos = GetPropA(hwnd,LookInInfosStr);
3430
3431 if(iItemPos < 0)
3432 {
3433 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3434 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3435 }
3436
3437 else
3438 {
3439 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3440 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3441 {
3442 int iRemovedItem;
3443
3444 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3445 break;
3446 if(iRemovedItem < iItemPos)
3447 iItemPos--;
3448 }
3449 }
3450
3451 CBSetCurSel(hwnd,iItemPos);
3452 liInfos->uSelectedItem = iItemPos;
3453
3454 return 0;
3455
3456 }
3457
3458 /***********************************************************************
3459 * FILEDLG95_LOOKIN_RemoveMostExpandedItem
3460 *
3461 * Remove the item with an expansion level over iExpansionLevel
3462 */
3463 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3464 {
3465 int iItemPos;
3466 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3467
3468 TRACE("\n");
3469
3470 if(liInfos->iMaxIndentation <= 2)
3471 return -1;
3472
3473 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3474 {
3475 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3476 COMDLG32_SHFree(tmpFolder->pidlItem);
3477 MemFree(tmpFolder);
3478 CBDeleteString(hwnd,iItemPos);
3479 liInfos->iMaxIndentation--;
3480
3481 return iItemPos;
3482 }
3483
3484 return -1;
3485 }
3486
3487 /***********************************************************************
3488 * FILEDLG95_LOOKIN_SearchItem
3489 *
3490 * Search for pidl in the lookin combo box
3491 * returns the index of the found item
3492 */
3493 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3494 {
3495 int i = 0;
3496 int iCount = CBGetCount(hwnd);
3497
3498 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3499
3500 if (iCount != CB_ERR)
3501 {
3502 for(;i<iCount;i++)
3503 {
3504 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3505
3506 if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3507 return i;
3508 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3509 return i;
3510 }
3511 }
3512
3513 return -1;
3514 }
3515
3516 /***********************************************************************
3517 * FILEDLG95_LOOKIN_Clean
3518 *
3519 * Clean the memory used by the lookin combo box
3520 */
3521 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3522 {
3523 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3524 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3525 int iPos;
3526 int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3527
3528 TRACE("\n");
3529
3530 /* Delete each string of the combo and their associated data */
3531 if (iCount != CB_ERR)
3532 {
3533 for(iPos = iCount-1;iPos>=0;iPos--)
3534 {
3535 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3536 COMDLG32_SHFree(tmpFolder->pidlItem);
3537 MemFree(tmpFolder);
3538 CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3539 }
3540 }
3541
3542 /* LookInInfos structure */
3543 MemFree(liInfos);
3544 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3545 }
3546
3547 /***********************************************************************
3548 * get_def_format
3549 *
3550 * Fill the FORMATETC used in the shell id list
3551 */
3552 static FORMATETC get_def_format(void)
3553 {
3554 static CLIPFORMAT cfFormat;
3555 FORMATETC formatetc;
3556
3557 if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA);
3558 formatetc.cfFormat = cfFormat;
3559 formatetc.ptd = 0;
3560 formatetc.dwAspect = DVASPECT_CONTENT;
3561 formatetc.lindex = -1;
3562 formatetc.tymed = TYMED_HGLOBAL;
3563 return formatetc;
3564 }
3565
3566 /***********************************************************************
3567 * FILEDLG95_FILENAME_FillFromSelection
3568 *
3569 * fills the edit box from the cached DataObject
3570 */
3571 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3572 {
3573 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3574 LPITEMIDLIST pidl;
3575 LPWSTR lpstrAllFiles, lpstrTmp;
3576 UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength;
3577 STGMEDIUM medium;
3578 LPIDA cida;
3579 FORMATETC formatetc = get_def_format();
3580
3581 TRACE("\n");
3582
3583 if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium)))
3584 return;
3585
3586 cida = GlobalLock(medium.u.hGlobal);
3587 nFileSelected = cida->cidl;
3588
3589 /* Allocate a buffer */
3590 nAllFilesMaxLength = MAX_PATH + 3;
3591 lpstrAllFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nAllFilesMaxLength * sizeof(WCHAR));
3592 if (!lpstrAllFiles)
3593 goto ret;
3594
3595 /* Loop through the selection, handle only files (not folders) */
3596 for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++)
3597 {
3598 pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]);
3599 if (pidl)
3600 {
3601 if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl))
3602 {
3603 if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength)
3604 {
3605 nAllFilesMaxLength *= 2;
3606 lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR));
3607 if (!lpstrTmp)
3608 goto ret;
3609 lpstrAllFiles = lpstrTmp;
3610 }
3611 nFiles += 1;
3612 lpstrAllFiles[nAllFilesLength++] = '"';
3613 GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength);
3614 nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength);
3615 nAllFilesLength += nThisFileLength;
3616 lpstrAllFiles[nAllFilesLength++] = '"';
3617 lpstrAllFiles[nAllFilesLength++] = ' ';
3618 }
3619 }
3620 }
3621
3622 if (nFiles != 0)
3623 {
3624 /* If there's only one file, use the name as-is without quotes */
3625 lpstrTmp = lpstrAllFiles;
3626 if (nFiles == 1)
3627 {
3628 lpstrTmp += 1;
3629 lpstrTmp[nThisFileLength] = 0;
3630 }
3631 SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp);
3632 /* Select the file name like Windows does */
3633 if (filename_is_edit(fodInfos))
3634 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3635 }
3636
3637 ret:
3638 HeapFree(GetProcessHeap(), 0, lpstrAllFiles);
3639 COMCTL32_ReleaseStgMedium(medium);
3640 }
3641
3642
3643 /* copied from shell32 to avoid linking to it
3644 * Although shell32 is already linked the behaviour of exported StrRetToStrN
3645 * is dependent on whether emulated OS is unicode or not.
3646 */
3647 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3648 {
3649 switch (src->uType)
3650 {
3651 case STRRET_WSTR:
3652 lstrcpynW(dest, src->u.pOleStr, len);
3653 COMDLG32_SHFree(src->u.pOleStr);
3654 break;
3655
3656 case STRRET_CSTR:
3657 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3658 dest[len-1] = 0;
3659 break;
3660
3661 case STRRET_OFFSET:
3662 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3663 dest[len-1] = 0;
3664 break;
3665
3666 default:
3667 FIXME("unknown type %x!\n", src->uType);
3668 if (len) *dest = '\0';
3669 return E_FAIL;
3670 }
3671 return S_OK;
3672 }
3673
3674 /***********************************************************************
3675 * FILEDLG95_FILENAME_GetFileNames
3676 *
3677 * Copies the filenames to a delimited string list.
3678 */
3679 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3680 {
3681 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3682 UINT nFileCount = 0; /* number of files */
3683 UINT nStrLen = 0; /* length of string in edit control */
3684 LPWSTR lpstrEdit; /* buffer for string from edit control */
3685
3686 TRACE("\n");
3687
3688 /* get the filenames from the filename control */
3689 nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
3690 lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3691 GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
3692
3693 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3694
3695 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3696 MemFree(lpstrEdit);
3697 return nFileCount;
3698 }
3699
3700 /*
3701 * DATAOBJECT Helper functions
3702 */
3703
3704 /***********************************************************************
3705 * COMCTL32_ReleaseStgMedium
3706 *
3707 * like ReleaseStgMedium from ole32
3708 */
3709 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3710 {
3711 if(medium.pUnkForRelease)
3712 {
3713 IUnknown_Release(medium.pUnkForRelease);
3714 }
3715 else
3716 {
3717 GlobalUnlock(medium.u.hGlobal);
3718 GlobalFree(medium.u.hGlobal);
3719 }
3720 }
3721
3722 /***********************************************************************
3723 * GetPidlFromDataObject
3724 *
3725 * Return pidl(s) by number from the cached DataObject
3726 *
3727 * nPidlIndex=0 gets the fully qualified root path
3728 */
3729 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3730 {
3731
3732 STGMEDIUM medium;
3733 FORMATETC formatetc = get_def_format();
3734 LPITEMIDLIST pidl = NULL;
3735
3736 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3737
3738 if (!doSelected)
3739 return NULL;
3740
3741 /* Get the pidls from IDataObject */
3742 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3743 {
3744 LPIDA cida = GlobalLock(medium.u.hGlobal);
3745 if(nPidlIndex <= cida->cidl)
3746 {
3747 pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3748 }
3749 COMCTL32_ReleaseStgMedium(medium);
3750 }
3751 return pidl;
3752 }
3753
3754 /***********************************************************************
3755 * GetNumSelected
3756 *
3757 * Return the number of selected items in the DataObject.
3758 *
3759 */
3760 static UINT GetNumSelected( IDataObject *doSelected )
3761 {
3762 UINT retVal = 0;
3763 STGMEDIUM medium;
3764 FORMATETC formatetc = get_def_format();
3765
3766 TRACE("sv=%p\n", doSelected);
3767
3768 if (!doSelected) return 0;
3769
3770 /* Get the pidls from IDataObject */
3771 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3772 {
3773 LPIDA cida = GlobalLock(medium.u.hGlobal);
3774 retVal = cida->cidl;
3775 COMCTL32_ReleaseStgMedium(medium);
3776 return retVal;
3777 }
3778 return 0;
3779 }
3780
3781 /*
3782 * TOOLS
3783 */
3784
3785 /***********************************************************************
3786 * GetName
3787 *
3788 * Get the pidl's display name (relative to folder) and
3789 * put it in lpstrFileName.
3790 *
3791 * Return NOERROR on success,
3792 * E_FAIL otherwise
3793 */
3794
3795 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3796 {
3797 STRRET str;
3798 HRESULT hRes;
3799
3800 TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3801
3802 if(!lpsf)
3803 {
3804 SHGetDesktopFolder(&lpsf);
3805 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3806 IShellFolder_Release(lpsf);
3807 return hRes;
3808 }
3809
3810 /* Get the display name of the pidl relative to the folder */
3811 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3812 {
3813 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3814 }
3815 return E_FAIL;
3816 }
3817
3818 /***********************************************************************
3819 * GetShellFolderFromPidl
3820 *
3821 * pidlRel is the item pidl relative
3822 * Return the IShellFolder of the absolute pidl
3823 */
3824 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3825 {
3826 IShellFolder *psf = NULL,*psfParent;
3827
3828 TRACE("%p\n", pidlAbs);
3829
3830 if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3831 {
3832 psf = psfParent;
3833 if(pidlAbs && pidlAbs->mkid.cb)
3834 {
3835 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3836 {
3837 IShellFolder_Release(psfParent);
3838 return psf;
3839 }
3840 }
3841 /* return the desktop */
3842 return psfParent;
3843 }
3844 return NULL;
3845 }
3846
3847 /***********************************************************************
3848 * GetParentPidl
3849 *
3850 * Return the LPITEMIDLIST to the parent of the pidl in the list
3851 */
3852 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3853 {
3854 LPITEMIDLIST pidlParent;
3855
3856 TRACE("%p\n", pidl);
3857
3858 pidlParent = COMDLG32_PIDL_ILClone(pidl);
3859 COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3860
3861 return pidlParent;
3862 }
3863
3864 /***********************************************************************
3865 * GetPidlFromName
3866 *
3867 * returns the pidl of the file name relative to folder
3868 * NULL if an error occurred
3869 */
3870 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3871 {
3872 LPITEMIDLIST pidl = NULL;
3873 ULONG ulEaten;
3874
3875 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3876
3877 if(!lpcstrFileName) return NULL;
3878 if(!*lpcstrFileName) return NULL;
3879
3880 if(!lpsf)
3881 {
3882 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3883 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3884 IShellFolder_Release(lpsf);
3885 }
3886 }
3887 else
3888 {
3889 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3890 }
3891 return pidl;
3892 }
3893
3894 /*
3895 */
3896 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3897 {
3898 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3899 HRESULT ret;
3900
3901 TRACE("%p, %p\n", psf, pidl);
3902
3903 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3904
3905 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
3906 /* see documentation shell 4.1*/
3907 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3908 }
3909
3910 /***********************************************************************
3911 * BrowseSelectedFolder
3912 */
3913 static BOOL BrowseSelectedFolder(HWND hwnd)
3914 {
3915 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
3916 BOOL bBrowseSelFolder = FALSE;
3917
3918 TRACE("\n");
3919
3920 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
3921 {
3922 LPITEMIDLIST pidlSelection;
3923
3924 /* get the file selected */
3925 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
3926 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
3927 {
3928 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
3929 pidlSelection, SBSP_RELATIVE ) ) )
3930 {
3931 WCHAR buf[64];
3932 LoadStringW( COMDLG32_hInstance, IDS_PATHNOTEXISTING, buf, sizeof(buf)/sizeof(WCHAR) );
3933 MessageBoxW( hwnd, buf, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
3934 }
3935 bBrowseSelFolder = TRUE;
3936 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3937 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
3938 }
3939 COMDLG32_SHFree( pidlSelection );
3940 }
3941
3942 return bBrowseSelFolder;
3943 }
3944
3945 /*
3946 * Memory allocation methods */
3947 static void *MemAlloc(UINT size)
3948 {
3949 return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
3950 }
3951
3952 static void MemFree(void *mem)
3953 {
3954 HeapFree(GetProcessHeap(),0,mem);
3955 }
3956
3957 static inline BOOL valid_struct_size( DWORD size )
3958 {
3959 return (size == OPENFILENAME_SIZE_VERSION_400W) ||
3960 (size == sizeof( OPENFILENAMEW ));
3961 }
3962
3963 static inline BOOL is_win16_looks(DWORD flags)
3964 {
3965 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
3966 !(flags & OFN_EXPLORER));
3967 }
3968
3969 /* ------------------ APIs ---------------------- */
3970
3971 /***********************************************************************
3972 * GetOpenFileNameA (COMDLG32.@)
3973 *
3974 * Creates a dialog box for the user to select a file to open.
3975 *
3976 * RETURNS
3977 * TRUE on success: user enters a valid file
3978 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
3979 *
3980 */
3981 BOOL WINAPI GetOpenFileNameA(OPENFILENAMEA *ofn)
3982 {
3983 TRACE("flags 0x%08x\n", ofn->Flags);
3984
3985 if (!valid_struct_size( ofn->lStructSize ))
3986 {
3987 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
3988 return FALSE;
3989 }
3990
3991 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
3992 if (ofn->Flags & OFN_FILEMUSTEXIST)
3993 ofn->Flags |= OFN_PATHMUSTEXIST;
3994
3995 if (is_win16_looks(ofn->Flags))
3996 return GetFileName31A(ofn, OPEN_DIALOG);
3997 else
3998 {
3999 FileOpenDlgInfos info;
4000
4001 init_filedlg_infoA(ofn, &info);
4002 return GetFileDialog95(&info, OPEN_DIALOG);
4003 }
4004 }
4005
4006 /***********************************************************************
4007 * GetOpenFileNameW (COMDLG32.@)
4008 *
4009 * Creates a dialog box for the user to select a file to open.
4010 *
4011 * RETURNS
4012 * TRUE on success: user enters a valid file
4013 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4014 *
4015 */
4016 BOOL WINAPI GetOpenFileNameW(OPENFILENAMEW *ofn)
4017 {
4018 TRACE("flags 0x%08x\n", ofn->Flags);
4019
4020 if (!valid_struct_size( ofn->lStructSize ))
4021 {
4022 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4023 return FALSE;
4024 }
4025
4026 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4027 if (ofn->Flags & OFN_FILEMUSTEXIST)
4028 ofn->Flags |= OFN_PATHMUSTEXIST;
4029
4030 if (is_win16_looks(ofn->Flags))
4031 return GetFileName31W(ofn, OPEN_DIALOG);
4032 else
4033 {
4034 FileOpenDlgInfos info;
4035
4036 init_filedlg_infoW(ofn, &info);
4037 return GetFileDialog95(&info, OPEN_DIALOG);
4038 }
4039 }
4040
4041
4042 /***********************************************************************
4043 * GetSaveFileNameA (COMDLG32.@)
4044 *
4045 * Creates a dialog box for the user to select a file to save.
4046 *
4047 * RETURNS
4048 * TRUE on success: user enters a valid file
4049 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4050 *
4051 */
4052 BOOL WINAPI GetSaveFileNameA(OPENFILENAMEA *ofn)
4053 {
4054 if (!valid_struct_size( ofn->lStructSize ))
4055 {
4056 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4057 return FALSE;
4058 }
4059
4060 if (is_win16_looks(ofn->Flags))
4061 return GetFileName31A(ofn, SAVE_DIALOG);
4062 else
4063 {
4064 FileOpenDlgInfos info;
4065
4066 init_filedlg_infoA(ofn, &info);
4067 return GetFileDialog95(&info, SAVE_DIALOG);
4068 }
4069 }
4070
4071 /***********************************************************************
4072 * GetSaveFileNameW (COMDLG32.@)
4073 *
4074 * Creates a dialog box for the user to select a file to save.
4075 *
4076 * RETURNS
4077 * TRUE on success: user enters a valid file
4078 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4079 *
4080 */
4081 BOOL WINAPI GetSaveFileNameW(
4082 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4083 {
4084 if (!valid_struct_size( ofn->lStructSize ))
4085 {
4086 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4087 return FALSE;
4088 }
4089
4090 if (is_win16_looks(ofn->Flags))
4091 return GetFileName31W(ofn, SAVE_DIALOG);
4092 else
4093 {
4094 FileOpenDlgInfos info;
4095
4096 init_filedlg_infoW(ofn, &info);
4097 return GetFileDialog95(&info, SAVE_DIALOG);
4098 }
4099 }
4100
4101 /***********************************************************************
4102 * GetFileTitleA (COMDLG32.@)
4103 *
4104 * See GetFileTitleW.
4105 */
4106 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4107 {
4108 int ret;
4109 UNICODE_STRING strWFile;
4110 LPWSTR lpWTitle;
4111
4112 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4113 lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4114 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4115 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4116 RtlFreeUnicodeString( &strWFile );
4117 RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4118 return ret;
4119 }
4120
4121
4122 /***********************************************************************
4123 * GetFileTitleW (COMDLG32.@)
4124 *
4125 * Get the name of a file.
4126 *
4127 * PARAMS
4128 * lpFile [I] name and location of file
4129 * lpTitle [O] returned file name
4130 * cbBuf [I] buffer size of lpTitle
4131 *
4132 * RETURNS
4133 * Success: zero
4134 * Failure: negative number.
4135 */
4136 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4137 {
4138 int i, len;
4139 static const WCHAR brkpoint[] = {'*','[',']',0};
4140 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4141
4142 if(lpFile == NULL || lpTitle == NULL)
4143 return -1;
4144
4145 len = lstrlenW(lpFile);
4146
4147 if (len == 0)
4148 return -1;
4149
4150 if(strpbrkW(lpFile, brkpoint))
4151 return -1;
4152
4153 len--;
4154
4155 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4156 return -1;
4157
4158 for(i = len; i >= 0; i--)
4159 {
4160 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':')
4161 {
4162 i++;
4163 break;
4164 }
4165 }
4166
4167 if(i == -1)
4168 i++;
4169
4170 TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4171
4172 len = lstrlenW(lpFile+i)+1;
4173 if(cbBuf < len)
4174 return len;
4175
4176 lstrcpyW(lpTitle, &lpFile[i]);
4177 return 0;
4178 }