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