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