* The Shell.. for a long time we dreamed of having a compatible, properly working...
[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 ((lpstrEdit[nStrCharCount]!='"') && (nStrCharCount <= nStrLen))
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 if (!handledPath) {
1562 WCHAR tmpBuf[MAX_PATH];
1563 WCHAR tmpBuf2[MAX_PATH];
1564 WCHAR *nameBit;
1565 DWORD result;
1566
1567 lstrcpyW(tmpBuf, fodInfos->initdir);
1568 if( PathFileExistsW(tmpBuf) ) {
1569 /* initdir does not have to be a directory. If a file is
1570 * specified, the dir part is taken */
1571 if( PathIsDirectoryW(tmpBuf)) {
1572 if (tmpBuf[lstrlenW(tmpBuf)-1] != '\\') {
1573 lstrcatW(tmpBuf, szwSlash);
1574 }
1575 lstrcatW(tmpBuf, szwStar);
1576 }
1577 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1578 if (result) {
1579 *nameBit = 0x00;
1580 MemFree(fodInfos->initdir);
1581 fodInfos->initdir = MemAlloc((lstrlenW(tmpBuf2) + 1)*sizeof(WCHAR));
1582 lstrcpyW(fodInfos->initdir, tmpBuf2);
1583 handledPath = TRUE;
1584 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1585 }
1586 }
1587 else if (fodInfos->initdir)
1588 {
1589 MemFree(fodInfos->initdir);
1590 fodInfos->initdir = NULL;
1591 TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1592 }
1593 }
1594 }
1595
1596 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1597 {
1598 /* 3. All except w2k+: if filename contains a path use it */
1599 if (!win2000plus && fodInfos->filename &&
1600 *fodInfos->filename &&
1601 strpbrkW(fodInfos->filename, szwSlash)) {
1602 WCHAR tmpBuf[MAX_PATH];
1603 WCHAR *nameBit;
1604 DWORD result;
1605
1606 result = GetFullPathNameW(fodInfos->filename, MAX_PATH,
1607 tmpBuf, &nameBit);
1608 if (result) {
1609 int len;
1610
1611 /* nameBit is always shorter than the original filename */
1612 lstrcpyW(fodInfos->filename, nameBit);
1613 *nameBit = 0x00;
1614
1615 len = lstrlenW(tmpBuf);
1616 MemFree(fodInfos->initdir);
1617 fodInfos->initdir = MemAlloc((len+1)*sizeof(WCHAR));
1618 lstrcpyW(fodInfos->initdir, tmpBuf);
1619
1620 handledPath = TRUE;
1621 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1622 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1623 }
1624 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1625 }
1626
1627 /* 4. Win2000+: Recently used */
1628 if (!handledPath && win2000plus) {
1629 fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
1630 fodInfos->initdir[0] = '\0';
1631
1632 FILEDLG95_MRU_load_filename(fodInfos->initdir);
1633
1634 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1635 handledPath = TRUE;
1636 }else{
1637 MemFree(fodInfos->initdir);
1638 fodInfos->initdir = NULL;
1639 }
1640 }
1641
1642 /* 5. win98+ and win2000+ if any files of specified filter types in
1643 current directory, use it */
1644 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1645
1646 LPCWSTR lpstrPos = fodInfos->filter;
1647 WIN32_FIND_DATAW FindFileData;
1648 HANDLE hFind;
1649
1650 while (1)
1651 {
1652 /* filter is a list... title\0ext\0......\0\0 */
1653
1654 /* Skip the title */
1655 if(! *lpstrPos) break; /* end */
1656 lpstrPos += lstrlenW(lpstrPos) + 1;
1657
1658 /* See if any files exist in the current dir with this extension */
1659 if(! *lpstrPos) break; /* end */
1660
1661 hFind = FindFirstFileW(lpstrPos, &FindFileData);
1662
1663 if (hFind == INVALID_HANDLE_VALUE) {
1664 /* None found - continue search */
1665 lpstrPos += lstrlenW(lpstrPos) + 1;
1666
1667 } else {
1668
1669 MemFree(fodInfos->initdir);
1670 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1671 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1672
1673 handledPath = TRUE;
1674 TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1675 debugstr_w(lpstrPos));
1676 FindClose(hFind);
1677 break;
1678 }
1679 }
1680 }
1681
1682 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1683 if (!handledPath && (win2000plus || win98plus)) {
1684 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1685
1686 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir))
1687 {
1688 if(!COMDLG32_SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir))
1689 {
1690 /* last fallback */
1691 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1692 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1693 } else {
1694 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1695 }
1696 } else {
1697 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1698 }
1699 handledPath = TRUE;
1700 } else if (!handledPath) {
1701 fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
1702 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir);
1703 handledPath = TRUE;
1704 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1705 }
1706 }
1707 SetFocus( fodInfos->DlgInfos.hwndFileName );
1708 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1709
1710 /* Must the open as read only check box be checked ?*/
1711 if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1712 {
1713 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0);
1714 }
1715
1716 /* Must the open as read only check box be hidden? */
1717 if(fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY)
1718 {
1719 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE);
1720 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE);
1721 }
1722
1723 /* Must the help button be hidden? */
1724 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1725 {
1726 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE);
1727 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE);
1728 }
1729
1730 /* change Open to Save */
1731 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1732 {
1733 WCHAR buf[16];
1734 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, sizeof(buf)/sizeof(WCHAR));
1735 SetDlgItemTextW(hwnd, IDOK, buf);
1736 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, sizeof(buf)/sizeof(WCHAR));
1737 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf);
1738 }
1739
1740 /* Initialize the filter combo box */
1741 FILEDLG95_FILETYPE_Init(hwnd);
1742
1743 return 0;
1744 }
1745
1746 /***********************************************************************
1747 * FILEDLG95_ResizeControls
1748 *
1749 * WM_INITDIALOG message handler (after hook notification)
1750 */
1751 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1752 {
1753 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1754
1755 if (fodInfos->DlgInfos.hwndCustomDlg)
1756 {
1757 RECT rc;
1758 UINT flags = SWP_NOACTIVATE;
1759
1760 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1761 (fodInfos->ofnInfos->Flags & (OFN_HIDEREADONLY | OFN_SHOWHELP)) == OFN_HIDEREADONLY);
1762
1763 /* resize the custom dialog to the parent size */
1764 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))
1765 GetClientRect(hwnd, &rc);
1766 else
1767 {
1768 /* our own fake template is zero sized and doesn't have children, so
1769 * there is no need to resize it. Picasa depends on it.
1770 */
1771 flags |= SWP_NOSIZE;
1772 SetRectEmpty(&rc);
1773 }
1774 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1775 0, 0, rc.right, rc.bottom, flags);
1776 }
1777 else
1778 {
1779 /* Resize the height; if opened as read-only, checkbox and help button are
1780 * hidden and we are not using a custom template nor a customDialog
1781 */
1782 if ( (fodInfos->ofnInfos->Flags & OFN_HIDEREADONLY) &&
1783 (!(fodInfos->ofnInfos->Flags &
1784 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE))))
1785 {
1786 RECT rectDlg, rectHelp, rectCancel;
1787 GetWindowRect(hwnd, &rectDlg);
1788 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1789 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1790 /* subtract the height of the help button plus the space between the help
1791 * button and the cancel button to the height of the dialog
1792 */
1793 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1794 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1795 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
1796 }
1797 }
1798 return TRUE;
1799 }
1800
1801 /***********************************************************************
1802 * FILEDLG95_FillControls
1803 *
1804 * WM_INITDIALOG message handler (after hook notification)
1805 */
1806 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam)
1807 {
1808 LPITEMIDLIST pidlItemId = NULL;
1809
1810 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1811
1812 TRACE("dir=%s file=%s\n",
1813 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1814
1815 /* Get the initial directory pidl */
1816
1817 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
1818 {
1819 WCHAR path[MAX_PATH];
1820
1821 GetCurrentDirectoryW(MAX_PATH,path);
1822 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
1823 }
1824
1825 /* Initialise shell objects */
1826 FILEDLG95_SHELL_Init(hwnd);
1827
1828 /* Initialize the Look In combo box */
1829 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
1830
1831 /* Browse to the initial directory */
1832 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
1833
1834 /* Free pidlItem memory */
1835 COMDLG32_SHFree(pidlItemId);
1836
1837 return TRUE;
1838 }
1839 /***********************************************************************
1840 * FILEDLG95_Clean
1841 *
1842 * Regroups all the cleaning functions of the filedlg
1843 */
1844 void FILEDLG95_Clean(HWND hwnd)
1845 {
1846 FILEDLG95_FILETYPE_Clean(hwnd);
1847 FILEDLG95_LOOKIN_Clean(hwnd);
1848 FILEDLG95_SHELL_Clean(hwnd);
1849 }
1850 /***********************************************************************
1851 * FILEDLG95_OnWMCommand
1852 *
1853 * WM_COMMAND message handler
1854 */
1855 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
1856 {
1857 WORD wNotifyCode = HIWORD(wParam); /* notification code */
1858 WORD wID = LOWORD(wParam); /* item, control, or accelerator identifier */
1859 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1860
1861 switch(wID)
1862 {
1863 /* OK button */
1864 case IDOK:
1865 FILEDLG95_OnOpen(hwnd);
1866 break;
1867 /* Cancel button */
1868 case IDCANCEL:
1869 FILEDLG95_Clean(hwnd);
1870 EndDialog(hwnd, FALSE);
1871 break;
1872 /* Filetype combo box */
1873 case IDC_FILETYPE:
1874 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
1875 break;
1876 /* LookIn combo box */
1877 case IDC_LOOKIN:
1878 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
1879 break;
1880
1881 /* --- toolbar --- */
1882 /* Up folder button */
1883 case FCIDM_TB_UPFOLDER:
1884 FILEDLG95_SHELL_UpFolder(hwnd);
1885 break;
1886 /* New folder button */
1887 case FCIDM_TB_NEWFOLDER:
1888 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
1889 break;
1890 /* List option button */
1891 case FCIDM_TB_SMALLICON:
1892 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
1893 break;
1894 /* Details option button */
1895 case FCIDM_TB_REPORTVIEW:
1896 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
1897 break;
1898 /* Details option button */
1899 case FCIDM_TB_DESKTOP:
1900 FILEDLG95_SHELL_BrowseToDesktop(hwnd);
1901 break;
1902
1903 case edt1:
1904 case cmb13:
1905 break;
1906
1907 }
1908 /* Do not use the listview selection anymore */
1909 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
1910 return 0;
1911 }
1912
1913 /***********************************************************************
1914 * FILEDLG95_OnWMGetIShellBrowser
1915 *
1916 * WM_GETISHELLBROWSER message handler
1917 */
1918 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd)
1919 {
1920 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1921
1922 TRACE("\n");
1923
1924 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
1925
1926 return TRUE;
1927 }
1928
1929
1930 /***********************************************************************
1931 * FILEDLG95_SendFileOK
1932 *
1933 * Sends the CDN_FILEOK notification if required
1934 *
1935 * RETURNS
1936 * TRUE if the dialog should close
1937 * FALSE if the dialog should not be closed
1938 */
1939 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos )
1940 {
1941 /* ask the hook if we can close */
1942 if(IsHooked(fodInfos))
1943 {
1944 LRESULT retval = 0;
1945
1946 TRACE("---\n");
1947 /* First send CDN_FILEOK as MSDN doc says */
1948 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1949 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK);
1950 if( retval)
1951 {
1952 TRACE("canceled\n");
1953 return FALSE;
1954 }
1955
1956 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
1957 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
1958 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
1959 if( retval)
1960 {
1961 TRACE("canceled\n");
1962 return FALSE;
1963 }
1964 }
1965 return TRUE;
1966 }
1967
1968 /***********************************************************************
1969 * FILEDLG95_OnOpenMultipleFiles
1970 *
1971 * Handles the opening of multiple files.
1972 *
1973 * FIXME
1974 * check destination buffer size
1975 */
1976 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
1977 {
1978 WCHAR lpstrPathSpec[MAX_PATH] = {0};
1979 UINT nCount, nSizePath;
1980 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
1981
1982 TRACE("\n");
1983
1984 if(fodInfos->unicode)
1985 {
1986 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
1987 ofn->lpstrFile[0] = '\0';
1988 }
1989 else
1990 {
1991 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos;
1992 ofn->lpstrFile[0] = '\0';
1993 }
1994
1995 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
1996
1997 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
1998 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
1999 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
2000 {
2001 LPWSTR lpstrTemp = lpstrFileList;
2002
2003 for ( nCount = 0; nCount < nFileCount; nCount++ )
2004 {
2005 LPITEMIDLIST pidl;
2006
2007 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
2008 if (!pidl)
2009 {
2010 WCHAR lpstrNotFound[100];
2011 WCHAR lpstrMsg[100];
2012 WCHAR tmp[400];
2013 static const WCHAR nl[] = {'\n',0};
2014
2015 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
2016 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100);
2017
2018 lstrcpyW(tmp, lpstrTemp);
2019 lstrcatW(tmp, nl);
2020 lstrcatW(tmp, lpstrNotFound);
2021 lstrcatW(tmp, nl);
2022 lstrcatW(tmp, lpstrMsg);
2023
2024 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
2025 return FALSE;
2026 }
2027
2028 /* move to the next file in the list of files */
2029 lpstrTemp += lstrlenW(lpstrTemp) + 1;
2030 COMDLG32_SHFree(pidl);
2031 }
2032 }
2033
2034 nSizePath = lstrlenW(lpstrPathSpec) + 1;
2035 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2036 {
2037 /* For "oldstyle" dialog the components have to
2038 be separated by blanks (not '\0'!) and short
2039 filenames have to be used! */
2040 FIXME("Components have to be separated by blanks\n");
2041 }
2042 if(fodInfos->unicode)
2043 {
2044 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2045 lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2046 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2047 }
2048 else
2049 {
2050 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2051
2052 if (ofn->lpstrFile != NULL)
2053 {
2054 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2055 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2056 if (ofn->nMaxFile > nSizePath)
2057 {
2058 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2059 ofn->lpstrFile + nSizePath,
2060 ofn->nMaxFile - nSizePath, NULL, NULL);
2061 }
2062 }
2063 }
2064
2065 fodInfos->ofnInfos->nFileOffset = nSizePath;
2066 fodInfos->ofnInfos->nFileExtension = 0;
2067
2068 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2069 return FALSE;
2070
2071 /* clean and exit */
2072 FILEDLG95_Clean(hwnd);
2073 return EndDialog(hwnd,TRUE);
2074 }
2075
2076 /* Returns the 'slot name' of the given module_name in the registry's
2077 * most-recently-used list. This will be an ASCII value in the
2078 * range ['a','z'). Returns zero on error.
2079 *
2080 * The slot's value in the registry has the form:
2081 * module_name\0mru_path\0
2082 *
2083 * If stored_path is given, then stored_path will contain the path name
2084 * stored in the registry's MRU list for the given module_name.
2085 *
2086 * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2087 * MRU list key for the given module_name.
2088 */
2089 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
2090 {
2091 WCHAR mru_list[32], *cur_mru_slot;
2092 BOOL taken[25] = {0};
2093 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2094 HKEY hkey_tmp, *hkey;
2095 LONG ret;
2096
2097 if(hkey_ret)
2098 hkey = hkey_ret;
2099 else
2100 hkey = &hkey_tmp;
2101
2102 if(stored_path)
2103 *stored_path = '\0';
2104
2105 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
2106 if(ret){
2107 WARN("Unable to create MRU key: %d\n", ret);
2108 return 0;
2109 }
2110
2111 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2112 (LPBYTE)mru_list, &mru_list_size);
2113 if(ret || key_type != REG_SZ){
2114 if(ret == ERROR_FILE_NOT_FOUND)
2115 return 'a';
2116
2117 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2118 RegCloseKey(*hkey);
2119 return 0;
2120 }
2121
2122 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2123 WCHAR value_data[MAX_PATH], value_name[2] = {0};
2124 DWORD value_data_size = sizeof(value_data);
2125
2126 *value_name = *cur_mru_slot;
2127
2128 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2129 &key_type, (LPBYTE)value_data, &value_data_size);
2130 if(ret || key_type != REG_BINARY){
2131 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2132 continue;
2133 }
2134
2135 if(!strcmpiW(module_name, value_data)){
2136 if(!hkey_ret)
2137 RegCloseKey(*hkey);
2138 if(stored_path)
2139 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2140 return *value_name;
2141 }
2142 }
2143
2144 if(!hkey_ret)
2145 RegCloseKey(*hkey);
2146
2147 /* the module name isn't in the registry, so find the next open slot */
2148 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2149 taken[*cur_mru_slot - 'a'] = TRUE;
2150 for(i = 0; i < 25; ++i){
2151 if(!taken[i])
2152 return i + 'a';
2153 }
2154
2155 /* all slots are taken, so return the last one in MRUList */
2156 --cur_mru_slot;
2157 return *cur_mru_slot;
2158 }
2159
2160 /* save the given filename as most-recently-used path for this module */
2161 static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
2162 {
2163 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2164 LONG ret;
2165 HKEY hkey;
2166
2167 /* get the current executable's name */
2168 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2169 WARN("GotModuleFileName failed: %d\n", GetLastError());
2170 return;
2171 }
2172 module_name = strrchrW(module_path, '\\');
2173 if(!module_name)
2174 module_name = module_path;
2175 else
2176 module_name += 1;
2177
2178 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
2179 if(!slot)
2180 return;
2181 *slot_name = slot;
2182
2183 { /* update the slot's info */
2184 WCHAR *path_ends, *final;
2185 DWORD path_len, final_len;
2186
2187 /* use only the path segment of `filename' */
2188 path_ends = strrchrW(filename, '\\');
2189 path_len = path_ends - filename;
2190
2191 final_len = path_len + lstrlenW(module_name) + 2;
2192
2193 final = MemAlloc(final_len * sizeof(WCHAR));
2194 if(!final)
2195 return;
2196 lstrcpyW(final, module_name);
2197 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2198 final[final_len-1] = '\0';
2199
2200 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2201 final_len * sizeof(WCHAR));
2202 if(ret){
2203 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2204 MemFree(final);
2205 RegCloseKey(hkey);
2206 return;
2207 }
2208
2209 MemFree(final);
2210 }
2211
2212 { /* update MRUList value */
2213 WCHAR old_mru_list[32], new_mru_list[32];
2214 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2215 DWORD mru_list_size = sizeof(old_mru_list), key_type;
2216
2217 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2218 (LPBYTE)old_mru_list, &mru_list_size);
2219 if(ret || key_type != REG_SZ){
2220 if(ret == ERROR_FILE_NOT_FOUND){
2221 new_mru_list[0] = slot;
2222 new_mru_list[1] = '\0';
2223 }else{
2224 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2225 RegCloseKey(hkey);
2226 return;
2227 }
2228 }else{
2229 /* copy old list data over so that the new slot is at the start
2230 * of the list */
2231 *new_mru_slot++ = slot;
2232 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2233 if(*old_mru_slot != slot)
2234 *new_mru_slot++ = *old_mru_slot;
2235 }
2236 *new_mru_slot = '\0';
2237 }
2238
2239 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2240 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2241 if(ret){
2242 WARN("Error saving MRUList data: %d\n", ret);
2243 RegCloseKey(hkey);
2244 return;
2245 }
2246 }
2247 }
2248
2249 /* load the most-recently-used path for this module */
2250 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2251 {
2252 WCHAR module_path[MAX_PATH], *module_name;
2253
2254 /* get the current executable's name */
2255 if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path)/sizeof(module_path[0]))) {
2256 WARN("GotModuleFileName failed: %d\n", GetLastError());
2257 return;
2258 }
2259 module_name = strrchrW(module_path, '\\');
2260 if(!module_name)
2261 module_name = module_path;
2262 else
2263 module_name += 1;
2264
2265 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2266 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2267 }
2268
2269 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2270 {
2271 WCHAR strMsgTitle[MAX_PATH];
2272 WCHAR strMsgText [MAX_PATH];
2273 if (idCaption)
2274 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, sizeof(strMsgTitle)/sizeof(WCHAR));
2275 else
2276 strMsgTitle[0] = '\0';
2277 LoadStringW(COMDLG32_hInstance, idText, strMsgText, sizeof(strMsgText)/sizeof(WCHAR));
2278 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2279 }
2280
2281 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2282 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2283 {
2284 int nOpenAction = defAction;
2285 LPWSTR lpszTemp, lpszTemp1;
2286 LPITEMIDLIST pidl = NULL;
2287 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2288
2289 /* check for invalid chars */
2290 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2291 {
2292 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME);
2293 return FALSE;
2294 }
2295
2296 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2297
2298 lpszTemp1 = lpszTemp = lpstrPathAndFile;
2299 while (lpszTemp1)
2300 {
2301 LPSHELLFOLDER lpsfChild;
2302 WCHAR lpwstrTemp[MAX_PATH];
2303 DWORD dwEaten, dwAttributes;
2304 LPWSTR p;
2305
2306 lstrcpyW(lpwstrTemp, lpszTemp);
2307 p = PathFindNextComponentW(lpwstrTemp);
2308
2309 if (!p) break; /* end of path */
2310
2311 *p = 0;
2312 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2313
2314 /* There are no wildcards when OFN_NOVALIDATE is set */
2315 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2316 {
2317 static const WCHAR wszWild[] = { '*', '?', 0 };
2318 /* if the last element is a wildcard do a search */
2319 if(strpbrkW(lpszTemp1, wszWild) != NULL)
2320 {
2321 nOpenAction = ONOPEN_SEARCH;
2322 break;
2323 }
2324 }
2325 lpszTemp1 = lpszTemp;
2326
2327 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2328
2329 /* append a backslash to drive letters */
2330 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2331 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2332 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2333 {
2334 PathAddBackslashW(lpwstrTemp);
2335 }
2336
2337 dwAttributes = SFGAO_FOLDER;
2338 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2339 {
2340 /* the path component is valid, we have a pidl of the next path component */
2341 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2342 if(dwAttributes & SFGAO_FOLDER)
2343 {
2344 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2345 {
2346 ERR("bind to failed\n"); /* should not fail */
2347 break;
2348 }
2349 IShellFolder_Release(*ppsf);
2350 *ppsf = lpsfChild;
2351 lpsfChild = NULL;
2352 }
2353 else
2354 {
2355 TRACE("value\n");
2356
2357 /* end dialog, return value */
2358 nOpenAction = ONOPEN_OPEN;
2359 break;
2360 }
2361 COMDLG32_SHFree(pidl);
2362 pidl = NULL;
2363 }
2364 else if (!(flags & OFN_NOVALIDATE))
2365 {
2366 if(*lpszTemp || /* points to trailing null for last path element */
2367 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2368 {
2369 if(flags & OFN_PATHMUSTEXIST)
2370 {
2371 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING);
2372 break;
2373 }
2374 }
2375 else
2376 {
2377 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2378 {
2379 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING);
2380 break;
2381 }
2382 }
2383 /* change to the current folder */
2384 nOpenAction = ONOPEN_OPEN;
2385 break;
2386 }
2387 else
2388 {
2389 nOpenAction = ONOPEN_OPEN;
2390 break;
2391 }
2392 }
2393 if(pidl) COMDLG32_SHFree(pidl);
2394
2395 return nOpenAction;
2396 }
2397
2398 /***********************************************************************
2399 * FILEDLG95_OnOpen
2400 *
2401 * Ok button WM_COMMAND message handler
2402 *
2403 * If the function succeeds, the return value is nonzero.
2404 */
2405 BOOL FILEDLG95_OnOpen(HWND hwnd)
2406 {
2407 LPWSTR lpstrFileList;
2408 UINT nFileCount = 0;
2409 UINT sizeUsed = 0;
2410 BOOL ret = TRUE;
2411 WCHAR lpstrPathAndFile[MAX_PATH];
2412 LPSHELLFOLDER lpsf = NULL;
2413 int nOpenAction;
2414 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2415
2416 TRACE("hwnd=%p\n", hwnd);
2417
2418 /* try to browse the selected item */
2419 if(BrowseSelectedFolder(hwnd))
2420 return FALSE;
2421
2422 /* get the files from the edit control */
2423 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2424
2425 if(nFileCount == 0)
2426 return FALSE;
2427
2428 if(nFileCount > 1)
2429 {
2430 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2431 goto ret;
2432 }
2433
2434 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2435
2436 /*
2437 Step 1: Build a complete path name from the current folder and
2438 the filename or path in the edit box.
2439 Special cases:
2440 - the path in the edit box is a root path
2441 (with or without drive letter)
2442 - the edit box contains ".." (or a path with ".." in it)
2443 */
2444
2445 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2446 MemFree(lpstrFileList);
2447
2448 /*
2449 Step 2: here we have a cleaned up path
2450
2451 We have to parse the path step by step to see if we have to browse
2452 to a folder if the path points to a directory or the last
2453 valid element is a directory.
2454
2455 valid variables:
2456 lpstrPathAndFile: cleaned up path
2457 */
2458
2459 if (nFileCount &&
2460 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2461 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2462 nOpenAction = ONOPEN_OPEN;
2463 else
2464 nOpenAction = ONOPEN_BROWSE;
2465
2466 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2467 fodInfos->ofnInfos->Flags,
2468 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2469 nOpenAction);
2470 if(!nOpenAction)
2471 goto ret;
2472
2473 /*
2474 Step 3: here we have a cleaned up and validated path
2475
2476 valid variables:
2477 lpsf: ShellFolder bound to the rightmost valid path component
2478 lpstrPathAndFile: cleaned up path
2479 nOpenAction: action to do
2480 */
2481 TRACE("end validate sf=%p\n", lpsf);
2482
2483 switch(nOpenAction)
2484 {
2485 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2486 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2487 {
2488 int iPos;
2489 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2490 DWORD len;
2491
2492 /* replace the current filter */
2493 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
2494 len = lstrlenW(lpszTemp)+1;
2495 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc(len * sizeof(WCHAR));
2496 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2497
2498 /* set the filter cb to the extension when possible */
2499 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2500 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, iPos);
2501 }
2502 /* fall through */
2503 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2504 TRACE("ONOPEN_BROWSE\n");
2505 {
2506 IPersistFolder2 * ppf2;
2507 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2508 {
2509 LPITEMIDLIST pidlCurrent;
2510 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2511 IPersistFolder2_Release(ppf2);
2512 if( ! COMDLG32_PIDL_ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2513 {
2514 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2515 && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2516 {
2517 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2518 }
2519 }
2520 else if( nOpenAction == ONOPEN_SEARCH )
2521 {
2522 if (fodInfos->Shell.FOIShellView)
2523 IShellView_Refresh(fodInfos->Shell.FOIShellView);
2524 }
2525 COMDLG32_SHFree(pidlCurrent);
2526 if (filename_is_edit( fodInfos ))
2527 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2528 }
2529 }
2530 ret = FALSE;
2531 break;
2532 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2533 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2534 {
2535 WCHAR *ext = NULL;
2536
2537 /* update READONLY check box flag */
2538 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED)
2539 fodInfos->ofnInfos->Flags |= OFN_READONLY;
2540 else
2541 fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2542
2543 /* Attach the file extension with file name*/
2544 ext = PathFindExtensionW(lpstrPathAndFile);
2545 if (! *ext && fodInfos->defext)
2546 {
2547 /* if no extension is specified with file name, then */
2548 /* attach the extension from file filter or default one */
2549
2550 WCHAR *filterExt = NULL;
2551 LPWSTR lpstrFilter = NULL;
2552 static const WCHAR szwDot[] = {'.',0};
2553 int PathLength = lstrlenW(lpstrPathAndFile);
2554
2555 /*Get the file extension from file type filter*/
2556 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2557 fodInfos->ofnInfos->nFilterIndex-1);
2558
2559 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
2560 {
2561 WCHAR* filterSearchIndex;
2562 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
2563 strcpyW(filterExt, lpstrFilter);
2564
2565 /* if a semicolon-separated list of file extensions was given, do not include the
2566 semicolon or anything after it in the extension.
2567 example: if filterExt was "*.abc;*.def", it will become "*.abc" */
2568 filterSearchIndex = strchrW(filterExt, ';');
2569 if (filterSearchIndex)
2570 {
2571 filterSearchIndex[0] = '\0';
2572 }
2573
2574 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
2575 /* if the extension is invalid or contains a glob, ignore it */
2576 filterSearchIndex = PathFindExtensionW(filterExt);
2577 if (*filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?'))
2578 {
2579 strcpyW(filterExt, filterSearchIndex);
2580 }
2581 else
2582 {
2583 HeapFree(GetProcessHeap(), 0, filterExt);
2584 filterExt = NULL;
2585 }
2586 }
2587
2588 if (!filterExt)
2589 {
2590 /* use the default file extension */
2591 filterExt = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
2592 strcpyW(filterExt, fodInfos->defext);
2593 }
2594
2595 if (*filterExt) /* ignore filterExt="" */
2596 {
2597 /* Attach the dot*/
2598 lstrcatW(lpstrPathAndFile, szwDot);
2599 /* Attach the extension */
2600 lstrcatW(lpstrPathAndFile, filterExt);
2601 }
2602
2603 HeapFree(GetProcessHeap(), 0, filterExt);
2604
2605 /* In Open dialog: if file does not exist try without extension */
2606 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
2607 lpstrPathAndFile[PathLength] = '\0';
2608
2609 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
2610 if (*ext)
2611 ext++;
2612 if (!lstrcmpiW(fodInfos->defext, ext))
2613 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
2614 else
2615 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
2616 }
2617
2618 /* In Save dialog: check if the file already exists */
2619 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
2620 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
2621 && PathFileExistsW(lpstrPathAndFile))
2622 {
2623 WCHAR lpstrOverwrite[100];
2624 int answer;
2625
2626 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
2627 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
2628 MB_YESNO | MB_ICONEXCLAMATION);
2629 if (answer == IDNO || answer == IDCANCEL)
2630 {
2631 ret = FALSE;
2632 goto ret;
2633 }
2634 }
2635
2636 /* In Open dialog: check if it should be created if it doesn't exist */
2637 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
2638 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
2639 && !PathFileExistsW(lpstrPathAndFile))
2640 {
2641 WCHAR lpstrCreate[100];
2642 int answer;
2643
2644 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
2645 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
2646 MB_YESNO | MB_ICONEXCLAMATION);
2647 if (answer == IDNO || answer == IDCANCEL)
2648 {
2649 ret = FALSE;
2650 goto ret;
2651 }
2652 }
2653
2654 /* Check that the size of the file does not exceed buffer size.
2655 (Allow for extra \0 if OFN_MULTISELECT is set.) */
2656 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
2657 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
2658 {
2659
2660 /* fill destination buffer */
2661 if (fodInfos->ofnInfos->lpstrFile)
2662 {
2663 if(fodInfos->unicode)
2664 {
2665 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2666
2667 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
2668 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2669 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
2670 }
2671 else
2672 {
2673 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2674
2675 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2676 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL);
2677 if (ofn->Flags & OFN_ALLOWMULTISELECT)
2678 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
2679 }
2680 }
2681
2682 if(fodInfos->unicode)
2683 {
2684 LPWSTR lpszTemp;
2685
2686 /* set filename offset */
2687 lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2688 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
2689
2690 /* set extension offset */
2691 lpszTemp = PathFindExtensionW(lpstrPathAndFile);
2692 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
2693 }
2694 else
2695 {
2696 LPSTR lpszTemp;
2697 CHAR tempFileA[MAX_PATH];
2698
2699 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
2700 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
2701 tempFileA, sizeof(tempFileA), NULL, NULL);
2702
2703 /* set filename offset */
2704 lpszTemp = PathFindFileNameA(tempFileA);
2705 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
2706
2707 /* set extension offset */
2708 lpszTemp = PathFindExtensionA(tempFileA);
2709 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
2710 }
2711
2712 /* set the lpstrFileTitle */
2713 if(fodInfos->ofnInfos->lpstrFileTitle)
2714 {
2715 LPWSTR lpstrFileTitle = PathFindFileNameW(lpstrPathAndFile);
2716 if(fodInfos->unicode)
2717 {
2718 LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2719 lstrcpynW(ofn->lpstrFileTitle, lpstrFileTitle, ofn->nMaxFileTitle);
2720 }
2721 else
2722 {
2723 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2724 WideCharToMultiByte(CP_ACP, 0, lpstrFileTitle, -1,
2725 ofn->lpstrFileTitle, ofn->nMaxFileTitle, NULL, NULL);
2726 }
2727 }
2728
2729 /* copy currently selected filter to lpstrCustomFilter */
2730 if (fodInfos->ofnInfos->lpstrCustomFilter)
2731 {
2732 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos;
2733 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2734 NULL, 0, NULL, NULL);
2735 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter)
2736 {
2737 LPSTR s = ofn->lpstrCustomFilter;
2738 s += strlen(ofn->lpstrCustomFilter)+1;
2739 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
2740 s, len, NULL, NULL);
2741 }
2742 }
2743
2744
2745 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2746 goto ret;
2747
2748 FILEDLG95_MRU_save_filename(lpstrPathAndFile);
2749
2750 TRACE("close\n");
2751 FILEDLG95_Clean(hwnd);
2752 ret = EndDialog(hwnd, TRUE);
2753 }
2754 else
2755 {
2756 WORD size;
2757
2758 size = lstrlenW(lpstrPathAndFile) + 1;
2759 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
2760 size += 1;
2761 /* return needed size in first two bytes of lpstrFile */
2762 if(fodInfos->ofnInfos->lpstrFile)
2763 *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
2764 FILEDLG95_Clean(hwnd);
2765 ret = EndDialog(hwnd, FALSE);
2766 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL);
2767 }
2768 }
2769 break;
2770 }
2771
2772 ret:
2773 if(lpsf) IShellFolder_Release(lpsf);
2774 return ret;
2775 }
2776
2777 /***********************************************************************
2778 * FILEDLG95_SHELL_Init
2779 *
2780 * Initialisation of the shell objects
2781 */
2782 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd)
2783 {
2784 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2785
2786 TRACE("\n");
2787
2788 /*
2789 * Initialisation of the FileOpenDialogInfos structure
2790 */
2791
2792 /* Shell */
2793
2794 /*ShellInfos */
2795 fodInfos->ShellInfos.hwndOwner = hwnd;
2796
2797 /* Disable multi-select if flag not set */
2798 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
2799 {
2800 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
2801 }
2802 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
2803 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
2804
2805 /* Construct the IShellBrowser interface */
2806 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
2807
2808 return NOERROR;
2809 }
2810
2811 /***********************************************************************
2812 * FILEDLG95_SHELL_ExecuteCommand
2813 *
2814 * Change the folder option and refresh the view
2815 * If the function succeeds, the return value is nonzero.
2816 */
2817 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb)
2818 {
2819 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2820 IContextMenu * pcm;
2821
2822 TRACE("(%p,%p)\n", hwnd, lpVerb);
2823
2824 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
2825 SVGIO_BACKGROUND,
2826 &IID_IContextMenu,
2827 (LPVOID*)&pcm)))
2828 {
2829 CMINVOKECOMMANDINFO ci;
2830 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
2831 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
2832 ci.lpVerb = lpVerb;
2833 ci.hwnd = hwnd;
2834
2835 IContextMenu_InvokeCommand(pcm, &ci);
2836 IContextMenu_Release(pcm);
2837 }
2838
2839 return FALSE;
2840 }
2841
2842 /***********************************************************************
2843 * FILEDLG95_SHELL_UpFolder
2844 *
2845 * Browse to the specified object
2846 * If the function succeeds, the return value is nonzero.
2847 */
2848 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd)
2849 {
2850 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2851
2852 TRACE("\n");
2853
2854 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
2855 NULL,
2856 SBSP_PARENT)))
2857 {
2858 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2859 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2860 return TRUE;
2861 }
2862 return FALSE;
2863 }
2864
2865 /***********************************************************************
2866 * FILEDLG95_SHELL_BrowseToDesktop
2867 *
2868 * Browse to the Desktop
2869 * If the function succeeds, the return value is nonzero.
2870 */
2871 static BOOL FILEDLG95_SHELL_BrowseToDesktop(HWND hwnd)
2872 {
2873 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2874 LPITEMIDLIST pidl;
2875 HRESULT hres;
2876
2877 TRACE("\n");
2878
2879 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidl);
2880 hres = IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2881 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2882 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
2883 COMDLG32_SHFree(pidl);
2884 return SUCCEEDED(hres);
2885 }
2886 /***********************************************************************
2887 * FILEDLG95_SHELL_Clean
2888 *
2889 * Cleans the memory used by shell objects
2890 */
2891 static void FILEDLG95_SHELL_Clean(HWND hwnd)
2892 {
2893 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2894
2895 TRACE("\n");
2896
2897 COMDLG32_SHFree(fodInfos->ShellInfos.pidlAbsCurrent);
2898
2899 /* clean Shell interfaces */
2900 if (fodInfos->Shell.FOIShellView)
2901 {
2902 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
2903 IShellView_Release(fodInfos->Shell.FOIShellView);
2904 }
2905 IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
2906 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
2907 if (fodInfos->Shell.FOIDataObject)
2908 IDataObject_Release(fodInfos->Shell.FOIDataObject);
2909 }
2910
2911 /***********************************************************************
2912 * FILEDLG95_FILETYPE_Init
2913 *
2914 * Initialisation of the file type combo box
2915 */
2916 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd)
2917 {
2918 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
2919 int nFilters = 0; /* number of filters */
2920 int nFilterIndexCB;
2921
2922 TRACE("\n");
2923
2924 if(fodInfos->customfilter)
2925 {
2926 /* customfilter has one entry... title\0ext\0
2927 * Set first entry of combo box item with customfilter
2928 */
2929 LPWSTR lpstrExt;
2930 LPCWSTR lpstrPos = fodInfos->customfilter;
2931
2932 /* Get the title */
2933 lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
2934
2935 /* Copy the extensions */
2936 if (! *lpstrPos) return E_FAIL; /* malformed filter */
2937 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2938 lstrcpyW(lpstrExt,lpstrPos);
2939
2940 /* Add the item at the end of the combo */
2941 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, fodInfos->customfilter);
2942 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters, lpstrExt);
2943 nFilters++;
2944 }
2945 if(fodInfos->filter)
2946 {
2947 LPCWSTR lpstrPos = fodInfos->filter;
2948
2949 for(;;)
2950 {
2951 /* filter is a list... title\0ext\0......\0\0
2952 * Set the combo item text to the title and the item data
2953 * to the ext
2954 */
2955 LPCWSTR lpstrDisplay;
2956 LPWSTR lpstrExt;
2957
2958 /* Get the title */
2959 if(! *lpstrPos) break; /* end */
2960 lpstrDisplay = lpstrPos;
2961 lpstrPos += lstrlenW(lpstrPos) + 1;
2962
2963 CBAddString(fodInfos->DlgInfos.hwndFileTypeCB, lpstrDisplay);
2964
2965 nFilters++;
2966
2967 /* Copy the extensions */
2968 if (!(lpstrExt = MemAlloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
2969 lstrcpyW(lpstrExt,lpstrPos);
2970 lpstrPos += lstrlenW(lpstrPos) + 1;
2971
2972 /* Add the item at the end of the combo */
2973 CBSetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, nFilters-1, lpstrExt);
2974
2975 /* malformed filters are added anyway... */
2976 if (!*lpstrExt) break;
2977 }
2978 }
2979
2980 /*
2981 * Set the current filter to the one specified
2982 * in the initialisation structure
2983 */
2984 if (fodInfos->filter || fodInfos->customfilter)
2985 {
2986 LPWSTR lpstrFilter;
2987
2988 /* Check to make sure our index isn't out of bounds. */
2989 if ( fodInfos->ofnInfos->nFilterIndex >
2990 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
2991 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
2992
2993 /* set default filter index */
2994 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
2995 fodInfos->ofnInfos->nFilterIndex = 1;
2996
2997 /* calculate index of Combo Box item */
2998 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
2999 if (fodInfos->customfilter == NULL)
3000 nFilterIndexCB--;
3001
3002 /* Set the current index selection. */
3003 CBSetCurSel(fodInfos->DlgInfos.hwndFileTypeCB, nFilterIndexCB);
3004
3005 /* Get the corresponding text string from the combo box. */
3006 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3007 nFilterIndexCB);
3008
3009 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
3010 lpstrFilter = NULL;
3011
3012 if(lpstrFilter)
3013 {
3014 DWORD len;
3015 CharLowerW(lpstrFilter); /* lowercase */
3016 len = lstrlenW(lpstrFilter)+1;
3017 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3018 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3019 }
3020 } else
3021 fodInfos->ofnInfos->nFilterIndex = 0;
3022 return S_OK;
3023 }
3024
3025 /***********************************************************************
3026 * FILEDLG95_FILETYPE_OnCommand
3027 *
3028 * WM_COMMAND of the file type combo box
3029 * If the function succeeds, the return value is nonzero.
3030 */
3031 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode)
3032 {
3033 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3034
3035 switch(wNotifyCode)
3036 {
3037 case CBN_SELENDOK:
3038 {
3039 LPWSTR lpstrFilter;
3040
3041 /* Get the current item of the filetype combo box */
3042 int iItem = CBGetCurSel(fodInfos->DlgInfos.hwndFileTypeCB);
3043
3044 /* set the current filter index */
3045 fodInfos->ofnInfos->nFilterIndex = iItem +
3046 (fodInfos->customfilter == NULL ? 1 : 0);
3047
3048 /* Set the current filter with the current selection */
3049 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3050
3051 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3052 iItem);
3053 if((INT_PTR)lpstrFilter != CB_ERR)
3054 {
3055 DWORD len;
3056 CharLowerW(lpstrFilter); /* lowercase */
3057 len = lstrlenW(lpstrFilter)+1;
3058 fodInfos->ShellInfos.lpstrCurrentFilter = MemAlloc( len * sizeof(WCHAR) );
3059 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3060 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3061 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE);
3062 }
3063
3064 /* Refresh the actual view to display the included items*/
3065 if (fodInfos->Shell.FOIShellView)
3066 IShellView_Refresh(fodInfos->Shell.FOIShellView);
3067 }
3068 }
3069 return FALSE;
3070 }
3071 /***********************************************************************
3072 * FILEDLG95_FILETYPE_SearchExt
3073 *
3074 * searches for an extension in the filetype box
3075 */
3076 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt)
3077 {
3078 int i, iCount = CBGetCount(hwnd);
3079
3080 TRACE("%s\n", debugstr_w(lpstrExt));
3081
3082 if(iCount != CB_ERR)
3083 {
3084 for(i=0;i<iCount;i++)
3085 {
3086 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3087 return i;
3088 }
3089 }
3090 return -1;
3091 }
3092
3093 /***********************************************************************
3094 * FILEDLG95_FILETYPE_Clean
3095 *
3096 * Clean the memory used by the filetype combo box
3097 */
3098 static void FILEDLG95_FILETYPE_Clean(HWND hwnd)
3099 {
3100 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3101 int iPos;
3102 int iCount = CBGetCount(fodInfos->DlgInfos.hwndFileTypeCB);
3103
3104 TRACE("\n");
3105
3106 /* Delete each string of the combo and their associated data */
3107 if(iCount != CB_ERR)
3108 {
3109 for(iPos = iCount-1;iPos>=0;iPos--)
3110 {
3111 MemFree((LPSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3112 CBDeleteString(fodInfos->DlgInfos.hwndFileTypeCB,iPos);
3113 }
3114 }
3115 /* Current filter */
3116 MemFree(fodInfos->ShellInfos.lpstrCurrentFilter);
3117
3118 }
3119
3120 /***********************************************************************
3121 * FILEDLG95_LOOKIN_Init
3122 *
3123 * Initialisation of the look in combo box
3124 */
3125
3126 /* Small helper function, to determine if the unixfs shell extension is rooted
3127 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3128 */
3129 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) {
3130 HKEY hKey;
3131 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3132 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3133 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3134 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3135 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3136 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3137 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3138
3139 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3140 return FALSE;
3141
3142 RegCloseKey(hKey);
3143 return TRUE;
3144 }
3145
3146 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3147 {
3148 IShellFolder *psfRoot, *psfDrives;
3149 IEnumIDList *lpeRoot, *lpeDrives;
3150 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3151 HDC hdc;
3152 TEXTMETRICW tm;
3153 LookInInfos *liInfos = MemAlloc(sizeof(LookInInfos));
3154
3155 TRACE("\n");
3156
3157 liInfos->iMaxIndentation = 0;
3158
3159 SetPropA(hwndCombo, LookInInfosStr, liInfos);
3160
3161 hdc = GetDC( hwndCombo );
3162 SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3163 GetTextMetricsW( hdc, &tm );
3164 ReleaseDC( hwndCombo, hdc );
3165
3166 /* set item height for both text field and listbox */
3167 CBSetItemHeight( hwndCombo, -1, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3168 CBSetItemHeight( hwndCombo, 0, max( tm.tmHeight, GetSystemMetrics(SM_CYSMICON) ));
3169
3170 /* Turn on the extended UI for the combo box like Windows does */
3171 CBSetExtendedUI(hwndCombo, TRUE);
3172
3173 /* Initialise data of Desktop folder */
3174 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp);
3175 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3176 COMDLG32_SHFree(pidlTmp);
3177
3178 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives);
3179
3180 SHGetDesktopFolder(&psfRoot);
3181
3182 if (psfRoot)
3183 {
3184 /* enumerate the contents of the desktop */
3185 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3186 {
3187 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3188 {
3189 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3190
3191 /* If the unixfs extension is rooted, we don't expand the drives by default */
3192 if (!FILEDLG95_unixfs_is_rooted_at_desktop())
3193 {
3194 /* special handling for CSIDL_DRIVES */
3195 if (COMDLG32_PIDL_ILIsEqual(pidlTmp, pidlDrives))
3196 {
3197 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3198 {
3199 /* enumerate the drives */
3200 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3201 {
3202 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3203 {
3204 pidlAbsTmp = COMDLG32_PIDL_ILCombine(pidlTmp, pidlTmp1);
3205 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3206 COMDLG32_SHFree(pidlAbsTmp);
3207 COMDLG32_SHFree(pidlTmp1);
3208 }
3209 IEnumIDList_Release(lpeDrives);
3210 }
3211 IShellFolder_Release(psfDrives);
3212 }
3213 }
3214 }
3215
3216 COMDLG32_SHFree(pidlTmp);
3217 }
3218 IEnumIDList_Release(lpeRoot);
3219 }
3220 IShellFolder_Release(psfRoot);
3221 }
3222
3223 COMDLG32_SHFree(pidlDrives);
3224 }
3225
3226 /***********************************************************************
3227 * FILEDLG95_LOOKIN_DrawItem
3228 *
3229 * WM_DRAWITEM message handler
3230 */
3231 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct)
3232 {
3233 COLORREF crWin = GetSysColor(COLOR_WINDOW);
3234 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3235 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
3236 RECT rectText;
3237 RECT rectIcon;
3238 SHFILEINFOW sfi;
3239 HIMAGELIST ilItemImage;
3240 int iIndentation;
3241 TEXTMETRICW tm;
3242 LPSFOLDER tmpFolder;
3243 LookInInfos *liInfos = GetPropA(pDIStruct->hwndItem,LookInInfosStr);
3244 UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME;
3245 UINT icon_width, icon_height;
3246
3247 TRACE("\n");
3248
3249 if(pDIStruct->itemID == -1)
3250 return 0;
3251
3252 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3253 pDIStruct->itemID)))
3254 return 0;
3255
3256
3257 icon_width = GetSystemMetrics(SM_CXICON);
3258 icon_height = GetSystemMetrics(SM_CYICON);
3259 if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3260 {
3261 icon_width = GetSystemMetrics(SM_CXSMICON);
3262 icon_height = GetSystemMetrics(SM_CYSMICON);
3263 shgfi_flags |= SHGFI_SMALLICON;
3264 }
3265
3266 if(pDIStruct->itemID == liInfos->uSelectedItem)
3267 {
3268 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3269 0, &sfi, sizeof (sfi), shgfi_flags );
3270 }
3271 else
3272 {
3273 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3274 0, &sfi, sizeof (sfi), shgfi_flags );
3275 }
3276
3277 /* Is this item selected ? */
3278 if(pDIStruct->itemState & ODS_SELECTED)
3279 {
3280 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3281 SetBkColor(pDIStruct->hDC,crHighLight);
3282 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3283 }
3284 else
3285 {
3286 SetTextColor(pDIStruct->hDC,crText);
3287 SetBkColor(pDIStruct->hDC,crWin);
3288 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3289 }
3290
3291 /* Do not indent item if drawing in the edit of the combo */
3292 if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3293 {
3294 iIndentation = 0;
3295 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3296 0, &sfi, sizeof (sfi), shgfi_flags );
3297
3298 }
3299 else
3300 {
3301 iIndentation = tmpFolder->m_iIndent;
3302 }
3303 /* Draw text and icon */
3304
3305 /* Initialise the icon display area */
3306 rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3307 rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3308 rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3309 rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3310
3311 /* Initialise the text display area */
3312 GetTextMetricsW(pDIStruct->hDC, &tm);
3313 rectText.left = rectIcon.right;
3314 rectText.top =
3315 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3316 rectText.right = pDIStruct->rcItem.right;
3317 rectText.bottom =
3318 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3319
3320 /* Draw the icon from the image list */
3321 ImageList_Draw(ilItemImage,
3322 sfi.iIcon,
3323 pDIStruct->hDC,
3324 rectIcon.left,
3325 rectIcon.top,
3326 ILD_TRANSPARENT );
3327
3328 /* Draw the associated text */
3329 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3330 return NOERROR;
3331 }
3332
3333 /***********************************************************************
3334 * FILEDLG95_LOOKIN_OnCommand
3335 *
3336 * LookIn combo box WM_COMMAND message handler
3337 * If the function succeeds, the return value is nonzero.
3338 */
3339 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode)
3340 {
3341 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3342
3343 TRACE("%p\n", fodInfos);
3344
3345 switch(wNotifyCode)
3346 {
3347 case CBN_SELENDOK:
3348 {
3349 LPSFOLDER tmpFolder;
3350 int iItem;
3351
3352 iItem = CBGetCurSel(fodInfos->DlgInfos.hwndLookInCB);
3353
3354 if( iItem == CB_ERR) return FALSE;
3355
3356 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3357 iItem)))
3358 return FALSE;
3359
3360
3361 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3362 tmpFolder->pidlItem,
3363 SBSP_ABSOLUTE)))
3364 {
3365 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3366 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE);
3367 return TRUE;
3368 }
3369 break;
3370 }
3371
3372 }
3373 return FALSE;
3374 }
3375
3376 /***********************************************************************
3377 * FILEDLG95_LOOKIN_AddItem
3378 *
3379 * Adds an absolute pidl item to the lookin combo box
3380 * returns the index of the inserted item
3381 */
3382 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3383 {
3384 LPITEMIDLIST pidlNext;
3385 SHFILEINFOW sfi;
3386 SFOLDER *tmpFolder;
3387 LookInInfos *liInfos;
3388
3389 TRACE("%08x\n", iInsertId);
3390
3391 if(!pidl)
3392 return -1;
3393
3394 if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3395 return -1;
3396
3397 tmpFolder = MemAlloc(sizeof(SFOLDER));
3398 tmpFolder->m_iIndent = 0;
3399
3400 /* Calculate the indentation of the item in the lookin*/
3401 pidlNext = pidl;
3402 while( (pidlNext=COMDLG32_PIDL_ILGetNext(pidlNext)) )
3403 {
3404 tmpFolder->m_iIndent++;
3405 }
3406
3407 tmpFolder->pidlItem = COMDLG32_PIDL_ILClone(pidl);
3408
3409 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3410 liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3411
3412 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3413 SHGetFileInfoW((LPCWSTR)pidl,
3414 0,
3415 &sfi,
3416 sizeof(sfi),
3417 SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX
3418 | SHGFI_PIDL | SHGFI_SMALLICON | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED);
3419
3420 TRACE("-- Add %s attr=%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3421
3422 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3423 {
3424 int iItemID;
3425
3426 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3427
3428 /* Add the item at the end of the list */
3429 if(iInsertId < 0)
3430 {
3431 iItemID = CBAddString(hwnd,sfi.szDisplayName);
3432 }
3433 /* Insert the item at the iInsertId position*/
3434 else
3435 {
3436 iItemID = CBInsertString(hwnd,sfi.szDisplayName,iInsertId);
3437 }
3438
3439 CBSetItemDataPtr(hwnd,iItemID,tmpFolder);
3440 return iItemID;
3441 }
3442
3443 COMDLG32_SHFree( tmpFolder->pidlItem );
3444 MemFree( tmpFolder );
3445 return -1;
3446
3447 }
3448
3449 /***********************************************************************
3450 * FILEDLG95_LOOKIN_InsertItemAfterParent
3451 *
3452 * Insert an item below its parent
3453 */
3454 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl)
3455 {
3456
3457 LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3458 int iParentPos;
3459
3460 TRACE("\n");
3461
3462 if (pidl == pidlParent)
3463 return -1;
3464
3465 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3466
3467 if(iParentPos < 0)
3468 {
3469 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3470 }
3471
3472 /* Free pidlParent memory */
3473 COMDLG32_SHFree(pidlParent);
3474
3475 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3476 }
3477
3478 /***********************************************************************
3479 * FILEDLG95_LOOKIN_SelectItem
3480 *
3481 * Adds an absolute pidl item to the lookin combo box
3482 * returns the index of the inserted item
3483 */
3484 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl)
3485 {
3486 int iItemPos;
3487 LookInInfos *liInfos;
3488
3489 TRACE("\n");
3490
3491 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL);
3492
3493 liInfos = GetPropA(hwnd,LookInInfosStr);
3494
3495 if(iItemPos < 0)
3496 {
3497 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1);
3498 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl);
3499 }
3500
3501 else
3502 {
3503 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3504 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3505 {
3506 int iRemovedItem;
3507
3508 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
3509 break;
3510 if(iRemovedItem < iItemPos)
3511 iItemPos--;
3512 }
3513 }
3514
3515 CBSetCurSel(hwnd,iItemPos);
3516 liInfos->uSelectedItem = iItemPos;
3517
3518 return 0;
3519
3520 }
3521
3522 /***********************************************************************
3523 * FILEDLG95_LOOKIN_RemoveMostExpandedItem
3524 *
3525 * Remove the item with an expansion level over iExpansionLevel
3526 */
3527 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd)
3528 {
3529 int iItemPos;
3530 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr);
3531
3532 TRACE("\n");
3533
3534 if(liInfos->iMaxIndentation <= 2)
3535 return -1;
3536
3537 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
3538 {
3539 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3540 COMDLG32_SHFree(tmpFolder->pidlItem);
3541 MemFree(tmpFolder);
3542 CBDeleteString(hwnd,iItemPos);
3543 liInfos->iMaxIndentation--;
3544
3545 return iItemPos;
3546 }
3547
3548 return -1;
3549 }
3550
3551 /***********************************************************************
3552 * FILEDLG95_LOOKIN_SearchItem
3553 *
3554 * Search for pidl in the lookin combo box
3555 * returns the index of the found item
3556 */
3557 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
3558 {
3559 int i = 0;
3560 int iCount = CBGetCount(hwnd);
3561
3562 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
3563
3564 if (iCount != CB_ERR)
3565 {
3566 for(;i<iCount;i++)
3567 {
3568 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
3569
3570 if(iSearchMethod == SEARCH_PIDL && COMDLG32_PIDL_ILIsEqual((LPITEMIDLIST)searchArg,tmpFolder->pidlItem))
3571 return i;
3572 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
3573 return i;
3574 }
3575 }
3576
3577 return -1;
3578 }
3579
3580 /***********************************************************************
3581 * FILEDLG95_LOOKIN_Clean
3582 *
3583 * Clean the memory used by the lookin combo box
3584 */
3585 static void FILEDLG95_LOOKIN_Clean(HWND hwnd)
3586 {
3587 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3588 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3589 int iPos;
3590 int iCount = CBGetCount(fodInfos->DlgInfos.hwndLookInCB);
3591
3592 TRACE("\n");
3593
3594 /* Delete each string of the combo and their associated data */
3595 if (iCount != CB_ERR)
3596 {
3597 for(iPos = iCount-1;iPos>=0;iPos--)
3598 {
3599 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
3600 COMDLG32_SHFree(tmpFolder->pidlItem);
3601 MemFree(tmpFolder);
3602 CBDeleteString(fodInfos->DlgInfos.hwndLookInCB,iPos);
3603 }
3604 }
3605
3606 /* LookInInfos structure */
3607 MemFree(liInfos);
3608 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
3609 }
3610
3611 /***********************************************************************
3612 * FILEDLG95_FILENAME_FillFromSelection
3613 *
3614 * fills the edit box from the cached DataObject
3615 */
3616 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd)
3617 {
3618 FileOpenDlgInfos *fodInfos;
3619 LPITEMIDLIST pidl;
3620 UINT nFiles = 0, nFileToOpen, nFileSelected, nLength = 0;
3621 WCHAR lpstrTemp[MAX_PATH];
3622 LPWSTR lpstrAllFile, lpstrCurrFile;
3623
3624 TRACE("\n");
3625 fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3626
3627 /* Count how many files we have */
3628 nFileSelected = GetNumSelected( fodInfos->Shell.FOIDataObject );
3629
3630 /* calculate the string length, count files */
3631 if (nFileSelected >= 1)
3632 {
3633 nLength += 3; /* first and last quotes, trailing \0 */
3634 for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ )
3635 {
3636 pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 );
3637
3638 if (pidl)
3639 {
3640 /* get the total length of the selected file names */
3641 lpstrTemp[0] = '\0';
3642 GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp );
3643
3644 if ( ! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl) ) /* Ignore folders */
3645 {
3646 nLength += lstrlenW( lpstrTemp ) + 3;
3647 nFiles++;
3648 }
3649 COMDLG32_SHFree( pidl );
3650 }
3651 }
3652 }
3653
3654 /* allocate the buffer */
3655 if (nFiles <= 1) nLength = MAX_PATH;
3656 lpstrAllFile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength * sizeof(WCHAR));
3657
3658 /* Generate the string for the edit control */
3659 if(nFiles >= 1)
3660 {
3661 lpstrCurrFile = lpstrAllFile;
3662 for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ )
3663 {
3664 pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 );
3665
3666 if (pidl)
3667 {
3668 /* get the file name */
3669 lpstrTemp[0] = '\0';
3670 GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp );
3671
3672 if (! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl)) /* Ignore folders */
3673 {
3674 if ( nFiles > 1)
3675 {
3676 *lpstrCurrFile++ = '\"';
3677 lstrcpyW( lpstrCurrFile, lpstrTemp );
3678 lpstrCurrFile += lstrlenW( lpstrTemp );
3679 *lpstrCurrFile++ = '\"';
3680 *lpstrCurrFile++ = ' ';
3681 *lpstrCurrFile = 0;
3682 }
3683 else
3684 {
3685 lstrcpyW( lpstrAllFile, lpstrTemp );
3686 }
3687 }
3688 COMDLG32_SHFree( pidl );
3689 }
3690 }
3691 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrAllFile );
3692
3693 /* Select the file name like Windows does */
3694 if (filename_is_edit( fodInfos ))
3695 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
3696 }
3697 HeapFree(GetProcessHeap(),0, lpstrAllFile );
3698 }
3699
3700
3701 /* copied from shell32 to avoid linking to it
3702 * Although shell32 is already linked the behaviour of exported StrRetToStrN
3703 * is dependent on whether emulated OS is unicode or not.
3704 */
3705 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl)
3706 {
3707 switch (src->uType)
3708 {
3709 case STRRET_WSTR:
3710 lstrcpynW(dest, src->u.pOleStr, len);
3711 COMDLG32_SHFree(src->u.pOleStr);
3712 break;
3713
3714 case STRRET_CSTR:
3715 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
3716 dest[len-1] = 0;
3717 break;
3718
3719 case STRRET_OFFSET:
3720 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
3721 dest[len-1] = 0;
3722 break;
3723
3724 default:
3725 FIXME("unknown type %x!\n", src->uType);
3726 if (len) *dest = '\0';
3727 return E_FAIL;
3728 }
3729 return S_OK;
3730 }
3731
3732 /***********************************************************************
3733 * FILEDLG95_FILENAME_GetFileNames
3734 *
3735 * Copies the filenames to a delimited string list.
3736 */
3737 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
3738 {
3739 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3740 UINT nFileCount = 0; /* number of files */
3741 UINT nStrLen = 0; /* length of string in edit control */
3742 LPWSTR lpstrEdit; /* buffer for string from edit control */
3743
3744 TRACE("\n");
3745
3746 /* get the filenames from the filename control */
3747 nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
3748 lpstrEdit = MemAlloc( (nStrLen+1)*sizeof(WCHAR) );
3749 GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
3750
3751 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
3752
3753 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
3754 MemFree(lpstrEdit);
3755 return nFileCount;
3756 }
3757
3758 #define SETDefFormatEtc(fe,cf,med) \
3759 { \
3760 (fe).cfFormat = cf;\
3761 (fe).dwAspect = DVASPECT_CONTENT; \
3762 (fe).ptd =NULL;\
3763 (fe).tymed = med;\
3764 (fe).lindex = -1;\
3765 };
3766
3767 /*
3768 * DATAOBJECT Helper functions
3769 */
3770
3771 /***********************************************************************
3772 * COMCTL32_ReleaseStgMedium
3773 *
3774 * like ReleaseStgMedium from ole32
3775 */
3776 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
3777 {
3778 if(medium.pUnkForRelease)
3779 {
3780 IUnknown_Release(medium.pUnkForRelease);
3781 }
3782 else
3783 {
3784 GlobalUnlock(medium.u.hGlobal);
3785 GlobalFree(medium.u.hGlobal);
3786 }
3787 }
3788
3789 /***********************************************************************
3790 * GetPidlFromDataObject
3791 *
3792 * Return pidl(s) by number from the cached DataObject
3793 *
3794 * nPidlIndex=0 gets the fully qualified root path
3795 */
3796 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex)
3797 {
3798
3799 STGMEDIUM medium;
3800 FORMATETC formatetc;
3801 LPITEMIDLIST pidl = NULL;
3802
3803 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
3804
3805 if (!doSelected)
3806 return NULL;
3807
3808 /* Set the FORMATETC structure*/
3809 SETDefFormatEtc(formatetc, RegisterClipboardFormatA(CFSTR_SHELLIDLISTA), TYMED_HGLOBAL);
3810
3811 /* Get the pidls from IDataObject */
3812 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3813 {
3814 LPIDA cida = GlobalLock(medium.u.hGlobal);
3815 if(nPidlIndex <= cida->cidl)
3816 {
3817 pidl = COMDLG32_PIDL_ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
3818 }
3819 COMCTL32_ReleaseStgMedium(medium);
3820 }
3821 return pidl;
3822 }
3823
3824 /***********************************************************************
3825 * GetNumSelected
3826 *
3827 * Return the number of selected items in the DataObject.
3828 *
3829 */
3830 static UINT GetNumSelected( IDataObject *doSelected )
3831 {
3832 UINT retVal = 0;
3833 STGMEDIUM medium;
3834 FORMATETC formatetc;
3835
3836 TRACE("sv=%p\n", doSelected);
3837
3838 if (!doSelected) return 0;
3839
3840 /* Set the FORMATETC structure*/
3841 SETDefFormatEtc(formatetc, RegisterClipboardFormatA(CFSTR_SHELLIDLISTA), TYMED_HGLOBAL);
3842
3843 /* Get the pidls from IDataObject */
3844 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
3845 {
3846 LPIDA cida = GlobalLock(medium.u.hGlobal);
3847 retVal = cida->cidl;
3848 COMCTL32_ReleaseStgMedium(medium);
3849 return retVal;
3850 }
3851 return 0;
3852 }
3853
3854 /*
3855 * TOOLS
3856 */
3857
3858 /***********************************************************************
3859 * GetName
3860 *
3861 * Get the pidl's display name (relative to folder) and
3862 * put it in lpstrFileName.
3863 *
3864 * Return NOERROR on success,
3865 * E_FAIL otherwise
3866 */
3867
3868 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
3869 {
3870 STRRET str;
3871 HRESULT hRes;
3872
3873 TRACE("sf=%p pidl=%p\n", lpsf, pidl);
3874
3875 if(!lpsf)
3876 {
3877 SHGetDesktopFolder(&lpsf);
3878 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
3879 IShellFolder_Release(lpsf);
3880 return hRes;
3881 }
3882
3883 /* Get the display name of the pidl relative to the folder */
3884 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
3885 {
3886 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
3887 }
3888 return E_FAIL;
3889 }
3890
3891 /***********************************************************************
3892 * GetShellFolderFromPidl
3893 *
3894 * pidlRel is the item pidl relative
3895 * Return the IShellFolder of the absolute pidl
3896 */
3897 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs)
3898 {
3899 IShellFolder *psf = NULL,*psfParent;
3900
3901 TRACE("%p\n", pidlAbs);
3902
3903 if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
3904 {
3905 psf = psfParent;
3906 if(pidlAbs && pidlAbs->mkid.cb)
3907 {
3908 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
3909 {
3910 IShellFolder_Release(psfParent);
3911 return psf;
3912 }
3913 }
3914 /* return the desktop */
3915 return psfParent;
3916 }
3917 return NULL;
3918 }
3919
3920 /***********************************************************************
3921 * GetParentPidl
3922 *
3923 * Return the LPITEMIDLIST to the parent of the pidl in the list
3924 */
3925 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl)
3926 {
3927 LPITEMIDLIST pidlParent;
3928
3929 TRACE("%p\n", pidl);
3930
3931 pidlParent = COMDLG32_PIDL_ILClone(pidl);
3932 COMDLG32_PIDL_ILRemoveLastID(pidlParent);
3933
3934 return pidlParent;
3935 }
3936
3937 /***********************************************************************
3938 * GetPidlFromName
3939 *
3940 * returns the pidl of the file name relative to folder
3941 * NULL if an error occurred
3942 */
3943 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
3944 {
3945 LPITEMIDLIST pidl = NULL;
3946 ULONG ulEaten;
3947
3948 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
3949
3950 if(!lpcstrFileName) return NULL;
3951 if(!*lpcstrFileName) return NULL;
3952
3953 if(!lpsf)
3954 {
3955 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
3956 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3957 IShellFolder_Release(lpsf);
3958 }
3959 }
3960 else
3961 {
3962 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
3963 }
3964 return pidl;
3965 }
3966
3967 /*
3968 */
3969 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
3970 {
3971 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
3972 HRESULT ret;
3973
3974 TRACE("%p, %p\n", psf, pidl);
3975
3976 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
3977
3978 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
3979 /* see documentation shell 4.1*/
3980 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
3981 }
3982
3983 /***********************************************************************
3984 * BrowseSelectedFolder
3985 */
3986 static BOOL BrowseSelectedFolder(HWND hwnd)
3987 {
3988 BOOL bBrowseSelFolder = FALSE;
3989 FileOpenDlgInfos *fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr);
3990
3991 TRACE("\n");
3992
3993 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
3994 {
3995 LPITEMIDLIST pidlSelection;
3996
3997 /* get the file selected */
3998 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
3999 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
4000 {
4001 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
4002 pidlSelection, SBSP_RELATIVE ) ) )
4003 {
4004 static const WCHAR notexist[] = {'P','a','t','h',' ','d','o','e','s',
4005 ' ','n','o','t',' ','e','x','i','s','t',0};
4006 MessageBoxW( hwnd, notexist, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
4007 }
4008 bBrowseSelFolder = TRUE;
4009 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
4010 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE);
4011 }
4012 COMDLG32_SHFree( pidlSelection );
4013 }
4014
4015 return bBrowseSelFolder;
4016 }
4017
4018 /*
4019 * Memory allocation methods */
4020 static void *MemAlloc(UINT size)
4021 {
4022 return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
4023 }
4024
4025 static void MemFree(void *mem)
4026 {
4027 HeapFree(GetProcessHeap(),0,mem);
4028 }
4029
4030 static inline BOOL valid_struct_size( DWORD size )
4031 {
4032 return (size == OPENFILENAME_SIZE_VERSION_400W) ||
4033 (size == sizeof( OPENFILENAMEW ));
4034 }
4035
4036 static inline BOOL is_win16_looks(DWORD flags)
4037 {
4038 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) &&
4039 !(flags & OFN_EXPLORER));
4040 }
4041
4042 /* ------------------ APIs ---------------------- */
4043
4044 /***********************************************************************
4045 * GetOpenFileNameA (COMDLG32.@)
4046 *
4047 * Creates a dialog box for the user to select a file to open.
4048 *
4049 * RETURNS
4050 * TRUE on success: user enters a valid file
4051 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4052 *
4053 */
4054 BOOL WINAPI GetOpenFileNameA(
4055 LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
4056 {
4057 TRACE("flags %08x\n", ofn->Flags);
4058
4059 if (!valid_struct_size( ofn->lStructSize ))
4060 {
4061 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4062 return FALSE;
4063 }
4064
4065 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4066 if (ofn->Flags & OFN_FILEMUSTEXIST)
4067 ofn->Flags |= OFN_PATHMUSTEXIST;
4068
4069 if (is_win16_looks(ofn->Flags))
4070 return GetFileName31A(ofn, OPEN_DIALOG);
4071 else
4072 return GetFileDialog95A(ofn, OPEN_DIALOG);
4073 }
4074
4075 /***********************************************************************
4076 * GetOpenFileNameW (COMDLG32.@)
4077 *
4078 * Creates a dialog box for the user to select a file to open.
4079 *
4080 * RETURNS
4081 * TRUE on success: user enters a valid file
4082 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4083 *
4084 */
4085 BOOL WINAPI GetOpenFileNameW(
4086 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4087 {
4088 TRACE("flags %08x\n", ofn->Flags);
4089
4090 if (!valid_struct_size( ofn->lStructSize ))
4091 {
4092 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4093 return FALSE;
4094 }
4095
4096 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */
4097 if (ofn->Flags & OFN_FILEMUSTEXIST)
4098 ofn->Flags |= OFN_PATHMUSTEXIST;
4099
4100 if (is_win16_looks(ofn->Flags))
4101 return GetFileName31W(ofn, OPEN_DIALOG);
4102 else
4103 return GetFileDialog95W(ofn, OPEN_DIALOG);
4104 }
4105
4106
4107 /***********************************************************************
4108 * GetSaveFileNameA (COMDLG32.@)
4109 *
4110 * Creates a dialog box for the user to select a file to save.
4111 *
4112 * RETURNS
4113 * TRUE on success: user enters a valid file
4114 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4115 *
4116 */
4117 BOOL WINAPI GetSaveFileNameA(
4118 LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
4119 {
4120 if (!valid_struct_size( ofn->lStructSize ))
4121 {
4122 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4123 return FALSE;
4124 }
4125
4126 if (is_win16_looks(ofn->Flags))
4127 return GetFileName31A(ofn, SAVE_DIALOG);
4128 else
4129 return GetFileDialog95A(ofn, SAVE_DIALOG);
4130 }
4131
4132 /***********************************************************************
4133 * GetSaveFileNameW (COMDLG32.@)
4134 *
4135 * Creates a dialog box for the user to select a file to save.
4136 *
4137 * RETURNS
4138 * TRUE on success: user enters a valid file
4139 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4140 *
4141 */
4142 BOOL WINAPI GetSaveFileNameW(
4143 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
4144 {
4145 if (!valid_struct_size( ofn->lStructSize ))
4146 {
4147 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE );
4148 return FALSE;
4149 }
4150
4151 if (is_win16_looks(ofn->Flags))
4152 return GetFileName31W(ofn, SAVE_DIALOG);
4153 else
4154 return GetFileDialog95W(ofn, SAVE_DIALOG);
4155 }
4156
4157 /***********************************************************************
4158 * GetFileTitleA (COMDLG32.@)
4159 *
4160 * See GetFileTitleW.
4161 */
4162 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf)
4163 {
4164 int ret;
4165 UNICODE_STRING strWFile;
4166 LPWSTR lpWTitle;
4167
4168 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile);
4169 lpWTitle = RtlAllocateHeap( GetProcessHeap(), 0, cbBuf*sizeof(WCHAR));
4170 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf);
4171 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL );
4172 RtlFreeUnicodeString( &strWFile );
4173 RtlFreeHeap( GetProcessHeap(), 0, lpWTitle );
4174 return ret;
4175 }
4176
4177
4178 /***********************************************************************
4179 * GetFileTitleW (COMDLG32.@)
4180 *
4181 * Get the name of a file.
4182 *
4183 * PARAMS
4184 * lpFile [I] name and location of file
4185 * lpTitle [O] returned file name
4186 * cbBuf [I] buffer size of lpTitle
4187 *
4188 * RETURNS
4189 * Success: zero
4190 * Failure: negative number.
4191 */
4192 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf)
4193 {
4194 int i, len;
4195 static const WCHAR brkpoint[] = {'*','[',']',0};
4196 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf);
4197
4198 if(lpFile == NULL || lpTitle == NULL)
4199 return -1;
4200
4201 len = lstrlenW(lpFile);
4202
4203 if (len == 0)
4204 return -1;
4205
4206 if(strpbrkW(lpFile, brkpoint))
4207 return -1;
4208
4209 len--;
4210
4211 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':')
4212 return -1;
4213
4214 for(i = len; i >= 0; i--)
4215 {
4216 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':')
4217 {
4218 i++;
4219 break;
4220 }
4221 }
4222
4223 if(i == -1)
4224 i++;
4225
4226 TRACE("---> %s\n", debugstr_w(&lpFile[i]));
4227
4228 len = lstrlenW(lpFile+i)+1;
4229 if(cbBuf < len)
4230 return len;
4231
4232 lstrcpyW(lpTitle, &lpFile[i]);
4233 return 0;
4234 }