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