Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[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 /* set the lpstrFileTitle */
446 if (ret && info->ofnInfos->lpstrFile && info->ofnInfos->lpstrFileTitle)
447 {
448 if (info->unicode)
449 {
450 LPOPENFILENAMEW ofn = info->ofnInfos;
451 WCHAR *file_title = PathFindFileNameW(ofn->lpstrFile);
452 lstrcpynW(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
453 }
454 else
455 {
456 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)info->ofnInfos;
457 char *file_title = PathFindFileNameA(ofn->lpstrFile);
458 lstrcpynA(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
459 }
460 }
461
462 if (current_dir)
463 {
464 SetCurrentDirectoryW(current_dir);
465 MemFree(current_dir);
466 }
467
468 if (!info->unicode)
469 {
470 MemFree((WCHAR *)info->defext);
471 MemFree((WCHAR *)info->title);
472 MemFree((WCHAR *)info->filter);
473 MemFree((WCHAR *)info->customfilter);
474 }
475
476 MemFree(info->filename);
477 MemFree(info->initdir);
478 return ret;
479 }
480
481 /******************************************************************************
482 * COMDLG32_GetDisplayNameOf [internal]
483 *
484 * Helper function to get the display name for a pidl.
485 */
486 static BOOL COMDLG32_GetDisplayNameOf(LPCITEMIDLIST pidl, LPWSTR pwszPath) {
487 LPSHELLFOLDER psfDesktop;
488 STRRET strret;
489
490 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
491 return FALSE;
492
493 if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
494 IShellFolder_Release(psfDesktop);
495 return FALSE;
496 }
497
498 IShellFolder_Release(psfDesktop);
499 return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
500 }
501
502 /******************************************************************************
503 * COMDLG32_GetCanonicalPath [internal]
504 *
505 * Helper function to get the canonical path.
506 */
507 void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent,
508 LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
509 {
510 WCHAR lpstrTemp[MAX_PATH];
511
512 /* Get the current directory name */
513 if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
514 {
515 /* last fallback */
516 GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
517 }
518 PathAddBackslashW(lpstrPathAndFile);
519
520 TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile));
521
522 /* if the user specified a fully qualified path use it */
523 if(PathIsRelativeW(lpstrFile))
524 {
525 lstrcatW(lpstrPathAndFile, lpstrFile);
526 }
527 else
528 {
529 /* does the path have a drive letter? */
530 if (PathGetDriveNumberW(lpstrFile) == -1)
531 lstrcpyW(lpstrPathAndFile+2, lpstrFile);
532 else
533 lstrcpyW(lpstrPathAndFile, lpstrFile);
534 }
535
536 /* resolve "." and ".." */
537 PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
538 lstrcpyW(lpstrPathAndFile, lpstrTemp);
539 TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
540 }
541
542 /***********************************************************************
543 * COMDLG32_SplitFileNames [internal]
544 *
545 * Creates a delimited list of filenames.
546 */
547 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
548 {
549 UINT nStrCharCount = 0; /* index in src buffer */
550 UINT nFileIndex = 0; /* index in dest buffer */
551 UINT nFileCount = 0; /* number of files */
552
553 /* we might get single filename without any '"',
554 * so we need nStrLen + terminating \0 + end-of-list \0 */
555 *lpstrFileList = MemAlloc( (nStrLen+2)*sizeof(WCHAR) );
556 *sizeUsed = 0;
557
558 /* build delimited file list from filenames */
559 while ( nStrCharCount <= nStrLen )
560 {
561 if ( lpstrEdit[nStrCharCount]=='"' )
562 {
563 nStrCharCount++;
564 while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
565 {
566 (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
567 nStrCharCount++;
568 }
569 (*lpstrFileList)[nFileIndex++] = 0;
570 nFileCount++;
571 }
572 nStrCharCount++;
573 }
574
575 /* single, unquoted string */
576 if ((nStrLen > 0) && (nFileIndex == 0) )
577 {
578 lstrcpyW(*lpstrFileList, lpstrEdit);
579 nFileIndex = lstrlenW(lpstrEdit) + 1;
580 nFileCount = 1;
581 }
582
583 /* trailing \0 */
584 (*lpstrFileList)[nFileIndex++] = '\0';
585
586 *sizeUsed = nFileIndex;
587 return nFileCount;
588 }
589
590 /***********************************************************************
591 * ArrangeCtrlPositions [internal]
592 *
593 * NOTE: Make sure to add testcases for any changes made here.
594 */
595 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
596 {
597 HWND hwndChild, hwndStc32;
598 RECT rectParent, rectChild, rectStc32;
599 INT help_fixup = 0;
600 int chgx, chgy;
601
602 /* Take into account if open as read only checkbox and help button
603 * are hidden
604 */
605 if (hide_help)
606 {
607 RECT rectHelp, rectCancel;
608 GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
609 GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
610 /* subtract the height of the help button plus the space between
611 * the help button and the cancel button to the height of the dialog
612 */
613 help_fixup = rectHelp.bottom - rectCancel.bottom;
614 }
615
616 /*
617 There are two possibilities to add components to the default file dialog box.
618
619 By default, all the new components are added below the standard dialog box (the else case).
620
621 However, if there is a static text component with the stc32 id, a special case happens.
622 The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
623 in the window and the cx and cy indicate how to size the window.
624 Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
625 of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
626
627 */
628
629 GetClientRect(hwndParentDlg, &rectParent);
630
631 /* when arranging controls we have to use fixed parent size */
632 rectParent.bottom -= help_fixup;
633
634 hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
635 if (hwndStc32)
636 {
637 GetWindowRect(hwndStc32, &rectStc32);
638 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
639
640 /* set the size of the stc32 control according to the size of
641 * client area of the parent dialog
642 */
643 SetWindowPos(hwndStc32, 0,
644 0, 0,
645 rectParent.right, rectParent.bottom,
646 SWP_NOMOVE | SWP_NOZORDER);
647 }
648 else
649 SetRectEmpty(&rectStc32);
650
651 /* this part moves controls of the child dialog */
652 hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
653 while (hwndChild)
654 {
655 if (hwndChild != hwndStc32)
656 {
657 GetWindowRect(hwndChild, &rectChild);
658 MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
659
660 /* move only if stc32 exist */
661 if (hwndStc32 && rectChild.left > rectStc32.right)
662 {
663 /* move to the right of visible controls of the parent dialog */
664 rectChild.left += rectParent.right;
665 rectChild.left -= rectStc32.right;
666 }
667 /* move even if stc32 doesn't exist */
668 if (rectChild.top >= rectStc32.bottom)
669 {
670 /* move below visible controls of the parent dialog */
671 rectChild.top += rectParent.bottom;
672 rectChild.top -= rectStc32.bottom - rectStc32.top;
673 }
674
675 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
676 0, 0, SWP_NOSIZE | SWP_NOZORDER);
677 }
678 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
679 }
680
681 /* this part moves controls of the parent dialog */
682 hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
683 while (hwndChild)
684 {
685 if (hwndChild != hwndChildDlg)
686 {
687 GetWindowRect(hwndChild, &rectChild);
688 MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
689
690 /* left,top of stc32 marks the position of controls
691 * from the parent dialog
692 */
693 rectChild.left += rectStc32.left;
694 rectChild.top += rectStc32.top;
695
696 SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
697 0, 0, SWP_NOSIZE | SWP_NOZORDER);
698 }
699 hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
700 }
701
702 /* calculate the size of the resulting dialog */
703
704 /* here we have to use original parent size */
705 GetClientRect(hwndParentDlg, &rectParent);
706 GetClientRect(hwndChildDlg, &rectChild);
707 TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
708 wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
709
710 if (hwndStc32)
711 {
712 /* width */
713 if (rectParent.right > rectStc32.right - rectStc32.left)
714 chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
715 else
716 chgx = rectChild.right - rectParent.right;
717 /* height */
718 if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
719 chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
720 else
721 /* Unconditionally set new dialog
722 * height to that of the child
723 */
724 chgy = rectChild.bottom - rectParent.bottom;
725 }
726 else
727 {
728 chgx = 0;
729 chgy = rectChild.bottom - help_fixup;
730 }
731 /* set the size of the parent dialog */
732 GetWindowRect(hwndParentDlg, &rectParent);
733 SetWindowPos(hwndParentDlg, 0,
734 0, 0,
735 rectParent.right - rectParent.left + chgx,
736 rectParent.bottom - rectParent.top + chgy,
737 SWP_NOMOVE | SWP_NOZORDER);
738 }
739
740 static INT_PTR CALLBACK FileOpenDlgProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
741 {
742 switch(uMsg) {
743 case WM_INITDIALOG:
744 return TRUE;
745 }
746 return FALSE;
747 }
748
749 static HWND CreateTemplateDialog(FileOpenDlgInfos *fodInfos, HWND hwnd)
750 {
751 LPCVOID template;
752 HRSRC hRes;
753 HANDLE hDlgTmpl = 0;
754 HWND hChildDlg = 0;
755
756 TRACE("%p, %p\n", fodInfos, hwnd);
757
758 /*
759 * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
760 * structure's hInstance parameter is not a HINSTANCE, but
761 * instead a pointer to a template resource to use.
762 */
763 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
764 {
765 HINSTANCE hinst;
766 if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
767 {
768 hinst = COMDLG32_hInstance;
769 if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
770 {
771 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
772 return NULL;
773 }
774 }
775 else
776 {
777 hinst = fodInfos->ofnInfos->hInstance;
778 if(fodInfos->unicode)
779 {
780 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
781 hRes = FindResourceW( hinst, ofn->lpTemplateName, (LPWSTR)RT_DIALOG);
782 }
783 else
784 {
785 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
786 hRes = FindResourceA( hinst, ofn->lpTemplateName, (LPSTR)RT_DIALOG);
787 }
788 if (!hRes)
789 {
790 COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
791 return NULL;
792 }
793 if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
794 !(template = LockResource( hDlgTmpl )))
795 {
796 COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
797 return NULL;
798 }
799 }
800 if (fodInfos->unicode)
801 hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
802 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
803 (LPARAM)fodInfos->ofnInfos);
804 else
805 hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
806 IsHooked(fodInfos) ? (DLGPROC)fodInfos->ofnInfos->lpfnHook : FileOpenDlgProcUserTemplate,
807 (LPARAM)fodInfos->ofnInfos);
808 return hChildDlg;
809 }
810 else if( IsHooked(fodInfos))
811 {
812 RECT rectHwnd;
813 struct {
814 DLGTEMPLATE tmplate;
815 WORD menu,class,title;
816 } temp;
817 GetClientRect(hwnd,&rectHwnd);
818 temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
819 temp.tmplate.dwExtendedStyle = 0;
820 temp.tmplate.cdit = 0;
821 temp.tmplate.x = 0;
822 temp.tmplate.y = 0;
823 temp.tmplate.cx = 0;
824 temp.tmplate.cy = 0;
825 temp.menu = temp.class = temp.title = 0;
826
827 hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
828 hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
829
830 return hChildDlg;
831 }
832 return NULL;
833 }
834
835 /***********************************************************************
836 * SendCustomDlgNotificationMessage
837 *
838 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
839 */
840
841 LRESULT SendCustomDlgNotificationMessage(HWND hwndParentDlg, UINT uCode)
842 {
843 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwndParentDlg);
844 LRESULT hook_result;
845 OFNOTIFYW ofnNotify;
846
847 TRACE("%p %d\n", hwndParentDlg, uCode);
848
849 if (!fodInfos || !fodInfos->DlgInfos.hwndCustomDlg)
850 return 0;
851
852 TRACE("CALL NOTIFY for %d\n", uCode);
853
854 ofnNotify.hdr.hwndFrom = hwndParentDlg;
855 ofnNotify.hdr.idFrom = 0;
856 ofnNotify.hdr.code = uCode;
857 ofnNotify.lpOFN = fodInfos->ofnInfos;
858 ofnNotify.pszFile = NULL;
859
860 if (fodInfos->unicode)
861 hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
862 else
863 hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
864
865 TRACE("RET NOTIFY retval %#lx\n", hook_result);
866
867 return hook_result;
868 }
869
870 static INT_PTR FILEDLG95_Handle_GetFilePath(HWND hwnd, DWORD size, LPVOID result)
871 {
872 UINT len, total;
873 WCHAR *p, *buffer;
874 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
875
876 TRACE("CDM_GETFILEPATH:\n");
877
878 if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
879 return -1;
880
881 /* get path and filenames */
882 len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
883 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 2 + MAX_PATH) * sizeof(WCHAR) );
884 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
885 if (len)
886 {
887 p = buffer + strlenW(buffer);
888 *p++ = '\\';
889 SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
890 }
891 if (fodInfos->unicode)
892 {
893 total = strlenW( buffer) + 1;
894 if (result) lstrcpynW( result, buffer, size );
895 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
896 }
897 else
898 {
899 total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
900 if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
901 TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
902 }
903 HeapFree( GetProcessHeap(), 0, buffer );
904 return total;
905 }
906
907 /***********************************************************************
908 * FILEDLG95_HandleCustomDialogMessages
909 *
910 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
911 */
912 static INT_PTR FILEDLG95_HandleCustomDialogMessages(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
913 {
914 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
915 WCHAR lpstrPath[MAX_PATH];
916 INT_PTR retval;
917
918 if(!fodInfos) return FALSE;
919
920 switch(uMsg)
921 {
922 case CDM_GETFILEPATH:
923 retval = FILEDLG95_Handle_GetFilePath(hwnd, (UINT)wParam, (LPVOID)lParam);
924 break;
925
926 case CDM_GETFOLDERPATH:
927 TRACE("CDM_GETFOLDERPATH:\n");
928 COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
929 if (lParam)
930 {
931 if (fodInfos->unicode)
932 lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
933 else
934 WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
935 (LPSTR)lParam, (int)wParam, NULL, NULL);
936 }
937 retval = lstrlenW(lpstrPath) + 1;
938 break;
939
940 case CDM_GETFOLDERIDLIST:
941 retval = COMDLG32_PIDL_ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
942 if (retval <= wParam)
943 memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
944 break;
945
946 case CDM_GETSPEC:
947 TRACE("CDM_GETSPEC:\n");
948 retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
949 if (lParam)
950 {
951 if (fodInfos->unicode)
952 SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
953 else
954 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
955 }
956 break;
957
958 case CDM_SETCONTROLTEXT:
959 TRACE("CDM_SETCONTROLTEXT:\n");
960 if ( lParam )
961 {
962 if( fodInfos->unicode )
963 SetDlgItemTextW( hwnd, (UINT) wParam, (LPWSTR) lParam );
964 else
965 SetDlgItemTextA( hwnd, (UINT) wParam, (LPSTR) lParam );
966 }
967 retval = TRUE;
968 break;
969
970 case CDM_HIDECONTROL:
971 /* MSDN states that it should fail for not OFN_EXPLORER case */
972 if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
973 {
974 HWND control = GetDlgItem( hwnd, wParam );
975 if (control) ShowWindow( control, SW_HIDE );
976 retval = TRUE;
977 }
978 else retval = FALSE;
979 break;
980
981 default:
982 if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
983 FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
984 return FALSE;
985 }
986 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, retval);
987 return TRUE;
988 }
989
990 /***********************************************************************
991 * FILEDLG95_OnWMGetMMI
992 *
993 * WM_GETMINMAXINFO message handler for resizable dialogs
994 */
995 static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
996 {
997 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
998 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
999 if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1000 {
1001 mmiptr->ptMinTrackSize = fodInfos->initial_size;
1002 }
1003 return TRUE;
1004 }
1005
1006 /***********************************************************************
1007 * FILEDLG95_OnWMSize
1008 *
1009 * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1010 *
1011 * FIXME: this could be made more elaborate. Now use a simple scheme
1012 * where the file view is enlarged and the controls are either moved
1013 * vertically or horizontally to get out of the way. Only the "grip"
1014 * is moved in both directions to stay in the corner.
1015 */
1016 static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
1017 {
1018 RECT rc, rcview;
1019 int chgx, chgy;
1020 HWND ctrl;
1021 HDWP hdwp;
1022 FileOpenDlgInfos *fodInfos;
1023
1024 if( wParam != SIZE_RESTORED) return FALSE;
1025 fodInfos = get_filedlg_infoptr(hwnd);
1026 if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1027 /* get the new dialog rectangle */
1028 GetWindowRect( hwnd, &rc);
1029 TRACE("%p, size from %d,%d to %d,%d\n", hwnd, fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1030 rc.right -rc.left, rc.bottom -rc.top);
1031 /* not initialized yet */
1032 if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1033 ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1034 (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1035 return FALSE;
1036 chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1037 chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1038 fodInfos->sizedlg.cx = rc.right - rc.left;
1039 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1040 /* change the size of the view window */
1041 GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1042 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1043 hdwp = BeginDeferWindowPos( 10);
1044 DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1045 rcview.right - rcview.left + chgx,
1046 rcview.bottom - rcview.top + chgy,
1047 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1048 /* change position and sizes of the controls */
1049 for( ctrl = GetWindow( hwnd, GW_CHILD); ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1050 {
1051 int ctrlid = GetDlgCtrlID( ctrl);
1052 GetWindowRect( ctrl, &rc);
1053 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1054 if( ctrl == fodInfos->DlgInfos.hwndGrip)
1055 {
1056 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1057 0, 0,
1058 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1059 }
1060 else if( rc.top > rcview.bottom)
1061 {
1062 /* if it was below the shell view
1063 * move to bottom */
1064 switch( ctrlid)
1065 {
1066 /* file name (edit or comboboxex) and file types combo change also width */
1067 case edt1:
1068 case cmb13:
1069 case cmb1:
1070 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1071 rc.right - rc.left + chgx, rc.bottom - rc.top,
1072 SWP_NOACTIVATE | SWP_NOZORDER);
1073 break;
1074 /* then these buttons must move out of the way */
1075 case IDOK:
1076 case IDCANCEL:
1077 case pshHelp:
1078 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1079 0, 0,
1080 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1081 break;
1082 default:
1083 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1084 0, 0,
1085 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1086 }
1087 }
1088 else if( rc.left > rcview.right)
1089 {
1090 /* if it was to the right of the shell view
1091 * move to right */
1092 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1093 0, 0,
1094 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1095 }
1096 else
1097 /* special cases */
1098 {
1099 switch( ctrlid)
1100 {
1101 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1102 case IDC_LOOKIN:
1103 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1104 rc.right - rc.left + chgx, rc.bottom - rc.top,
1105 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1106 break;
1107 case IDC_TOOLBARSTATIC:
1108 case IDC_TOOLBAR:
1109 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1110 0, 0,
1111 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1112 break;
1113 #endif
1114 /* not resized in windows. Since wine uses this invisible control
1115 * to size the browser view it needs to be resized */
1116 case IDC_SHELLSTATIC:
1117 DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1118 rc.right - rc.left + chgx,
1119 rc.bottom - rc.top + chgy,
1120 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1121 break;
1122 }
1123 }
1124 }
1125 if(fodInfos->DlgInfos.hwndCustomDlg &&
1126 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
1127 {
1128 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1129 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT))
1130 {
1131 GetWindowRect( ctrl, &rc);
1132 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1133 if( rc.top > rcview.bottom)
1134 {
1135 /* if it was below the shell view
1136 * move to bottom */
1137 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1138 rc.right - rc.left, rc.bottom - rc.top,
1139 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1140 }
1141 else if( rc.left > rcview.right)
1142 {
1143 /* if it was to the right of the shell view
1144 * move to right */
1145 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1146 rc.right - rc.left, rc.bottom - rc.top,
1147 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1148 }
1149 }
1150 /* size the custom dialog at the end: some applications do some
1151 * control re-arranging at this point */
1152 GetClientRect(hwnd, &rc);
1153 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1154 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1155 }
1156 EndDeferWindowPos( hdwp);
1157 /* should not be needed */
1158 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE );
1159 return TRUE;
1160 }
1161
1162 /***********************************************************************
1163 * FileOpenDlgProc95
1164 *
1165 * File open dialog procedure
1166 */
1167 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1168 {
1169 #if 0
1170 TRACE("%p 0x%04x\n", hwnd, uMsg);
1171 #endif
1172
1173 switch(uMsg)
1174 {
1175 case WM_INITDIALOG:
1176 {
1177 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1178 RECT rc, rcstc;
1179 int gripx = GetSystemMetrics( SM_CYHSCROLL);
1180 int gripy = GetSystemMetrics( SM_CYVSCROLL);
1181
1182 /* Some shell namespace extensions depend on COM being initialized. */
1183 if (SUCCEEDED(OleInitialize(NULL)))
1184 fodInfos->ole_initialized = TRUE;
1185
1186 SetPropW(hwnd, filedlg_info_propnameW, fodInfos);
1187
1188 FILEDLG95_InitControls(hwnd);
1189
1190 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1191 {
1192 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1193 DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1194 RECT client, client_adjusted;
1195
1196 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1197 {
1198 style |= WS_SIZEBOX;
1199 ex_style |= WS_EX_WINDOWEDGE;
1200 }
1201 else
1202 style &= ~WS_SIZEBOX;
1203 SetWindowLongW(hwnd, GWL_STYLE, style);
1204 SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1205
1206 GetClientRect( hwnd, &client );
1207 GetClientRect( hwnd, &client_adjusted );
1208 AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1209
1210 GetWindowRect( hwnd, &rc );
1211 rc.right += client_adjusted.right - client.right;
1212 rc.bottom += client_adjusted.bottom - client.bottom;
1213 SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1214 SWP_NOZORDER | SWP_NOMOVE);
1215
1216 GetWindowRect( hwnd, &rc );
1217 fodInfos->DlgInfos.hwndGrip =
1218 CreateWindowExA( 0, "SCROLLBAR", NULL,
1219 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS |
1220 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
1221 rc.right - gripx, rc.bottom - gripy,
1222 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1223 }
1224
1225 fodInfos->DlgInfos.hwndCustomDlg =
1226 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd);
1227
1228 FILEDLG95_ResizeControls(hwnd, wParam, lParam);
1229 FILEDLG95_FillControls(hwnd, wParam, lParam);
1230
1231 if( fodInfos->DlgInfos.hwndCustomDlg)
1232 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1233
1234 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1235 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE);
1236 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
1237 }
1238
1239 /* if the app has changed the position of the invisible listbox,
1240 * change that of the listview (browser) as well */
1241 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1242 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc);
1243 if( !EqualRect( &rc, &rcstc))
1244 {
1245 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1246 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1247 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1248 SWP_NOACTIVATE | SWP_NOZORDER);
1249 }
1250
1251 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1252 {
1253 GetWindowRect( hwnd, &rc);
1254 fodInfos->sizedlg.cx = rc.right - rc.left;
1255 fodInfos->sizedlg.cy = rc.bottom - rc.top;
1256 fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1257 fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1258 GetClientRect( hwnd, &rc);
1259 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1260 rc.right - gripx, rc.bottom - gripy,
1261 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
1262 /* resize the dialog to the previous invocation */
1263 if( MemDialogSize.cx && MemDialogSize.cy)
1264 SetWindowPos( hwnd, NULL,
1265 0, 0, MemDialogSize.cx, MemDialogSize.cy,
1266 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1267 }
1268
1269 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1270 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE);
1271
1272 return 0;
1273 }
1274 case WM_SIZE:
1275 return FILEDLG95_OnWMSize(hwnd, wParam);
1276 case WM_GETMINMAXINFO:
1277 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
1278 case WM_COMMAND:
1279 return FILEDLG95_OnWMCommand(hwnd, wParam);
1280 case WM_DRAWITEM:
1281 {
1282 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1283 {
1284 case IDC_LOOKIN:
1285 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam);
1286 return TRUE;
1287 }
1288 }
1289 return FALSE;
1290
1291 case WM_GETISHELLBROWSER:
1292 return FILEDLG95_OnWMGetIShellBrowser(hwnd);
1293
1294 case WM_DESTROY:
1295 {
1296 FileOpenDlgInfos * fodInfos = get_filedlg_infoptr(hwnd);
1297 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1298 MemDialogSize = fodInfos->sizedlg;
1299 RemovePropW(hwnd, filedlg_info_propnameW);
1300 return FALSE;
1301 }
1302 case WM_NOTIFY:
1303 {
1304 LPNMHDR lpnmh = (LPNMHDR)lParam;
1305 UINT stringId = -1;
1306
1307 /* set up the button tooltips strings */
1308 if(TTN_GETDISPINFOA == lpnmh->code )
1309 {
1310 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam;
1311 switch(lpnmh->idFrom )
1312 {
1313 /* Up folder button */
1314 case FCIDM_TB_UPFOLDER:
1315 stringId = IDS_UPFOLDER;
1316 break;
1317 /* New folder button */
1318 case FCIDM_TB_NEWFOLDER:
1319 stringId = IDS_NEWFOLDER;
1320 break;
1321 /* List option button */
1322 case FCIDM_TB_SMALLICON:
1323 stringId = IDS_LISTVIEW;
1324 break;
1325 /* Details option button */
1326 case FCIDM_TB_REPORTVIEW:
1327 stringId = IDS_REPORTVIEW;
1328 break;
1329 /* Desktop button */
1330 case FCIDM_TB_DESKTOP:
1331 stringId = IDS_TODESKTOP;
1332 break;
1333 default:
1334 stringId = 0;
1335 }
1336 lpdi->hinst = COMDLG32_hInstance;
1337 lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1338 }
1339 return FALSE;
1340 }
1341 default :
1342 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1343 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam);
1344 return FALSE;
1345 }
1346 }
1347
1348 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info )
1349 {
1350 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1351 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE));
1352 }
1353
1354 /***********************************************************************
1355 * FILEDLG95_InitControls
1356 *
1357 * WM_INITDIALOG message handler (before hook notification)
1358 */
1359 static LRESULT FILEDLG95_InitControls(HWND hwnd)
1360 {
1361 BOOL win2000plus = FALSE;
1362 BOOL win98plus = FALSE;
1363 BOOL handledPath = FALSE;
1364 OSVERSIONINFOW osVi;
1365 static const WCHAR szwSlash[] = { '\\', 0 };
1366 static const WCHAR szwStar[] = { '*',0 };
1367
1368 static const TBBUTTON tbb[] =
1369 {
1370 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1371 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1372 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1373 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1374 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1375 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1376 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1377 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1378 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 },
1379 };
1380 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1381
1382 RECT rectTB;
1383 RECT rectlook;
1384
1385 HIMAGELIST toolbarImageList;
1386 SHFILEINFOA shFileInfo;
1387 ITEMIDLIST *desktopPidl;
1388
1389 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1390
1391 TRACE("%p\n", fodInfos);
1392
1393 /* Get windows version emulating */
1394 osVi.dwOSVersionInfoSize = sizeof(osVi);
1395 GetVersionExW(&osVi);
1396 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1397 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1398 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1399 win2000plus = (osVi.dwMajorVersion > 4);
1400 if (win2000plus) win98plus = TRUE;
1401 }
1402 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1403
1404
1405 /* Use either the edit or the comboboxex for the filename control */
1406 if (filename_is_edit( fodInfos ))
1407 {
1408 DestroyWindow( GetDlgItem( hwnd, cmb13 ) );
1409 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1410 }
1411 else
1412 {
1413 DestroyWindow( GetDlgItem( hwnd, edt1 ) );
1414 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1415 }
1416
1417 /* Get the hwnd of the controls */
1418 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1419 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1420
1421 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1422 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1423
1424 /* construct the toolbar */
1425 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB);
1426 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1427
1428 rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1429 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1430 rectTB.left = rectlook.right;
1431 rectTB.top = rectlook.top-1;
1432
1433 if (fodInfos->unicode)
1434 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1435 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1436 rectTB.left, rectTB.top,
1437 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1438 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1439 else
1440 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1441 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | CCS_NODIVIDER | CCS_NORESIZE,
1442 rectTB.left, rectTB.top,
1443 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1444 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL);
1445
1446 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1447
1448 /* FIXME: use TB_LOADIMAGES when implemented */
1449 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1450 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1451 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1452
1453 /* Retrieve and add desktop icon to the toolbar */
1454 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1455 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl);
1456 SHGetFileInfoA((LPCSTR)desktopPidl, 0, &shFileInfo, sizeof(shFileInfo),
1457 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
1458 ImageList_AddIcon(toolbarImageList, shFileInfo.hIcon);
1459
1460 DestroyIcon(shFileInfo.hIcon);
1461 CoTaskMemFree(desktopPidl);
1462
1463 /* Finish Toolbar Construction */
1464 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1465 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1466
1467 /* Set the window text with the text specified in the OPENFILENAME structure */
1468 if(fodInfos->title)
1469 {
1470 SetWindowTextW(hwnd,fodInfos->title);
1471 }
1472 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1473 {
1474 WCHAR buf[64];
1475 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, sizeof(buf)/sizeof(WCHAR));
1476 SetWindowTextW(hwnd, buf);
1477 }
1478
1479 /* Initialise the file name edit control */
1480 handledPath = FALSE;
1481 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1482
1483 if(fodInfos->filename)
1484 {
1485 /* 1. If win2000 or higher and filename contains a path, use it
1486 in preference over the lpstrInitialDir */
1487 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) {
1488 WCHAR tmpBuf[MAX_PATH];
1489 WCHAR *nameBit;
1490 DWORD result;
1491
1492 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1493 if (result) {
1494
1495 /* nameBit is always shorter than the original filename. It may be NULL
1496 * when the filename contains only a drive name instead of file name */
1497 if (nameBit)
1498 {
1499 lstrcpyW(fodInfos->filename,nameBit);
1500 *nameBit = 0x00;
1501 }
1502 else
1503 *fodInfos->filename = '\0';
1504
1505 MemFree(fodInfos->initdir);
1506 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1507 lstrcpyW(fodInfos->initdir, tmpBuf);
1508 handledPath = TRUE;
1509 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1510 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1511 }
1512 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1513
1514 } else {
1515 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1516 }
1517 }
1518
1519 /* 2. (All platforms) If initdir is not null, then use it */
1520 if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1521 {
1522 /* Work out the proper path as supplied one might be relative */
1523 /* (Here because supplying '.' as dir browses to My Computer) */
1524 WCHAR tmpBuf[MAX_PATH];
1525 WCHAR tmpBuf2[MAX_PATH];
1526 WCHAR *nameBit;
1527 DWORD result;
1528
1529 lstrcpyW(tmpBuf, fodInfos->initdir);
1530 if (PathFileExistsW(tmpBuf)) {
1531 /* initdir does not have to be a directory. If a file is
1532 * specified, the dir part is taken */
1533 if (PathIsDirectoryW(tmpBuf)) {
1534 PathAddBackslashW(tmpBuf);
1535 lstrcatW(tmpBuf, szwStar);
1536 }
1537 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1538 if (result) {
1539 *nameBit = 0x00;
1540 MemFree(fodInfos->initdir);
1541 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1542 lstrcpyW(fodInfos->initdir, tmpBuf2);
1543 handledPath = TRUE;
1544 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1545 }
1546 }
1547 else if (fodInfos->initdir)
1548 {
1549 MemFree(fodInfos->initdir);
1550 fodInfos->initdir = NULL;
1551 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1552 }
1553 }
1554
1555 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1556 {
1557 /* 3. All except w2k+: if filename contains a path use it */
1558 if (!win2000plus && fodInfos->filename &&
1559 *fodInfos->filename &&
1560 strpbrkW(fodInfos->filename, szwSlash)) {
1561 WCHAR tmpBuf[MAX_PATH];
1562 WCHAR *nameBit;
1563 DWORD result;
1564
1565 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1566 tmpBuf, &nameBit);
1567 if (result) {
1568 int len;
1569
1570 /* nameBit is always shorter than the original filename */
1571 lstrcpyW(fodInfos->filename, nameBit);
1572 *nameBit = 0x00;
1573
1574 len = lstrlenW(tmpBuf);
1575 MemFree(fodInfos->initdir);
1576 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1577 lstrcpyW(fodInfos->initdir, tmpBuf);
1578
1579 handledPath = TRUE;
1580 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1581 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1582 }
1583 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1584 }
1585
1586 /* 4. Win2000+: Recently used */
1587 if (!handledPath && win2000plus) {
1588 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1589 fodInfos->initdir[0] = '\0';
1590
1591 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1592
1593 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1594 handledPath = TRUE;
1595 }else{
1596 MemFree(fodInfos->initdir);
1597 fodInfos->initdir = NULL;
1598 }
1599 }
1600
1601 /* 5. win98+ and win2000+ if any files of specified filter types in
1602 current directory, use it */
1603 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1604
1605 LPCWSTR lpstrPos = fodInfos->filter;
1606 WIN32_FIND_DATAW FindFileData;
1607 HANDLE hFind;
1608
1609 while (1)
1610 {
1611 /* filter is a list... title\0ext\0......\0\0 */
1612
1613 /* Skip the title */
1614 if(! *lpstrPos) break; /* end */
1615 lpstrPos += lstrlenW(lpstrPos) + 1;
1616
1617 /* See if any files exist in the current dir with this extension */
1618 if(! *lpstrPos) break; /* end */
1619
1620 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1621
1622 if (hFind == INVALID_HANDLE_VALUE) {
1623 /* None found - continue search */
1624 lpstrPos += lstrlenW(lpstrPos) + 1;
1625
1626 } else {
1627
1628 MemFree(fodInfos->initdir);
1629 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1630 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1631
1632 handledPath = TRUE;
1633 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1634 debugstr_w(lpstrPos));
1635 FindClose(hFind);
1636 break;
1637 }
1638 }
1639 }
1640
1641 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1642 if (!handledPath && (win2000plus || win98plus)) {
1643 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1644
1645 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1646 {
1647 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1648 {
1649 /* last fallback */
1650 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1651 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1652 } else {
1653 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1654 }
1655 } else {
1656 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1657 }
1658 handledPath = TRUE;
1659 } else if (!handledPath) {
1660 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1661 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1662 handledPath = TRUE;
1663 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1664 }
1665 }
1666 SetFocus( fodInfos->DlgInfos.hwndFileName );
1667 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1668
1669 /* Must the open as read only check box be checked ?*/
1670 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1671 {
1672 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1673 }
1674
1675 /* Must the open as read only check box be hidden? */
1676 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1677 {
1678 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1679 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1680 }
1681
1682 /* Must the help button be hidden? */
1683 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1684 {
1685 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1686 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1687 }
1688
1689 /* change Open to Save */
1690 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1691 {
1692 WCHAR buf[16];
1693 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1694 SetDlgItemTextW(hwnd, IDOK, buf);
1695 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1696 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1697 }
1698
1699 /* Initialize the filter combo box */
1700 FILEDLG95_FILETYPE_Init(hwnd);
1701
1702 return 0;
1703 }
1704
1705 /***********************************************************************
1706 * FILEDLG95_ResizeControls
1707 *
1708 * WM_INITDIALOG message handler (after hook notification)
1709 */
1710 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1711 {
1712 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1713
1714 if (fodInfos->DlgInfos.hwndCustomDlg)
1715 {
1716 RECT rc;
1717 UINT flags = SWP_NOACTIVATE;
1718
1719 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1720 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1721
1722 /* resize the custom dialog to the parent size */
1723 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1724 GetClientRect(hwnd, &rc);
1725 else
1726 {
1727 /* our own fake template is zero sized and doesn't have children, so
1728 * there is no need to resize it. Picasa depends on it.
1729 */
1730 flags |= SWP_NOSIZE;
1731 SetRectEmpty(&rc);
1732 }
1733 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1734 0, 0, rc.right, rc.bottom, flags);
1735 }
1736 else
1737 {
1738 /* Resize the height; if opened as read-only, checkbox and help button are
1739 * hidden and we are not using a custom template nor a customDialog
1740 */
1741 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1742 (!(fodInfos->ofnInfos->Flags &
1743 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1744 {
1745 RECT rectDlg, rectHelp, rectCancel;
1746 GetWindowRect(hwnd, &rectDlg);
1747 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1748 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1749 /* subtract the height of the help button plus the space between the help
1750 * button and the cancel button to the height of the dialog
1751 */
1752 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1753 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1754 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1755 }
1756 }
1757 return TRUE;
1758 }
1759
1760 /***********************************************************************
1761 * FILEDLG95_FillControls
1762 *
1763 * WM_INITDIALOG message handler (after hook notification)
1764 */
1765 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1766 {
1767 LPITEMIDLIST pidlItemId = NULL;
1768
1769 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1770
1771 TRACE("dir=%s file=%s\n",
1772 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1773
1774 /* Get the initial directory pidl */
1775
1776 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1777 {
1778 WCHAR path[MAX_PATH];
1779
1780 GetCurrentDirectoryW(MAX_PATH,path);
1781 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1782 }
1783
1784 /* Initialise shell objects */
1785 FILEDLG95_SHELL_Init(hwnd);
1786
1787 /* Initialize the Look In combo box */
1788 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1789
1790 /* Browse to the initial directory */
1791 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1792
1793 /* Free pidlItem memory */
1794 COMDLG32_SHFree(pidlItemId);
1795
1796 return TRUE;
1797 }
1798 /***********************************************************************
1799 * FILEDLG95_Clean
1800 *
1801 * Regroups all the cleaning functions of the filedlg
1802 */
1803 void FILEDLG95_Clean(HWND hwnd)
1804 {
1805 FILEDLG95_FILETYPE_Clean(hwnd);
1806 FILEDLG95_LOOKIN_Clean(hwnd);
1807 FILEDLG95_SHELL_Clean(hwnd);
1808 }
1809 /***********************************************************************
1810 * FILEDLG95_OnWMCommand
1811 *
1812 * WM_COMMAND message handler
1813 */
1814 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1815 {
1816 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1817 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1818 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1819
1820 switch(wID)
1821 {
1822 /* OK button */
1823 case IDOK:
1824 FILEDLG95_OnOpen(hwnd);
1825 break;
1826 /* Cancel button */
1827 case IDCANCEL:
1828 FILEDLG95_Clean(hwnd);
1829 EndDialog(hwnd, FALSE);
1830 break;
1831 /* Filetype combo box */
1832 case IDC_FILETYPE:
1833 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1834 break;
1835 /* LookIn combo box */
1836 case IDC_LOOKIN:
1837 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1838 break;
1839
1840 /* --- toolbar --- */
1841 /* Up folder button */
1842 case FCIDM_TB_UPFOLDER:
1843 FILEDLG95_SHELL_UpFolder(hwnd);
1844 break;
1845 /* New folder button */
1846 case FCIDM_TB_NEWFOLDER:
1847 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1848 break;
1849 /* List option button */
1850 case FCIDM_TB_SMALLICON:
1851 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1852 break;
1853 /* Details option button */
1854 case FCIDM_TB_REPORTVIEW:
1855 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1856 break;
1857 /* Details option button */
1858 case FCIDM_TB_DESKTOP:
1859 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1860 break;
1861
1862 case edt1:
1863 case cmb13:
1864 break;
1865
1866 }
1867 /* Do not use the listview selection anymore */
1868 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1869 return 0;
1870 }
1871
1872 /***********************************************************************
1873 * FILEDLG95_OnWMGetIShellBrowser
1874 *
1875 * WM_GETISHELLBROWSER message handler
1876 */
1877 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1878 {
1879 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1880
1881 TRACE("\n");
1882
1883 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1884
1885 return TRUE;
1886 }
1887
1888
1889 /***********************************************************************
1890 * FILEDLG95_SendFileOK
1891 *
1892 * Sends the CDN_FILEOK notification if required
1893 *
1894 * RETURNS
1895 * TRUE if the dialog should close
1896 * FALSE if the dialog should not be closed
1897 */
1898 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1899 {
1900 /* ask the hook if we can close */
1901 if(IsHooked(fodInfos))
1902 {
1903 LRESULT retval = 0;
1904
1905 TRACE("---\n");
1906 /* First send CDN_FILEOK as MSDN doc says */
1907 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1908 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1909 if( retval)
1910 {
1911 TRACE("canceled\n");
1912 return FALSE;
1913 }
1914
1915 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1916 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1917 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1918 if( retval)
1919 {
1920 TRACE("canceled\n");
1921 return FALSE;
1922 }
1923 }
1924 return TRUE;
1925 }
1926
1927 /***********************************************************************
1928 * FILEDLG95_OnOpenMultipleFiles
1929 *
1930 * Handles the opening of multiple files.
1931 *
1932 * FIXME
1933 * check destination buffer size
1934 */
1935 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
1936 {
1937 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
1938 WCHAR lpstrPathSpec[MAX_PATH] = {0};
1939 UINT nCount, nSizePath;
1940
1941 TRACE("\n");
1942
1943 if(fodInfos->unicode)
1944 {
1945 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1946 ofn->lpstrFile[0] = '\0';
1947 }
1948 else
1949 {
1950 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
1951 ofn->lpstrFile[0] = '\0';
1952 }
1953
1954 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
1955
1956 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
1957 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
1958 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
1959 {
1960 LPWSTR lpstrTemp = lpstrFileList;
1961
1962 for ( nCount = 0; nCount < nFileCount; nCount++ )
1963 {
1964 LPITEMIDLIST pidl;
1965
1966 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
1967 if (!pidl)
1968 {
1969 WCHAR lpstrNotFound[100];
1970 WCHAR lpstrMsg[100];
1971 WCHAR tmp[400];
1972 static const WCHAR nl[] = {'\n',0};
1973
1974 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
1975 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
1976
1977 lstrcpyW(tmp, lpstrTemp);
1978 lstrcatW(tmp, nl);
1979 lstrcatW(tmp, lpstrNotFound);
1980 lstrcatW(tmp, nl);
1981 lstrcatW(tmp, lpstrMsg);
1982
1983 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
1984 return FALSE;
1985 }
1986
1987 /* move to the next file in the list of files */
1988 lpstrTemp += lstrlenW(lpstrTemp) + 1;
1989 COMDLG32_SHFree(pidl);
1990 }
1991 }
1992
1993 nSizePath = lstrlenW(lpstrPathSpec) + 1;
1994 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
1995 {
1996 /* For "oldstyle" dialog the components have to
1997 be separated by blanks (not '\0'!) and short
1998 filenames have to be used! */
1999 FIXME("Components have to be separated by blanks\n");
2000 }
2001 if(fodInfos->unicode)
2002 {
2003 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2004 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2005 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2006 }
2007 else
2008 {
2009 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2010
2011 if (ofn->lpstrFile != NULL)
2012 {
2013 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2014 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2015 if (ofn->nMaxFile > nSizePath)
2016 {
2017 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2018 ofn->lpstrFile + nSizePath,
2019 ofn->nMaxFile - nSizePath, NULL, NULL);
2020 }
2021 }
2022 }
2023
2024 fodInfos->ofnInfos->nFileOffset = nSizePath;
2025 fodInfos->ofnInfos->nFileExtension = 0;
2026
2027 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2028 return FALSE;
2029
2030 /* clean and exit */
2031 FILEDLG95_Clean(hwnd);
2032 return EndDialog(hwnd,TRUE);
2033 }
2034
2035 /* Returns the 'slot name' of the given module_name in the registry's
2036 * most-recently-used list. This will be an ASCII value in the
2037 * range ['a','z'). Returns zero on error.
2038 *
2039 * The slot's value in the registry has the form:
2040 * module_name\0mru_path\0
2041 *
2042 * If stored_path is given, then stored_path will contain the path name
2043 * stored in the registry's MRU list for the given module_name.
2044 *
2045 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2046 * MRU list key for the given module_name.
2047 */
2048 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2049 {
2050 WCHAR mru_list[32], *cur_mru_slot;
2051 BOOL taken[25] = {0};
2052 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2053 HKEY hkey_tmp, *hkey;
2054 LONG ret;
2055
2056 if(hkey_ret)
2057 hkey = hkey_ret;
2058 else
2059 hkey = &hkey_tmp;
2060
2061 if(stored_path)
2062 *stored_path = '\0';
2063
2064 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2065 if(ret){
2066 WARN("Unable to create MRU key: %d\n", ret);
2067 return 0;
2068 }
2069
2070 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2071 (LPBYTE)mru_list, &mru_list_size);
2072 if(ret || key_type != REG_SZ){
2073 if(ret == ERROR_FILE_NOT_FOUND)
2074 return 'a';
2075
2076 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2077 RegCloseKey(*hkey);
2078 return 0;
2079 }
2080
2081 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2082 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2083 DWORD value_data_size = sizeof(value_data);
2084
2085 *value_name = *cur_mru_slot;
2086
2087 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2088 &key_type, (LPBYTE)value_data, &value_data_size);
2089 if(ret || key_type != REG_BINARY){
2090 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2091 continue;
2092 }
2093
2094 if(!strcmpiW(module_name, value_data)){
2095 if(!hkey_ret)
2096 RegCloseKey(*hkey);
2097 if(stored_path)
2098 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2099 return *value_name;
2100 }
2101 }
2102
2103 if(!hkey_ret)
2104 RegCloseKey(*hkey);
2105
2106 /* the module name isn't in the registry, so find the next open slot */
2107 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2108 taken[*cur_mru_slot - 'a'] = TRUE;
2109 for(i = 0; i < 25; ++i){
2110 if(!taken[i])
2111 return i + 'a';
2112 }
2113
2114 /* all slots are taken, so return the last one in MRUList */
2115 --cur_mru_slot;
2116 return *cur_mru_slot;
2117 }
2118
2119 /* save the given filename as most-recently-used path for this module */
2120 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2121 {
2122 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2123 LONG ret;
2124 HKEY hkey;
2125
2126 /* get the current executable's name */
2127 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2128 WARN("GotModuleFileName failed: %d\n", GetLastError());
2129 return;
2130 }
2131 module_name = strrchrW(module_path, '\\');
2132 if(!module_name)
2133 module_name = module_path;
2134 else
2135 module_name += 1;
2136
2137 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2138 if(!slot)
2139 return;
2140 *slot_name = slot;
2141
2142 { /* update the slot's info */
2143 WCHAR *path_ends, *final;
2144 DWORD path_len, final_len;
2145
2146 /* use only the path segment of `filename' */
2147 path_ends = strrchrW(filename, '\\');
2148 path_len = path_ends - filename;
2149
2150 final_len = path_len + lstrlenW(module_name) + 2;
2151
2152 final = MemAlloc(final_len * sizeof(WCHAR));
2153 if(!final)
2154 return;
2155 lstrcpyW(final, module_name);
2156 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2157 final[final_len-1] = '\0';
2158
2159 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2160 final_len * sizeof(WCHAR));
2161 if(ret){
2162 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2163 MemFree(final);
2164 RegCloseKey(hkey);
2165 return;
2166 }
2167
2168 MemFree(final);
2169 }
2170
2171 { /* update MRUList value */
2172 WCHAR old_mru_list[32], new_mru_list[32];
2173 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2174 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2175
2176 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2177 (LPBYTE)old_mru_list, &mru_list_size);
2178 if(ret || key_type != REG_SZ){
2179 if(ret == ERROR_FILE_NOT_FOUND){
2180 new_mru_list[0] = slot;
2181 new_mru_list[1] = '\0';
2182 }else{
2183 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2184 RegCloseKey(hkey);
2185 return;
2186 }
2187 }else{
2188 /* copy old list data over so that the new slot is at the start
2189 * of the list */
2190 *new_mru_slot++ = slot;
2191 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2192 if(*old_mru_slot != slot)
2193 *new_mru_slot++ = *old_mru_slot;
2194 }
2195 *new_mru_slot = '\0';
2196 }
2197
2198 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2199 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2200 if(ret){
2201 WARN("Error saving MRUList data: %d\n", ret);
2202 RegCloseKey(hkey);
2203 return;
2204 }
2205 }
2206 }
2207
2208 /* load the most-recently-used path for this module */
2209 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2210 {
2211 WCHAR module_path[MAX_PATH], *module_name;
2212
2213 /* get the current executable's name */
2214 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2215 WARN("GotModuleFileName failed: %d\n", GetLastError());
2216 return;
2217 }
2218 module_name = strrchrW(module_path, '\\');
2219 if(!module_name)
2220 module_name = module_path;
2221 else
2222 module_name += 1;
2223
2224 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2225 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2226 }
2227
2228 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2229 {
2230 WCHAR strMsgTitle[MAX_PATH];
2231 WCHAR strMsgText [MAX_PATH];
2232 if (idCaption)
2233 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2234 else
2235 strMsgTitle[0] = '\0';
2236 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2237 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2238 }
2239
2240 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2241 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2242 {
2243 int nOpenAction = defAction;
2244 LPWSTR lpszTemp, lpszTemp1;
2245 LPITEMIDLIST pidl = NULL;
2246 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2247
2248 /* check for invalid chars */
2249 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2250 {
2251 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2252 return FALSE;
2253 }
2254
2255 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2256
2257 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2258 while (lpszTemp1)
2259 {
2260 LPSHELLFOLDER lpsfChild;
2261 WCHAR lpwstrTemp[MAX_PATH];
2262 DWORD dwEaten, dwAttributes;
2263 LPWSTR p;
2264
2265 lstrcpyW(lpwstrTemp, lpszTemp);
2266 p = PathFindNextComponentW(lpwstrTemp);
2267
2268 if (!p) break; /* end of path */
2269
2270 *p = 0;
2271 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2272
2273 /* There are no wildcards when OFN_NOVALIDATE is set */
2274 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2275 {
2276 static const WCHAR wszWild[] = { '*', '?', 0 };
2277 /* if the last element is a wildcard do a search */
2278 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2279 {
2280 nOpenAction = ONOPEN_SEARCH;
2281 break;
2282 }
2283 }
2284 lpszTemp1 = lpszTemp;
2285
2286 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2287
2288 /* append a backslash to drive letters */
2289 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2290 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2291 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2292 {
2293 PathAddBackslashW(lpwstrTemp);
2294 }
2295
2296 dwAttributes = SFGAO_FOLDER;
2297 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2298 {
2299 /* the path component is valid, we have a pidl of the next path component */
2300 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2301 if(dwAttributes & SFGAO_FOLDER)
2302 {
2303 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2304 {
2305 ERR("bind to failed\n"); /* should not fail */
2306 break;
2307 }
2308 IShellFolder_Release(*ppsf);
2309 *ppsf = lpsfChild;
2310 lpsfChild = NULL;
2311 }
2312 else
2313 {
2314 TRACE("value\n");
2315
2316 /* end dialog, return value */
2317 nOpenAction = ONOPEN_OPEN;
2318 break;
2319 }
2320 COMDLG32_SHFree(pidl);
2321 pidl = NULL;
2322 }
2323 else if (!(flags & OFN_NOVALIDATE))
2324 {
2325 if(*lpszTemp || /* points to trailing null for last path element */
2326 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2327 {
2328 if(flags & OFN_PATHMUSTEXIST)
2329 {
2330 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2331 break;
2332 }
2333 }
2334 else
2335 {
2336 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2337 {
2338 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2339 break;
2340 }
2341 }
2342 /* change to the current folder */
2343 nOpenAction = ONOPEN_OPEN;
2344 break;
2345 }
2346 else
2347 {
2348 nOpenAction = ONOPEN_OPEN;
2349 break;
2350 }
2351 }
2352 if(pidl) COMDLG32_SHFree(pidl);
2353
2354 return nOpenAction;
2355 }
2356
2357 /***********************************************************************
2358 * FILEDLG95_OnOpen
2359 *
2360 * Ok button WM_COMMAND message handler
2361 *
2362 * If the function succeeds, the return value is nonzero.
2363 */
2364 BOOL FILEDLG95_OnOpen(HWND hwnd)
2365 {
2366 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd);
2367 LPWSTR lpstrFileList;
2368 UINT nFileCount = 0;
2369 UINT sizeUsed = 0;
2370 BOOL ret = TRUE;
2371 WCHAR lpstrPathAndFile[MAX_PATH];
2372 LPSHELLFOLDER lpsf = NULL;
2373 int nOpenAction;
2374
2375 TRACE("hwnd=%p\n", hwnd);
2376
2377 /* try to browse the selected item */
2378 if(BrowseSelectedFolder(hwnd))
2379 return FALSE;
2380
2381 /* get the files from the edit control */
2382 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2383
2384 if(nFileCount == 0)
2385 return FALSE;
2386
2387 if(nFileCount > 1)
2388 {
2389 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2390 goto ret;
2391 }
2392
2393 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2394
2395 /*
2396 Step 1: Build a complete path name from the current folder and
2397 the filename or path in the edit box.
2398 Special cases:
2399 - the path in the edit box is a root path
2400 (with or without drive letter)
2401 - the edit box contains ".." (or a path with ".." in it)
2402 */
2403
2404 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2405 MemFree(lpstrFileList);
2406
2407 /*
2408 Step 2: here we have a cleaned up path
2409
2410 We have to parse the path step by step to see if we have to browse
2411 to a folder if the path points to a directory or the last
2412 valid element is a directory.
2413
2414 valid variables:
2415 lpstrPathAndFile: cleaned up path
2416 */
2417
2418 if (nFileCount &&
2419 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2420 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2421 nOpenAction = ONOPEN_OPEN;
2422 else
2423 nOpenAction = ONOPEN_BROWSE;
2424
2425 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2426 fodInfos->ofnInfos->Flags,
2427 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2428 nOpenAction);
2429 if(!nOpenAction)
2430 goto ret;
2431
2432 /*
2433 Step 3: here we have a cleaned up and validated path
2434
2435 valid variables:
2436 lpsf: ShellFolder bound to the rightmost valid path component
2437 lpstrPathAndFile: cleaned up path
2438 nOpenAction: action to do
2439 */
2440 TRACE("end validate sf=%p\n", lpsf);
2441
2442 switch(nOpenAction)
2443 {
2444 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2445 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2446 {
2447 int iPos;
2448 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2449 DWORD len;
2450
2451 /* replace the current filter */
2452 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2453 len = lstrlenW(lpszTemp)+1;
2454 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2455 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2456
2457 /* set the filter cb to the extension when possible */
2458 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2459 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2460 }
2461 /* fall through */
2462 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2463 TRACE("ONOPEN_BROWSE\n");
2464 {
2465 IPersistFolder2 * ppf2;
2466 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2467 {
2468 LPITEMIDLIST pidlCurrent;
2469 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2470 IPersistFolder2_Release(ppf2);
2471 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2472 {
2473 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2474 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2475 {
2476 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2477 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2478 }
2479 }
2480 else if( nOpenAction == ONOPEN_SEARCH )
2481 {
2482 if (fodInfos->Shell.FOIShellView)
2483 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2484 }
2485 COMDLG32_SHFree(pidlCurrent);
2486 if (filename_is_edit( fodInfos ))
2487 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2488 else
2489 {
2490 HWND hwnd;
2491
2492 hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2493 SendMessageW(hwnd, EM_SETSEL, 0, -1);
2494 }
2495 }
2496 }
2497 ret = FALSE;
2498 break;
2499 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2500 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2501 {
2502 WCHAR *ext = NULL;
2503
2504 /* update READONLY check box flag */
2505 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2506 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2507 else
2508 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2509
2510 /* Attach the file extension with file name*/
2511 ext = PathFindExtensionW(lpstrPathAndFile);
2512 if (! *ext && fodInfos->defext)
2513 {
2514 /* if no extension is specified with file name, then */
2515 /* attach the extension from file filter or default one */
2516
2517 WCHAR *filterExt = NULL;
2518 LPWSTR lpstrFilter = NULL;
2519 static const WCHAR szwDot[] = {'.',0};
2520 int PathLength = lstrlenW(lpstrPathAndFile);
2521
2522 /*Get the file extension from file type filter*/
2523 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2524 fodInfos->ofnInfos->nFilterIndex-1);
2525
2526 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2527 {
2528 WCHAR* filterSearchIndex;
2529 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2530 strcpyW(filterExt, lpstrFilter);
2531
2532 /* if a semicolon-separated list of file extensions was given, do not include the
2533 semicolon or anything after it in the extension.
2534 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2535 filterSearchIndex = strchrW(filterExt, ';');
2536 if (filterSearchIndex)
2537 {
2538 filterSearchIndex[0] = '\0';
2539 }
2540
2541 /* find the file extension by searching for the first dot in filterExt */
2542 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2543 /* if the extension is invalid or contains a glob, ignore it */
2544 filterSearchIndex = strchrW(filterExt, '.');
2545 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2546 {
2547 strcpyW(filterExt, filterSearchIndex);
2548 }
2549 else
2550 {
2551 HeapFree(GetProcessHeap(), 0, filterExt);
2552 filterExt = NULL;
2553 }
2554 }
2555
2556 if (!filterExt)
2557 {
2558 /* use the default file extension */
2559 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2560 strcpyW(filterExt, fodInfos->defext);
2561 }
2562
2563 if (*filterExt) /* ignore filterExt="" */
2564 {
2565 /* Attach the dot*/
2566 lstrcatW(lpstrPathAndFile, szwDot);
2567 /* Attach the extension */
2568 lstrcatW(lpstrPathAndFile, filterExt);
2569 }
2570
2571 HeapFree(GetProcessHeap(), 0, filterExt);
2572
2573 /* In Open dialog: if file does not exist try without extension */
2574 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2575 lpstrPathAndFile[PathLength] = '\0';
2576
2577 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2578 if (*ext)
2579 ext++;
2580 if (!lstrcmpiW(fodInfos->defext, ext))
2581 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2582 else
2583 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2584 }
2585
2586 /* In Save dialog: check if the file already exists */
2587 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2588 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2589 && PathFileExistsW(lpstrPathAndFile))
2590 {
2591 WCHAR lpstrOverwrite[100];
2592 int answer;
2593
2594 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2595 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2596 MB_YESNO | MB_ICONEXCLAMATION);
2597 if (answer == IDNO || answer == IDCANCEL)
2598 {
2599 ret = FALSE;
2600 goto ret;
2601 }
2602 }
2603
2604 /* In Open dialog: check if it should be created if it doesn't exist */
2605 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2606 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2607 && !PathFileExistsW(lpstrPathAndFile))
2608 {
2609 WCHAR lpstrCreate[100];
2610 int answer;
2611
2612 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2613 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2614 MB_YESNO | MB_ICONEXCLAMATION);
2615 if (answer == IDNO || answer == IDCANCEL)
2616 {
2617 ret = FALSE;
2618 goto ret;
2619 }
2620 }
2621
2622 /* Check that the size of the file does not exceed buffer size.
2623 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2624 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2625 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2626 {
2627
2628 /* fill destination buffer */
2629 if (fodInfos->ofnInfos->lpstrFile)
2630 {
2631 if(fodInfos->unicode)
2632 {
2633 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2634
2635 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2636 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2637 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2638 }
2639 else
2640 {
2641 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2642
2643 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2644 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2645 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2646 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2647 }
2648 }
2649
2650 if(fodInfos->unicode)
2651 {
2652 LPWSTR lpszTemp;
2653
2654 /* set filename offset */
2655 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2656 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2657
2658 /* set extension offset */
2659 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2660 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2661 }
2662 else
2663 {
2664 LPSTR lpszTemp;
2665 CHAR tempFileA[MAX_PATH];
2666
2667 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2668 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2669 tempFileA, sizeof(tempFileA), NULL, NULL);
2670
2671 /* set filename offset */
2672 lpszTemp = PathFindFileNameA(tempFileA);
2673 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2674
2675 /* set extension offset */
2676 lpszTemp = PathFindExtensionA(tempFileA);
2677 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
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 }