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