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