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