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