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