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