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